Skip to content
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

Closed
wants to merge 6 commits into from

Conversation

r3v4s
Copy link
Contributor

@r3v4s r3v4s commented May 9, 2024

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...
  • Added new tests, or not needed, or not feasible
  • Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory
  • Updated the official documentation or not needed
  • No breaking changes were made, or a BREAKING CHANGE: xxx message was included in the description
  • Added references to related issues and PRs
  • Provided any useful hints for running manual tests
  • Added new benchmarks to generated graphs, if any. More info here.

@github-actions github-actions bot added 📦 🤖 gnovm Issues or PRs gnovm related 📦 🌐 tendermint v2 Issues or PRs tm2 related labels May 9, 2024
@r3v4s r3v4s changed the title [tm2/gnovm] feat: add msg_idx in event to identify feat(tm2/gnovm): add msg_idx in event to identify May 9, 2024
Copy link

codecov bot commented May 9, 2024

Codecov Report

Attention: Patch coverage is 54.09836% with 28 lines in your changes are missing coverage. Please review.

Project coverage is 46.25%. Comparing base (8057505) to head (dabf13c).
Report is 9 commits behind head on master.

Files Patch % Lines
gno.land/pkg/gnoclient/client_txs.go 57.44% 10 Missing and 10 partials ⚠️
tm2/pkg/bft/abci/types/types.go 0.00% 4 Missing ⚠️
tm2/pkg/sdk/baseapp.go 60.00% 3 Missing and 1 partial ⚠️
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     
Flag Coverage Δ
gno.land 61.65% <57.44%> (?)
tm2 53.94% <42.85%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@r3v4s r3v4s requested a review from a team as a code owner May 9, 2024 06:19
@github-actions github-actions bot added the 📦 ⛰️ gno.land Issues or PRs gno.land package related label May 9, 2024
@Kouteki Kouteki added this to the 🏗4️⃣ test4.gno.land milestone May 10, 2024
@moul
Copy link
Member

moul commented May 12, 2024

Why do you need an index when the events are delivered in an ordered slice?

Your PR only shows msg_idx=0, which seems incorrect.

@r3v4s
Copy link
Contributor Author

r3v4s commented May 13, 2024

Why do you need an index when the events are delivered in an ordered slice?

Your PR only shows msg_idx=0, which seems incorrect.

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

@r3v4s r3v4s requested review from leohhhn, gfanton and a team as code owners May 13, 2024 03:32
@github-actions github-actions bot added the 🧾 package/realm Tag used for new Realms or Packages. label May 13, 2024
@@ -113,6 +114,64 @@ func TestCallMultiple_Integration(t *testing.T) {
assert.Equal(t, expected, got)
}

func TestCallMultipleWithEvent_Integration(t *testing.T) {
Copy link
Contributor

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

Copy link
Contributor Author

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

Comment on lines +59 to +62
func (e gnoEvent) SetMsgIdx(msgIdx int) interface{} {
e.MsgIdx = msgIdx
return e
}
Copy link
Contributor

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?

Copy link
Contributor Author

@r3v4s r3v4s May 13, 2024

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{}
Copy link
Contributor

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:

Suggested change
SetMsgIdx(int) interface{}
ID() []byte

If the ID is expected to be sequential:

Suggested change
SetMsgIdx(int) interface{}
Seq() uint64

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looking Good!

Copy link
Contributor Author

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

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.

gno/tm2/pkg/sdk/baseapp.go

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)
}

@r3v4s r3v4s marked this pull request as draft May 13, 2024 13:21
@r3v4s r3v4s force-pushed the feat/add-msg-idx-in-event branch from df0f268 to dabf13c Compare May 14, 2024 05:34
@r3v4s r3v4s marked this pull request as ready for review May 14, 2024 05:49
Copy link
Member

@zivkovicmilos zivkovicmilos left a 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) {
Copy link
Member

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 🙏

Copy link
Contributor Author

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
Copy link
Member

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{} {
Copy link
Member

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?

Copy link
Contributor Author

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

ctx.EventLogger.EmitEvent(evt)

Which requires type should be abci.Event

// 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

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 {
Copy link
Member

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{}
Copy link
Member

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

Copy link
Contributor Author

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)

Copy link
Contributor Author

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

Copy link
Member

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?

@zivkovicmilos
Copy link
Member

After our PR discussions, and discussions with @r3v4s, we've decided to close this PR. The bulk of the reasoning is outlined here:
#2061 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
📦 🌐 tendermint v2 Issues or PRs tm2 related 📦 ⛰️ gno.land Issues or PRs gno.land package related 📦 🤖 gnovm Issues or PRs gnovm related 🧾 package/realm Tag used for new Realms or Packages.
Projects
Status: Done
Status: Done
Development

Successfully merging this pull request may close these issues.

[tm2/gnovm] Identify each msg's event
5 participants