Skip to content

Move read capability to capability repo#21

Merged
ettec merged 14 commits into
mainfrom
move-read-capability-to-capability-repo
Oct 4, 2024
Merged

Move read capability to capability repo#21
ettec merged 14 commits into
mainfrom
move-read-capability-to-capability-repo

Conversation

@ettec

@ettec ettec commented Sep 26, 2024

Copy link
Copy Markdown
Contributor

No description provided.

@ettec ettec force-pushed the move-read-capability-to-capability-repo branch 2 times, most recently from 6f93ac0 to 1e47751 Compare September 26, 2024 10:15
@ettec ettec marked this pull request as ready for review September 26, 2024 10:21
@ettec ettec force-pushed the move-read-capability-to-capability-repo branch 2 times, most recently from fb07ea9 to 4740ab7 Compare September 26, 2024 10:26
lggr logger.Logger

capabilities.CapabilityInfo
capabilities.Validator[RequestConfig, Input, capabilities.CapabilityResponse]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you use the json schema, you don't need the validator. You can simply marshal the object to json and back. This does the same thing, but needs to build up the schema, whereas using the generated classes doesn't require us to build the schema back up.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems simpler to just use the validator.Validate method rather than have to go values->type->json explicitly in the code? though not as efficient.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validator does the same thing internally, but less efficiently. I'm ok with it for now, I'll add a Validate() method on the structs later and we can switch to that because it won't need to sterilize and deserialize.

Comment on lines +138 to +195
func (r *ReadContractAction) RegisterToWorkflow(ctx context.Context, request capabilities.RegisterToWorkflowRequest) error {
// Do Nothing
return nil
}

func (r *ReadContractAction) UnregisterFromWorkflow(ctx context.Context, request capabilities.UnregisterFromWorkflowRequest) error {
// Do Nothing
return nil
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't you need to start the contract reader on register and stop it on unregister? Otherwise, if it's meant to read events, it won't be listening for them.

@ettec ettec Sep 26, 2024

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its not meant to read events, @kidambisrinivas is working on a trigger capability to do that

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not talking about triggering on events, the chain reader can read a lastest value from events as well. If you don't want to support that (which would be a regression compared to the underlying chain reader), you'll need to verify that the user doesn't request it somehow because the underlying chain reader will just allow it and return nothing otherwise.

CC: @ilija42 to explain the use case.

@ilija42 ilija42 Sep 27, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Baseline is since this s a chain agnostic component its important to Start and Close the service to give room for handling catching up/starting up or doing whatever else is needed to make some data searchable. Looks like ReadContractAction uses GetLatestValue which is meant to read the latest state, on EVM this state can be contract state or latest emitted log. For this reason you need to start the service to kickstart EVM Log Poller. I think Aptos will do something similar and I imagine that Solana will also need this wiggle room.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ic, ok I guess that would have been caught during the integration testing (still a WIP), I didn't get that from the docs on the Chainreader interface

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated the capability to start/stop contract readers

Comment thread readcontract/action/read_contract_action.go Outdated
Comment thread readcontract/action/read_contract_action.go Outdated
Comment thread readcontract/README.md Outdated

@DeividasK DeividasK left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing project.json

@jmank88

jmank88 commented Sep 30, 2024

Copy link
Copy Markdown
Contributor

This is missing project.json

Will that trigger the CI checks?
image

Comment thread readcontract/.mockery.yaml Outdated
Comment thread readcontract/action/read_contract_action_test.go
Comment thread readcontract/action/read_contract_action_test.go Outdated
@nolag nolag force-pushed the move-read-capability-to-capability-repo branch from 7fd9fb3 to 1877ffa Compare October 1, 2024 14:34
@DeividasK DeividasK dismissed their stale review October 1, 2024 15:15

Addressed

@ettec ettec force-pushed the move-read-capability-to-capability-repo branch 2 times, most recently from 627218e to 4a65957 Compare October 1, 2024 17:32
Comment thread readcontract/README.md
@@ -0,0 +1,49 @@
Capability to read a contract on a given chain. The chain is configured by providing a json string in the config parameter of the standard capability, for example:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This readme won't reflect a workflow, they don't call execute themselves. They will use the generated code. For now, it's a lot better than nothing.

lggr logger.Logger

capabilities.CapabilityInfo
capabilities.Validator[readcontractcap.Config, readcontractcap.Input, capabilities.CapabilityResponse]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could just serialize and deserialize the config, and input. That would validate them for you. Not blocking, but it's slightly more efficient. I'm likely going to add a Validate() method in future that would make it so you don't need to serialize and deserialize.

Comment on lines +187 to +195
func (r *ReadContractAction) RegisterToWorkflow(ctx context.Context, request capabilities.RegisterToWorkflowRequest) error {
// Do Nothing - there are no resources managed by this capability that match the lifecycle of a workflow
return nil
}

func (r *ReadContractAction) UnregisterFromWorkflow(ctx context.Context, request capabilities.UnregisterFromWorkflowRequest) error {
// Do Nothing - there are no resources managed by this capability that match the lifecycle of a workflow
return nil
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetLatestValue in contract reader can be set to use the latest event emitted. Register and unregister may need to deal with that. We don't need it for Swift's PoC, and it's not clear how a handoff would work if a workflow changes, but a TODO would be good.

For now, it's probably ok to just ignore this case. @ilija42 and @jmank88 any objections?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nolag rgd workflow modification, why would unregister/register not be called in this case? If they are, given this would be relatively infrequent (I assume), why is handover a concern?

// ServiceCache is a cache that evicts and closes services after a configurable period of inactivity once a minimum size is reached.
// It is safe for concurrent use. The ServiceCache is responsible for starting and stopping the services it caches and itself should
// be closed to ensure all services are stopped.
type ServiceCache[I comparable, S services.Service] struct {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't work once the chain reader deals with events for get latest value. It seems like a pre-mature optimization. Why not just cache it on register and remove it on unregister workflow?

@ettec ettec Oct 2, 2024

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was 50/50 on which approach to use tbh, the above had the advantage of sharing readers across workflows. However, given what you say above rgd event reading possibly needing this I'll update to use workflow caching.

Comment thread readcontract/action/read_contract_action.go
@ettec ettec force-pushed the move-read-capability-to-capability-repo branch from 6dae1b7 to e0461b6 Compare October 3, 2024 09:07
Comment thread readcontract/main.go Outdated
Comment thread readcontract/action/read_contract_action.go Outdated
contractReaders *ServiceCache[string, ContractReader]
}

type Relayer interface {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this will work: in Go interfaces aren't "mapped" transitively, i.e. something which satisfies this interface:

type Relayer interface {
NewContractReader() BigInterface
}

does not match this interface:

type Relayer interface {
NewContractReader() SmallerSubsetInterface
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wrap the relayer, otherwise it would not compile:

`type readContractRelayer struct {
relayer core.Relayer
}

func (r *readContractRelayer) NewContractReader(ctx context.Context, contractReaderConfig []byte) (actions.ContractReader, error) {
return r.relayer.NewContractReader(ctx, contractReaderConfig)
}`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cheeky :) Yeah that'll do it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this is helpful, but you could capture the return type as a generic type param:

type Relayer[CR ContractReader] interface {
	NewContractReader() CR
}

@ettec ettec added this pull request to the merge queue Oct 4, 2024
Merged via the queue into main with commit d8f38c8 Oct 4, 2024
@ettec ettec deleted the move-read-capability-to-capability-repo branch October 4, 2024 14:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants