-
Notifications
You must be signed in to change notification settings - Fork 373
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(gnovm, tm2): implement event emission with
std.Emit
(#1653)
# Description Succeed in my predecessor's legacy. I have implemented the output to show the path where the event occurred, as discussed in #1833. Also made it print the timestamp or block height together. ## Key Changes In this change, event emission functionality has been added to the Gno. The main changes include: 1. Introducing of the `emitEvent` function: - The `emitEvent` function emits an event based on the given type and attributes - Attributes are passed as an even-length of array key-value pairs - When emitting an event, the current _package path_, _timestamp_, and _block height_ information are recorded along with the event(discussed in #1833). This metadata provides additional context about where the event occured. 2. Additional of event-related types and functions in the `sdk` packages: - `NewEvent` creates a new `Event` object based on the provided information. - `ArributedEvent` struct contains informations such as event type, package path, block height, timestamp and attributes. But I'm not sure how to utilize the `eventType` yet. So, I've just put it as a placeholder which will be a disscussion for another time. - `EventArribute` represents an attribute of an event and consists of a key-value pair - `NewEventArribute` creates a new `EventAttribute` object based on the given key-value pair. ## Example ```go package ee import ( "std" ) const ( EventSender = "sender" EventReceiver = "receiver" ) func Sender(){ SubSender() SubReceiver() } func SubSender() { std.Emit( EventSender, "key1", "value1", "key2", "value2", "key3", "value3", ) } func SubReceiver() { std.Emit( EventReceiver, "bar", "baz", ) } func Receiver() { std.Emit( EventReceiver, "foo", "bar", ) } ``` ### Result ```json [ "{\"type\":\"sender\",\"pkg_path\":\"gno.land/r/demo/ee\",\"identifier\":\"SubSender\",\"timestamp\":1713846501,\"attributes\":[{\"key\":\"key1\",\"value\":\"value1\"},{\"key\":\"key2\",\"value\":\"value2\"},{\"key\":\"key3\",\"value\":\"value3\"}]}", "{\"type\":\"receiver\",\"pkg_path\":\"gno.land/r/demo/ee\",\"identifier\":\"SubReceiver\",\"timestamp\":1713846501,\"attributes\":[{\"key\":\"bar\",\"value\":\"baz\"}]}" ] ``` ## Related Issue/PR #575 emit & event built-in functions (@r3v4s) #853 feat: event & emit in gno (@anarcher) <- previous work. #975 [META] Gno Wishlist / Feature Request Dump (@zivkovicmilos) --------- Co-authored-by: n3wbie <r3v4@onbloc.xyz> Co-authored-by: Manfred Touron <94029+moul@users.noreply.github.com>
- Loading branch information
1 parent
de2fc45
commit 5429655
Showing
19 changed files
with
744 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,25 @@ | ||
# test for add package | ||
|
||
# load hello.gno package located in $WORK directory as gno.land/r/hello | ||
loadpkg gno.land/r/hello $WORK | ||
|
||
## start a new node | ||
gnoland start | ||
|
||
## add hello.gno package located in $WORK directory as gno.land/r/hello | ||
gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/hello -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 | ||
|
||
## compare AddPkg | ||
cmp stdout stdout.addpkg.success | ||
|
||
|
||
## execute SayHello | ||
gnokey maketx call -pkgpath gno.land/r/hello -func SayHello -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 | ||
|
||
gnokey maketx call -pkgpath gno.land/r/hello -func SayHello -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 | ||
|
||
## compare SayHello | ||
cmp stdout stdout.call.success | ||
stdout '\("hello world!" string\)' | ||
stdout OK! | ||
stdout 'GAS WANTED: 2000000' | ||
stdout 'GAS USED: \d+' | ||
stdout 'HEIGHT: \d+' | ||
stdout 'EVENTS: \[\]' | ||
|
||
-- hello.gno -- | ||
package hello | ||
|
||
func SayHello() string { | ||
return "hello world!" | ||
} | ||
|
||
|
||
-- stdout.addpkg.success -- | ||
|
||
OK! | ||
GAS WANTED: 2000000 | ||
GAS USED: 119829 | ||
-- stdout.call.success -- | ||
("hello world!" string) | ||
OK! | ||
GAS WANTED: 2000000 | ||
GAS USED: 52801 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# load the package from $WORK directory | ||
loadpkg gno.land/r/demo/ee $WORK | ||
|
||
# start a new node | ||
gnoland start | ||
|
||
gnokey maketx call -pkgpath gno.land/r/demo/ee -func Foo -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 | ||
stdout OK! | ||
stdout 'GAS WANTED: 2000000' | ||
stdout 'GAS USED: \d+' | ||
stdout 'HEIGHT: \d+' | ||
stdout 'EVENTS: \[{\"type\":\"foo\",\"pkg_path\":\"gno.land\/r\/demo\/ee\",\"func\":\"SubFoo\",\"attrs\":\[{\"key\":\"key1\",\"value\":\"value1\"},{\"key\":\"key2\",\"value\":\"value2\"},{\"key\":\"key3\",\"value\":\"value3\"}\]},{\"type\":\"bar\",\"pkg_path\":\"gno.land\/r\/demo\/ee\",\"func\":\"SubBar\",\"attrs\":\[{\"key\":\"bar\",\"value\":\"baz\"}\]}\]' | ||
|
||
gnokey maketx call -pkgpath gno.land/r/demo/ee -func Bar -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 | ||
stdout OK! | ||
stdout 'GAS WANTED: 2000000' | ||
stdout 'GAS USED: \d+' | ||
stdout 'HEIGHT: \d+' | ||
stdout 'EVENTS: \[{\"type\":\"bar\",\"pkg_path\":\"gno.land\/r\/demo\/ee\",\"func\":\"Bar\",\"attrs\":\[{\"key\":\"foo\",\"value\":\"bar\"}\]}\]' | ||
|
||
-- ee.gno -- | ||
package ee | ||
|
||
import ( | ||
"std" | ||
) | ||
|
||
const ( | ||
EventFoo = "foo" | ||
EventBar = "bar" | ||
) | ||
|
||
func Foo(){ | ||
SubFoo() | ||
SubBar() | ||
} | ||
|
||
func SubFoo() { | ||
std.Emit( | ||
EventFoo, | ||
"key1", "value1", | ||
"key2", "value2", | ||
"key3", "value3", | ||
) | ||
} | ||
|
||
func SubBar() { | ||
std.Emit( | ||
EventBar, | ||
"bar", "baz", | ||
) | ||
} | ||
|
||
func Bar() { | ||
std.Emit( | ||
EventBar, | ||
"foo", "bar", | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# load the package from $WORK directory | ||
loadpkg gno.land/r/demo/cbee $WORK | ||
|
||
# start a new node | ||
gnoland start | ||
|
||
gnokey maketx call -pkgpath gno.land/r/demo/cbee -func Foo -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 | ||
stdout OK! | ||
stdout 'GAS WANTED: 2000000' | ||
stdout 'GAS USED: [0-9]+' | ||
stdout 'HEIGHT: [0-9]+' | ||
stdout 'EVENTS: \[{\"type\":\"foo\",\"pkg_path\":\"gno\.land\/r\/demo\/cbee\",\"func\":\"subFoo\",\"attrs\":\[{\"key\":\"k1\",\"value\":\"v1\"},{\"key\":\"k2\",\"value\":\"v2\"}\]},{\"type\":\"bar\",\"pkg_path\":\"gno\.land\/r\/demo\/cbee\",\"func\":\"subBar\",\"attrs\":\[{\"key\":\"bar\",\"value\":\"baz\"}\]}\]' | ||
|
||
|
||
-- cbee.gno -- | ||
package cbee | ||
|
||
import ( | ||
"std" | ||
) | ||
|
||
const ( | ||
foo = "foo" | ||
bar = "bar" | ||
) | ||
|
||
type contractA struct{} | ||
|
||
func (c *contractA) foo(cb func()) { | ||
subFoo() | ||
cb() | ||
} | ||
|
||
func subFoo() { | ||
std.Emit(foo, "k1", "v1", "k2", "v2") | ||
} | ||
|
||
type contractB struct{} | ||
|
||
func (c *contractB) subBar() { | ||
std.Emit(bar, "bar", "baz") | ||
} | ||
|
||
func Foo() { | ||
a := &contractA{} | ||
b := &contractB{} | ||
|
||
a.foo(func() { | ||
b.subBar() | ||
}) | ||
} |
64 changes: 64 additions & 0 deletions
64
gno.land/cmd/gnoland/testdata/event_defer_callback_loop.txtar
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# load the package from $WORK directory | ||
loadpkg gno.land/r/demo/edcl $WORK | ||
|
||
# start a new node | ||
gnoland start | ||
|
||
gnokey maketx call -pkgpath gno.land/r/demo/edcl -func Main -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 | ||
stdout OK! | ||
stdout 'GAS WANTED: 2000000' | ||
stdout 'GAS USED: [0-9]+' | ||
stdout 'HEIGHT: [0-9]+' | ||
stdout 'EVENTS: \[{\"type\":\"ForLoopEvent\",\"pkg_path\":\"gno.land\/r\/demo\/edcl\",\"func\":\"\",\"attrs\":\[{\"key\":\"iteration\",\"value\":\"0\"},{\"key\":\"key\",\"value\":\"value\"}\]},{\"type\":\"ForLoopEvent\",\"pkg_path\":\"gno.land\/r\/demo\/edcl\",\"func\":\"\",\"attrs\":\[{\"key\":\"iteration\",\"value\":\"1\"},{\"key\":\"key\",\"value\":\"value\"}\]},{\"type\":\"ForLoopEvent\",\"pkg_path\":\"gno.land\/r\/demo\/edcl\",\"func\":\"\",\"attrs\":\[{\"key\":\"iteration\",\"value\":\"2\"},{\"key\":\"key\",\"value\":\"value\"}\]},{\"type\":\"ForLoopCompletionEvent\",\"pkg_path\":\"gno.land\/r\/demo\/edcl\",\"func\":\"forLoopEmitExample\",\"attrs\":\[{\"key\":\"count\",\"value\":\"3\"}\]},{\"type\":\"CallbackEvent\",\"pkg_path\":\"gno.land\/r\/demo\/edcl\",\"func\":\"\",\"attrs\":\[{\"key\":\"key1\",\"value\":\"value1\"},{\"key\":\"key2\",\"value\":\"value2\"}\]},{\"type\":\"CallbackCompletionEvent\",\"pkg_path\":\"gno.land\/r\/demo\/edcl\",\"func\":\"callbackEmitExample\",\"attrs\":\[{\"key\":\"key\",\"value\":\"value\"}\]},{\"type\":\"DeferEvent\",\"pkg_path\":\"gno.land\/r\/demo\/edcl\",\"func\":\"deferEmitExample\",\"attrs\":\[{\"key\":\"key1\",\"value\":\"value1\"},{\"key\":\"key2\",\"value\":\"value2\"}\]}\]' | ||
|
||
-- edcl.gno -- | ||
|
||
package edcl | ||
|
||
import ( | ||
"std" | ||
"strconv" | ||
) | ||
|
||
func Main() { | ||
deferEmitExample() | ||
} | ||
|
||
func deferEmitExample() { | ||
defer func() { | ||
std.Emit("DeferEvent", "key1", "value1", "key2", "value2") | ||
println("Defer emit executed") | ||
}() | ||
|
||
forLoopEmitExample(3, func(i int) { | ||
std.Emit("ForLoopEvent", "iteration", strconv.Itoa(i), "key", "value") | ||
println("For loop emit executed: iteration ", i) | ||
}) | ||
|
||
callbackEmitExample(func() { | ||
std.Emit("CallbackEvent", "key1", "value1", "key2", "value2") | ||
println("Callback emit executed") | ||
}) | ||
|
||
println("deferEmitExample completed") | ||
} | ||
|
||
func forLoopEmitExample(count int, callback func(int)) { | ||
defer func() { | ||
std.Emit("ForLoopCompletionEvent", "count", strconv.Itoa(count)) | ||
println("For loop completion emit executed ", count) | ||
}() | ||
|
||
for i := 0; i < count; i++ { | ||
callback(i) | ||
} | ||
} | ||
|
||
func callbackEmitExample(callback func()) { | ||
defer func() { | ||
std.Emit("CallbackCompletionEvent", "key", "value") | ||
println("Callback completion emit executed") | ||
}() | ||
|
||
callback() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# load the package from $WORK directory | ||
loadpkg gno.land/r/demo/foree $WORK | ||
|
||
# start a new node | ||
gnoland start | ||
|
||
gnokey maketx call -pkgpath gno.land/r/demo/foree -func Foo -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 | ||
stdout OK! | ||
stdout 'GAS WANTED: 2000000' | ||
stdout 'GAS USED: [0-9]+' | ||
stdout 'HEIGHT: [0-9]+' | ||
stdout 'EVENTS: \[{"type":"testing","pkg_path":"gno.land\/r\/demo\/foree","func":"Foo","attrs":\[{"key":"foo","value":"bar"}\]},{"type":"testing","pkg_path":"gno.land\/r\/demo\/foree","func":"Foo","attrs":\[{"key":"foo","value":"bar"}\]},{"type":"testing","pkg_path":"gno.land\/r\/demo\/foree","func":"Foo","attrs":\[{"key":"foo","value":"bar"}\]},{"type":"testing","pkg_path":"gno.land\/r\/demo\/foree","func":"Foo","attrs":\[{"key":"foo","value":"bar"}\]},{"type":"testing","pkg_path":"gno.land\/r\/demo\/foree","func":"Foo","attrs":\[{"key":"foo","value":"bar"}\]},{"type":"testing","pkg_path":"gno.land\/r\/demo\/foree","func":"Foo","attrs":\[{"key":"foo","value":"bar"}\]},{"type":"testing","pkg_path":"gno.land\/r\/demo\/foree","func":"Foo","attrs":\[{"key":"foo","value":"bar"}\]},{"type":"testing","pkg_path":"gno.land\/r\/demo\/foree","func":"Foo","attrs":\[{"key":"foo","value":"bar"}\]},{"type":"testing","pkg_path":"gno.land\/r\/demo\/foree","func":"Foo","attrs":\[{"key":"foo","value":"bar"}\]},{"type":"testing","pkg_path":"gno.land\/r\/demo\/foree","func":"Foo","attrs":\[{"key":"foo","value":"bar"}\]}\]' | ||
|
||
gnokey maketx call -pkgpath gno.land/r/demo/foree -func Bar -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 | ||
stdout OK! | ||
stdout 'GAS WANTED: 2000000' | ||
stdout 'GAS USED: [0-9]+' | ||
stdout 'HEIGHT: [0-9]+' | ||
stdout 'EVENTS: \[{"type":"Foo","pkg_path":"gno.land\/r\/demo\/foree","func":"subFoo","attrs":\[{"key":"k1","value":"v1"},{"key":"k2","value":"v2"}\]},{"type":"Bar","pkg_path":"gno.land\/r\/demo\/foree","func":"subBar","attrs":\[{"key":"bar","value":"baz"}\]},{"type":"Foo","pkg_path":"gno.land\/r\/demo\/foree","func":"subFoo","attrs":\[{"key":"k1","value":"v1"},{"key":"k2","value":"v2"}\]},{"type":"Bar","pkg_path":"gno.land\/r\/demo\/foree","func":"subBar","attrs":\[{"key":"bar","value":"baz"}\]}\]' | ||
|
||
|
||
-- foree.gno -- | ||
|
||
package foree | ||
|
||
import "std" | ||
|
||
func Foo() { | ||
for i := 0; i < 10; i++ { | ||
std.Emit("testing", "foo", "bar") | ||
} | ||
} | ||
|
||
const ( | ||
eventFoo = "Foo" | ||
eventBar = "Bar" | ||
) | ||
|
||
type contractA struct{} | ||
|
||
func (c *contractA) Foo(cb func()) { | ||
subFoo() | ||
cb() | ||
} | ||
|
||
func subFoo() { | ||
std.Emit(eventFoo, "k1", "v1", "k2", "v2") | ||
} | ||
|
||
type contractB struct{} | ||
|
||
func (c *contractB) subBar() { | ||
std.Emit(eventBar, "bar", "baz") | ||
} | ||
|
||
func Bar() { | ||
a := &contractA{} | ||
b := &contractB{} | ||
for i := 0; i < 2; i++ { | ||
a.Foo(func() { | ||
b.subBar() | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package std | ||
|
||
// Emit is a function that constructs a gnoEvent with a specified type and attributes. | ||
// It then forwards this event to the event logger. Each emitted event carries metadata | ||
// such as the event type, the initializing realm, and the provided attributes. | ||
// | ||
// The function takes type and attribute strings. typ (or type) is an arbitrary string that represents | ||
// the type of the event. It plays a role of indexing what kind of event occurred. For example, | ||
// a name like "Foo" or "Bar" can be used to indicating the purpose (nature) of the event. | ||
// | ||
// And the attrs (attributes) accepts an even number of strings and sets them as key-value pairs | ||
// according to the order they are passed. For example, if the attr strings "key1", "value1" are | ||
// passed in, the key is set to "key1" and the value is set to "value1". | ||
// | ||
// The event is dispatched to the EventLogger, which resides in the tm2/pkg/sdk/events.go file. | ||
// | ||
// For more details about the GnoEvent data structure, refer to its definition in the emit_event.go file. | ||
func Emit(typ string, attrs ...string) { emit(typ, attrs) } | ||
func emit(typ string, attrs []string) |
Oops, something went wrong.