-
Notifications
You must be signed in to change notification settings - Fork 382
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
feat(tm2/gnovm): add msg_idx in event to identify #2061
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #2061 +/- ##
==========================================
- Coverage 54.96% 46.25% -8.72%
==========================================
Files 481 505 +24
Lines 67391 71040 +3649
==========================================
- Hits 37040 32856 -4184
- Misses 27330 35457 +8127
+ Partials 3021 2727 -294
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
Why do you need an index when the events are delivered in an ordered slice? Your PR only shows |
Index is about MESSAGE index, not EVENT. So as described in #2031, current event output is just list of events without knowing which msg emitted which event if tx inlcudes 2 or more msgs. I'll add test cases that runs 2 messages // UPDATE 3002936 |
@@ -113,6 +114,64 @@ func TestCallMultiple_Integration(t *testing.T) { | |||
assert.Equal(t, expected, got) | |||
} | |||
|
|||
func TestCallMultipleWithEvent_Integration(t *testing.T) { |
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.
Could you test generating several events between different blocks and different transactions? thanks
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'll add test that tests multi tx + mutli msg
.
But I don't think testing events between different block is necessary test, different block really has nothing to do with multi-msg
func (e gnoEvent) SetMsgIdx(msgIdx int) interface{} { | ||
e.MsgIdx = msgIdx | ||
return e | ||
} |
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 this instead of e.MsgIdx = msgIdx
when needed?
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.
If what you're suggesting is e.MsgIdx = msgIdx
without return e
, vm was panicking if function is like func (e *gnoEvnet)
.
UPDATE //
abci.Event is interface type described here
Based on my knowledge pointer receiver can't be used with interface
@@ -206,6 +206,7 @@ type Error interface { | |||
|
|||
type Event interface { | |||
AssertABCIEvent() | |||
SetMsgIdx(int) interface{} |
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.
If you want to force that an Event needs an ID, I would recommend adding the method providing that ID instead of the provided Setter:
SetMsgIdx(int) interface{} | |
ID() []byte |
If the ID is expected to be sequential:
SetMsgIdx(int) interface{} | |
Seq() uint64 |
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.
Looking Good!
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.
Reverted.
Event needs not only ID, but also setter for setting msg_idx.
Currently, event gets emitted from here
gno/gnovm/stdlibs/std/emit_event.go
Lines 14 to 31 in d7c04be
func X_emit(m *gno.Machine, typ string, attrs []string) { | |
eventAttrs, err := attrKeysAndValues(attrs) | |
if err != nil { | |
m.Panic(typedString(err.Error())) | |
} | |
pkgPath := CurrentRealmPath(m) | |
fnIdent := getPrevFunctionNameFromTarget(m, "Emit") | |
evt := gnoEvent{ | |
Type: typ, | |
PkgPath: pkgPath, | |
Func: fnIdent, | |
Attributes: eventAttrs, | |
} | |
ctx := m.Context.(ExecContext) | |
ctx.EventLogger.EmitEvent(evt) | |
} |
At this point, there is no way for us to determine message index if current request was made by multi-msg tx.
However baseapp.go
does know full list of messages so, we can set message index from there.
Lines 634 to 666 in dabf13c
for i, msg := range msgs { | |
// match message route | |
msgRoute := msg.Route() | |
handler := app.router.Route(msgRoute) | |
if handler == nil { | |
result.Error = ABCIError(std.ErrUnknownRequest("unrecognized message type: " + msgRoute)) | |
return | |
} | |
var msgResult Result | |
ctx = ctx.WithEventLogger(NewEventLogger()) | |
// run the message! | |
// skip actual execution for CheckTx mode | |
if mode != RunTxModeCheck { | |
msgResult = handler.Process(ctx, msg) | |
} | |
// Each message result's Data must be length prefixed in order to separate | |
// each result. | |
data = append(data, msgResult.Data...) | |
// each msgs' event | |
var msgEvent []abci.Event | |
// handle each msg's ctx event | |
msgCtxEvents := ctx.EventLogger().Events() | |
for _, msgCtxEvent := range msgCtxEvents { | |
overwriteInterface := msgCtxEvent.SetMsgIdx(i) | |
overwrwriteEvent := overwriteInterface.(abci.Event) | |
msgEvent = append(msgEvent, overwrwriteEvent) | |
} |
df0f268
to
dabf13c
Compare
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.
Please check my comments, I don't think we need this functionality in the events 🙁
@@ -114,6 +114,62 @@ | |||
return c.signAndBroadcastTxCommit(tx, cfg.AccountNumber, cfg.SequenceNumber) | |||
} | |||
|
|||
// CallTxSync executes one or more MsgCall calls on the blockchain synchronously | |||
func (c *Client) CallTxSync(cfg BaseTxCfg, msgs ...MsgCall) (*ctypes.ResultBroadcastTx, error) { |
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.
We should leave this for a separate PR 🙏
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.
will do
// TODO append msgevent from ctx. XXX XXX | ||
|
||
// each msgs' event | ||
var msgEvent []abci.Event |
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.
You know at this point in time how many events you'll have, so this slice should be initialized with a capacity:
msgEvent := make([]abci.Event, 0, len(msgCtxEvents))
Attributes []gnoEventAttribute `json:"attrs"` | ||
} | ||
|
||
func (e gnoEvent) AssertABCIEvent() {} | ||
|
||
func (e gnoEvent) SetMsgIdx(msgIdx int) interface{} { |
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 do you need to return the message event from this method?
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.
That type gno Event
will be emitted from here
gno/gnovm/stdlibs/std/emit_event.go
Line 31 in dabf13c
ctx.EventLogger.EmitEvent(evt) |
Which requires type should be abci.Event
Lines 23 to 37 in ccc6d5b
// EmitEvent stores a single Event object. | |
func (em *EventLogger) EmitEvent(event Event) { | |
em.events = append(em.events, event) | |
} | |
// EmitEvents stores a series of Event objects. | |
func (em *EventLogger) EmitEvents(events []Event) { | |
em.events = append(em.events, events...) | |
} | |
// ---------------------------------------------------------------------------- | |
// Event | |
// ---------------------------------------------------------------------------- | |
type Event = abci.Event |
And also abci.Evnet
is interface
gno/tm2/pkg/bft/abci/types/types.go
Lines 207 to 210 in dabf13c
type Event interface { | |
AssertABCIEvent() | |
SetMsgIdx(int) interface{} | |
} |
Based my knowledge, to modify struct value using setter without return value it need pointer receiver. But we can't use pointer receiver for interface type.
|
||
// handle each msg's ctx event | ||
msgCtxEvents := ctx.EventLogger().Events() | ||
for _, msgCtxEvent := range msgCtxEvents { |
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'm not sure what kind of performance overhead this has, but it can't be minimal
@@ -206,6 +206,7 @@ type Error interface { | |||
|
|||
type Event interface { | |||
AssertABCIEvent() | |||
SetMsgIdx(int) interface{} |
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'm gonna suggest something radical, but hear me out
We don't actually need this information in the event.
Why would it matter which event belongs to which message?
How can you guarantee that the node doesn't shuffle the messages before executing them? (it doesn't now, but I see no reason why it might not)
I'm not in favor of this idea because it:
- introduces more complexity on the already bloated event API
- it's just a QoL feature that doesn't have any real functional value, since events are async and we should be concerned with their content, not their origin
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.
We don't actually need this information in the event.
Why would it matter which event belongs to which message?
-
Let't say... single block has N txs, and each txs has N2 messages and each message has N3 emitted events.
-
If all of N2 messages are same function with just different input params, this is situation where I use
msg_idx
to identify it. -
One of gnoswap(+interface) functionality is removing all of your positions in one shot, by calling
RemovePosition(positionId)
several time(≈several msgs). -
If that
RemovePosition
emits few event with some underlying infos. At this point, we need to know which events was came from which message (i.e which position emitted which events)
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.
How can you guarantee that the node doesn't shuffle the messages before executing them? (it doesn't now, but I see no reason why it might not)
- I really didn't think about this :(
introduces more complexity on the already bloated event API
- Agreed
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.
If all of N2 messages are same function with just different input params, this is situation where I use msg_idx to identify it.
Yes, but why? Isn't it enough that you can parse RemovePosition
events on the client, and use data from the event content to figure out what to do?
Maybe I'm missing something, but tying events to their respective messages doesn't solve any issue -- you should be able to adequately parse them client-side (reactive, async) and execute logic based on their appearance. This is the foundation of event driven design
@ajnavarro @gfanton maybe I am missing something?
After our PR discussions, and discussions with @r3v4s, we've decided to close this PR. The bulk of the reasoning is outlined here: |
Closes #2031
As describe in above issue, this pr adds
msg_idx
field in ctx.Event struct to identify which event was emitted by which msg.FYI, this pr also includes another bug fix in #2030. It can be close if we're willing to merge this one
Contributors' checklist...
BREAKING CHANGE: xxx
message was included in the description