Move read capability to capability repo#21
Conversation
6f93ac0 to
1e47751
Compare
fb07ea9 to
4740ab7
Compare
| lggr logger.Logger | ||
|
|
||
| capabilities.CapabilityInfo | ||
| capabilities.Validator[RequestConfig, Input, capabilities.CapabilityResponse] |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
| 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 | ||
| } |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
its not meant to read events, @kidambisrinivas is working on a trigger capability to do that
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
updated the capability to start/stop contract readers
DeividasK
left a comment
There was a problem hiding this comment.
This is missing project.json
7fd9fb3 to
1877ffa
Compare
627218e to
4a65957
Compare
| @@ -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: | |||
There was a problem hiding this comment.
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] |
There was a problem hiding this comment.
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.
| 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 | ||
| } |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
@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 { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
6dae1b7 to
e0461b6
Compare
| contractReaders *ServiceCache[string, ContractReader] | ||
| } | ||
|
|
||
| type Relayer interface { |
There was a problem hiding this comment.
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
}
There was a problem hiding this comment.
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)
}`
There was a problem hiding this comment.
Cheeky :) Yeah that'll do it
There was a problem hiding this comment.
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
}
No description provided.