-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ADR 031: Typed Events #7406
ADR 031: Typed Events #7406
Conversation
|
||
// SDKEvent contains the string representation of the event and the module information | ||
type SDKEvent struct { | ||
Sev StringEvent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why Sev
? Also, you don't define what StringEvent
is. Does Sev
not encapsulate Base
(seems this way based off of the godoc)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this is a typo, I'm trying to reference the StringEvents
type already defined in the events.go
. StringEvents
does contain the Module
and Action
but it is inconvenient to pull out. We could also add some functions here to StringEvents
(func (sev StringEvents) Module() string
and func (sev StringEvents) Action() string
) and dispense with the BaseModuleEvent
and SDKEvent
types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am strongly in favor of typed events.
If we're going to go ahead and refactor events, I think we could simplify this a bit by leveraging what we get from protobuf, especially the built in message name. For the case described here we'd have the following proto definition:
package cosmos.gov;
message EventSubmitProposal {
string from_address = 1;
uint64 proposal_id = 2;
google.protobuf.Any proposal = 3;
}
This gives us a message name cosmos.gov.EventSubmitProposal
. We could say that what we're now calling the module name is the package cosmos.gov
and that the action is message name EventSubmitProposal
.
Using this protobuf approach, clients would know the module and action just from the .proto files, instead of the current approach where that information lives out of band in golang source code. This could also simplify things for module developers by eliminating the need to implement this BaseModuleEvent
stuff.
Obviously, there are some legacy considerations we'd need to address around the current module and action names. But going forward I'd like to see the .proto files as the source of truth for as much as possible. That eventually could look like events being communicated as Any
s rather than key/value pairs. That might be a larger discussion but if we're refactoring events anyway and we just did this big proto migrations, we should align those two.
The problem with that @aaronc is that the tendermint API returns them in Also In addition, this isn't a refactor. Its adding functionality using the existing code. I does not refactor anything. |
We could serialize events using protobuf pretty easily using the current ABCI events by setting
That would be preferable I think. It would even be nice to see a method on
Gotcha. But without stronger typed events like I'm proposing with .proto defintions, this ADR only accomplishes its goals for other golang developers. I see how that works for Akash's use case. But just noting that this benefit doesn't translate to clients in other languages. Also I would note that the protobuf approach I'm proposing would obviate the need to implement all those |
Here's the gist of what I'm thinking. We add a function func (em *EventManager) EmitTypedEvent(event proto.Message) error {
evtType := proto.MessageName(event)
evtJson, err := codec.ProtoMarshalJSON(event)
if err != nil {
return err
}
var attrMap map[string]json.RawMessage
err = json.Unmarshal(evtJson, &attrMap)
if err != nil {
return err
}
var attrs []abci.EventAttribute
for k, v := range attrMap {
attrs = append(attrs, abci.EventAttribute{
Key: []byte(k),
Value: v,
})
}
em.EmitEvent(Event{
Type: evtType,
Attributes: attrs,
})
return nil
} Then we have a corresponding function func ParseTypedEvent(event abci.Event) (proto.Message, error) {
// look up proto.Message type by event.Type
// populate attributes into map[string]json.RawMessage and marshal that to a json string
// unmarshal the json string to the proto.Message
} |
relevant context: issue on tendermint event subscription |
Thanks @aaronc, your approach looks great.
Is there a way to support nested objects with this? Even if it doesn't we can have a |
I'm not sure I quite follow. What do you mean by nested objects? |
@aaronc I am not sure if there's a requirement for this but let's say we want to message TxResponse {
int64 height = 1;
string txhash = 2 [(gogoproto.customname) = "TxHash"];
string codespace = 3;
uint32 code = 4;
...
google.protobuf.Any tx = 11;
...
} Or even any simple proto message with |
@anilcse can you think of a different example besides But if it were, with the approach I'm destining nested |
Thanks @aaronc. Even if there's a case as such, we just get to add a custom |
Link to rendered ADR
Context
Currently in the SDK, events are defined in the handlers for each message, meaning each module doesn't have a cannonical set of types for each event. Above all else this makes these events difficult to consume as it requires a great deal of raw string matching and parsing. This proposal focuses on updating the events to use typed events defined in each module such that emiting and subscribing to events will be much easier. This workflow comes from the experience of the Akash Network team.
Our platform requires a number of programatic on chain interactions both on the provider (datacenter - to bid on new orders and listen for leases created) and user (application developer - to send the app manifest to the provider) side. In addition the Akash team is now maintaining the IBC
relayer
, another very event driven process. In working on these core pieces of infrastructure, and integrating lessons learned from Kubernetes developement, our team has developed a standard method for defining and consuming typed events in SDK modules. We have found that it is extremely useful in building this type of event driven application.As the SDK gets used more extensively for apps like
peggy
, other peg zones, IBC, DeFi, etc... there will be an exploding demand for event driven applications to support new features desired by users. We propose upstreaming our findings into the SDK to enable all SDK applications to quickly and easily build event driven apps to aid their core application. Wallets, exchanges, explorers, and defi protocols all stand to benefit from this work.If this proposal is accepted, users will be able to build event driven SDK apps in go by just writing
EventHandler
s for their specific event types and passing them toEventEmitters
that are defined in the SDK.The end of this proposal contains a detailed example of how to consume events after this refactor.