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

Track and identify emitted events #2437

Open
jinoosss opened this issue Jun 25, 2024 · 5 comments
Open

Track and identify emitted events #2437

jinoosss opened this issue Jun 25, 2024 · 5 comments

Comments

@jinoosss
Copy link
Member

jinoosss commented Jun 25, 2024

Description

We're planning to introduce a new feature in GnoScan which traces and displays events that occured within a transaction.
Below is the preview of the UI.

image

Unfortunately, we've come across an issue in the progress.
There isn't a clear way to guarantee (1) the "depth" of each event within the function call stack, and (2) the function from which the event was emitted.

Here’s a simple contract example.

Two functions(EmitEventMain, EmitEventMainWithSub) in a Realm(emit_main) emit the same event "EVENT_MAIN".

  • In the EmitEventMainWithSub function, another contract(emit_sub)'s function is called to emit the "EVENT_SUB" event.

  • In this case, we cannot identify which of the two functions in the emit_main realm has triggered the emission of "EVENT_SUB" simply by parsing the events.

    [Realm: gno.land/r/demo/emit_sub]

    package emit_sub
    
    import "std"
    
    // Emit the 'EVENT_SUB' event.
    func EmitEventSub() string {
        std.EmitEventSub("EVENT_SUB")
    
        return "EmitEventSub"
    }

    [Realm: gno.land/r/demo/emit_main]

    package emit_main
    
    import (
    	emit_sub "gno.land/r/demo/emit_sub"
    	"std"
    )
    
    // Emit the 'EVENT_MAIN' event.
    func EmitEventMain() string {
    	std.Emit("EVENT_MAIN")
    	emit_sub.Emit()
    
    	return "EmitEventMain"
    }
    
    // Call a function that emits the 'EVENT_MAIN' event and emits the 'EVENT_SUB' event.
    func EmitEventMainWithSub() string {
    	std.Emit("EVENT_MAIN")
    	emit_sub.EmitEventSub() // When calling 'EmitEventSub' in 'gno.land/r/demo/emit_sub', emit the event 'EVENT_SUB'
    
    	return "EmitEventMainWithSub"
    }

    Block Results's ResponseBase when the EmitEventMain function and EmitEventMainWithSub function are executed as multi messages

    {
      "Error": null,
      "Data": "KCJFTUlUX01BSU4iIHN0cmluZykKCigiRU1JVF9NQUlOX1dJVEhfU1VCIiBzdHJpbmcpCgo=",
      "Events": [
        {
          "@type": "/tm.gnoEvent",
          "type": "EVENT_MAIN",
          "pkg_path": "gno.land/r/demo/emit_main",
          "func": "EmitMain",
          "attrs": null
        },
        {
          "@type": "/tm.gnoEvent",
          "type": "EVENT_MAIN",
          "pkg_path": "gno.land/r/demo/emit_main",
          "func": "EmitMainWithSub",
          "attrs": null
        },
        {
          "@type": "/tm.gnoEvent",
          "type": "EVENT_SUB",
          "pkg_path": "gno.land/r/demo/emit_sub",
          "func": "Emit",
          "attrs": null
        }
      ],
      "Log": "msg:0,success:true,log:,events:[]\nmsg:1,success:true,log:,events:[]",
      "Info": ""
    }

PR #2061, was an attempt to provide such a way by adding an index to each event, but was closed due to some concerns.

Are there any suggestions on how we should approach this issue?
If not, it seems like it's worth re-exploring the msg_idx idea or something similar.

This event support in GnoScan will be useful for users and developers to track events in-depth, especially for contracts that require connected events, such as DAOs, DEXes, etc.

@dongwon8247
Copy link
Member

@zivkovicmilos @thehowl Can you take a look, please?

@thehowl
Copy link
Member

thehowl commented Jun 27, 2024

There is an assumption in this OP, and that is that having the information on where the event was called is useful information to begin with. I tend to disagree. If in the event we have information on where an event was emitted, then a consumer of the event will use this information to discriminate between different events.

Personally, I actually think we should remove more functionality from the current event implementation; ie. the Func field, for the same reason I described. A consumer of the event should not have this information, as it can be used to conditionally handle an event, where really only the event pkgpath, type and attributes should be used to make that work. If I'm reorganizing my realm's code and decide to move a function with a different name, or to also emit the event somewhere else, the consumer application should not break because of this.

In light of this, here's what I think:

  • In the short term, to support the feature in Gnoscan, what do you think about having a field like Line in the event, which contains a line/column spec like gno.land/r/demo/emitter/package/pkg.gno:89:3? This way you can point to where the event emission happens in Gnoscan, while having the event data be more obviously debugging information rather than information which should be depended upn.
  • In the long term, what if it was a feature of the indexer / a specialised sentry node? Especially if we have stacktracing (feat(gnovm): add stacktraces and log them in panic messages #2145) available, it is possible for a GnoVM instance executing the transaction to capture the entire call stack of the event, including all parameters to all functions, and create rich information on Gnoscan. I don't think this should be a chain feature; or something that should be stored in the event data (and thus occupy storage bytes)
    • Note that if we manage to do this before mainnet, this would likely mean removing the above Line field.

Does this sound like something which could fit your use case?

@notJoon
Copy link
Member

notJoon commented Jul 1, 2024

I agree with that we shouldn't include too much information in std.Emit like @thehowl suggested. However, there's also a need for detailed information to advancing the Gnoscan. But, using the line numbers seems impractical as it wouldn't provide enough context and would require complex tasks like drawing call graphs. Also, we cannot retreive enough state/data from a given blocks.

So instead of showing metadata like previous function or realm through the Emit, what if create a new function like Ethereum's getLog function to send only logging information via RPC, allowing data exchange with tools like tx-indexer?

In this case, I think we can use Emit purely for messages, which allows to remove unneccesary metadata while still being able to utilize this information throught the log. What do you think?

cc @dongwon8247 @jinoosss @r3v4s

@thehowl
Copy link
Member

thehowl commented Jul 1, 2024

If I understand your proposal correctly @notJoon,

  • Have internal data (ie. of an event emission) stored as a log
  • Have it available as data if requested through a "getlog" rpc call
  • ie. not have it on-chain, but can be communicated to the indexer

This sounds OK to me, but also like a functionality that maybe should be disabled on a node by default.

I'm summoning @zivkovicmilos for a second opinion.

@notJoon
Copy link
Member

notJoon commented Jul 2, 2024

This sounds OK to me, but also like a functionality that maybe should be disabled on a node by default.

I'm summoning @zivkovicmilos for a second opinion.

Yes, you're right. I'll start working on it later based on the results. thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Backlog
Development

No branches or pull requests

4 participants