From 0f440aae3298bf28c88e3b8ab01550672e8dc442 Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Thu, 7 Dec 2023 18:37:38 +0100 Subject: [PATCH 1/4] feat(examples): add {p,r}/demo/tamagotchi (#1359) Co-authored-by: Morgan --- examples/gno.land/p/demo/tamagotchi/gno.mod | 3 + .../gno.land/p/demo/tamagotchi/tamagotchi.gno | 175 ++++++++++++++++++ .../p/demo/tamagotchi/z0_filetest.gno | 106 +++++++++++ examples/gno.land/r/demo/tamagotchi/gno.mod | 6 + examples/gno.land/r/demo/tamagotchi/realm.gno | 53 ++++++ .../r/demo/tamagotchi/z0_filetest.gno | 28 +++ gno.land/Makefile | 12 +- misc/loop/Makefile | 8 +- misc/loop/run_loop.sh | 2 +- 9 files changed, 386 insertions(+), 7 deletions(-) create mode 100644 examples/gno.land/p/demo/tamagotchi/gno.mod create mode 100644 examples/gno.land/p/demo/tamagotchi/tamagotchi.gno create mode 100644 examples/gno.land/p/demo/tamagotchi/z0_filetest.gno create mode 100644 examples/gno.land/r/demo/tamagotchi/gno.mod create mode 100644 examples/gno.land/r/demo/tamagotchi/realm.gno create mode 100644 examples/gno.land/r/demo/tamagotchi/z0_filetest.gno diff --git a/examples/gno.land/p/demo/tamagotchi/gno.mod b/examples/gno.land/p/demo/tamagotchi/gno.mod new file mode 100644 index 00000000000..58441284a6b --- /dev/null +++ b/examples/gno.land/p/demo/tamagotchi/gno.mod @@ -0,0 +1,3 @@ +module gno.land/p/demo/tamagotchi + +require gno.land/p/demo/ufmt v0.0.0-latest diff --git a/examples/gno.land/p/demo/tamagotchi/tamagotchi.gno b/examples/gno.land/p/demo/tamagotchi/tamagotchi.gno new file mode 100644 index 00000000000..4e0cb6cb2b2 --- /dev/null +++ b/examples/gno.land/p/demo/tamagotchi/tamagotchi.gno @@ -0,0 +1,175 @@ +package tamagotchi + +import ( + "time" + + "gno.land/p/demo/ufmt" +) + +// Tamagotchi structure +type Tamagotchi struct { + name string + hunger int + happiness int + health int + age int + maxAge int + sleepy int + created time.Time + lastUpdated time.Time +} + +func New(name string) *Tamagotchi { + now := time.Now() + return &Tamagotchi{ + name: name, + hunger: 50, + happiness: 50, + health: 50, + maxAge: 100, + lastUpdated: now, + created: now, + } +} + +func (t *Tamagotchi) Name() string { + t.update() + return t.name +} + +func (t *Tamagotchi) Hunger() int { + t.update() + return t.hunger +} + +func (t *Tamagotchi) Happiness() int { + t.update() + return t.happiness +} + +func (t *Tamagotchi) Health() int { + t.update() + return t.health +} + +func (t *Tamagotchi) Age() int { + t.update() + return t.age +} + +func (t *Tamagotchi) Sleepy() int { + t.update() + return t.sleepy +} + +// Feed method for Tamagotchi +func (t *Tamagotchi) Feed() { + t.update() + if t.dead() { + return + } + t.hunger = bound(t.hunger-10, 0, 100) +} + +// Play method for Tamagotchi +func (t *Tamagotchi) Play() { + t.update() + if t.dead() { + return + } + t.happiness = bound(t.happiness+10, 0, 100) +} + +// Heal method for Tamagotchi +func (t *Tamagotchi) Heal() { + t.update() + + if t.dead() { + return + } + t.health = bound(t.health+10, 0, 100) +} + +func (t Tamagotchi) dead() bool { return t.health == 0 } + +// Update applies changes based on the duration since the last update +func (t *Tamagotchi) update() { + if t.dead() { + return + } + + now := time.Now() + if t.lastUpdated == now { + return + } + + duration := now.Sub(t.lastUpdated) + elapsedMins := int(duration.Minutes()) + + t.hunger = bound(t.hunger+elapsedMins, 0, 100) + t.happiness = bound(t.happiness-elapsedMins, 0, 100) + t.health = bound(t.health-elapsedMins, 0, 100) + t.sleepy = bound(t.sleepy+elapsedMins, 0, 100) + + // age is hours since created + t.age = int(now.Sub(t.created).Hours()) + if t.age > t.maxAge { + t.age = t.maxAge + t.health = 0 + } + if t.health == 0 { + t.sleepy = 0 + t.happiness = 0 + t.hunger = 0 + } + + t.lastUpdated = now +} + +// Face returns an ASCII art representation of the Tamagotchi's current state +func (t *Tamagotchi) Face() string { + t.update() + return t.face() +} + +func (t *Tamagotchi) face() string { + switch { + case t.health == 0: + return "😵" // dead face + case t.health < 30: + return "😷" // sick face + case t.happiness < 30: + return "😢" // sad face + case t.hunger > 70: + return "😫" // hungry face + case t.sleepy > 70: + return "😴" // sleepy face + default: + return "😃" // happy face + } +} + +// Markdown method for Tamagotchi +func (t *Tamagotchi) Markdown() string { + t.update() + return ufmt.Sprintf(`# %s %s + +* age: %d +* hunger: %d +* happiness: %d +* health: %d +* sleepy: %d`, + t.name, t.Face(), + t.age, t.hunger, t.happiness, t.health, t.sleepy, + ) +} + +func bound(n, min, max int) int { + if n < min { + return min + } + if n > max { + return max + } + return n +} diff --git a/examples/gno.land/p/demo/tamagotchi/z0_filetest.gno b/examples/gno.land/p/demo/tamagotchi/z0_filetest.gno new file mode 100644 index 00000000000..36163065e7f --- /dev/null +++ b/examples/gno.land/p/demo/tamagotchi/z0_filetest.gno @@ -0,0 +1,106 @@ +package main + +import ( + "std" + "time" + + "internal/os_test" + + "gno.land/p/demo/tamagotchi" +) + +func main() { + t := tamagotchi.New("Gnome") + + println("\n-- INITIAL\n") + println(t.Markdown()) + + println("\n-- WAIT 20 minutes\n") + os_test.Sleep(20 * time.Minute) + println(t.Markdown()) + + println("\n-- FEEDx3, PLAYx2, HEALx4\n") + t.Feed() + t.Feed() + t.Feed() + t.Play() + t.Play() + t.Heal() + t.Heal() + t.Heal() + t.Heal() + println(t.Markdown()) + + println("\n-- WAIT 20 minutes\n") + os_test.Sleep(20 * time.Minute) + println(t.Markdown()) + + println("\n-- WAIT 20 hours\n") + os_test.Sleep(20 * time.Hour) + println(t.Markdown()) + + println("\n-- WAIT 20 hours\n") + os_test.Sleep(20 * time.Hour) + println(t.Markdown()) +} + +// Output: +// -- INITIAL +// +// # Gnome 😃 +// +// * age: 0 +// * hunger: 50 +// * happiness: 50 +// * health: 50 +// * sleepy: 0 +// +// -- WAIT 20 minutes +// +// # Gnome 😃 +// +// * age: 0 +// * hunger: 70 +// * happiness: 30 +// * health: 30 +// * sleepy: 20 +// +// -- FEEDx3, PLAYx2, HEALx4 +// +// # Gnome 😃 +// +// * age: 0 +// * hunger: 40 +// * happiness: 50 +// * health: 70 +// * sleepy: 20 +// +// -- WAIT 20 minutes +// +// # Gnome 😃 +// +// * age: 0 +// * hunger: 60 +// * happiness: 30 +// * health: 50 +// * sleepy: 40 +// +// -- WAIT 20 hours +// +// # Gnome 😵 +// +// * age: 20 +// * hunger: 0 +// * happiness: 0 +// * health: 0 +// * sleepy: 0 +// +// -- WAIT 20 hours +// +// # Gnome 😵 +// +// * age: 20 +// * hunger: 0 +// * happiness: 0 +// * health: 0 +// * sleepy: 0 diff --git a/examples/gno.land/r/demo/tamagotchi/gno.mod b/examples/gno.land/r/demo/tamagotchi/gno.mod new file mode 100644 index 00000000000..b7a2deea2c2 --- /dev/null +++ b/examples/gno.land/r/demo/tamagotchi/gno.mod @@ -0,0 +1,6 @@ +module gno.land/r/demo/tamagotchi + +require ( + gno.land/p/demo/tamagotchi v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest +) diff --git a/examples/gno.land/r/demo/tamagotchi/realm.gno b/examples/gno.land/r/demo/tamagotchi/realm.gno new file mode 100644 index 00000000000..f8f62c9fc7a --- /dev/null +++ b/examples/gno.land/r/demo/tamagotchi/realm.gno @@ -0,0 +1,53 @@ +package tamagotchi + +import ( + "std" + + "gno.land/p/demo/tamagotchi" + "gno.land/p/demo/ufmt" +) + +var t *tamagotchi.Tamagotchi + +func init() { + Reset("gnome#0") +} + +func Reset(optionalName string) string { + name := optionalName + if name == "" { + height := std.GetHeight() + name = ufmt.Sprintf("gnome#%d", height) + } + + t = tamagotchi.New(name) + + return ufmt.Sprintf("A new tamagotchi is born. Their name is %s %s.", t.Name(), t.Face()) +} + +func Feed() string { + t.Feed() + return t.Markdown() +} + +func Play() string { + t.Play() + return t.Markdown() +} + +func Heal() string { + t.Heal() + return t.Markdown() +} + +func Render(path string) string { + tama := t.Markdown() + links := `Actions: +* [Feed](/r/demo/tamagotchi?help&__func=Feed) +* [Play](/r/demo/tamagotchi?help&__func=Play) +* [Heal](/r/demo/tamagotchi?help&__func=Heal) +* [Reset](/r/demo/tamagotchi?help&__func=Reset) +` + + return tama + "\n\n" + links +} diff --git a/examples/gno.land/r/demo/tamagotchi/z0_filetest.gno b/examples/gno.land/r/demo/tamagotchi/z0_filetest.gno new file mode 100644 index 00000000000..373d737b3b5 --- /dev/null +++ b/examples/gno.land/r/demo/tamagotchi/z0_filetest.gno @@ -0,0 +1,28 @@ +package main + +import ( + "std" + "time" + + "gno.land/r/demo/tamagotchi" +) + +func main() { + tamagotchi.Reset("tamagnotchi") + println(tamagotchi.Render("")) +} + +// Output: +// # tamagnotchi 😃 +// +// * age: 0 +// * hunger: 50 +// * happiness: 50 +// * health: 50 +// * sleepy: 0 +// +// Actions: +// * [Feed](/r/demo/tamagotchi?help&__func=Feed) +// * [Play](/r/demo/tamagotchi?help&__func=Play) +// * [Heal](/r/demo/tamagotchi?help&__func=Heal) +// * [Reset](/r/demo/tamagotchi?help&__func=Reset) diff --git a/gno.land/Makefile b/gno.land/Makefile index 1297da393cb..982063d155c 100644 --- a/gno.land/Makefile +++ b/gno.land/Makefile @@ -5,8 +5,16 @@ help: rundep=go run -modfile ../misc/devdeps/go.mod -.PHONY: gnoland.start -gnoland.start:; go run ./cmd/gnoland start +gnoland.start: + @#TODO: remove this makefile directive in a few weeks. + @echo "DEPRECATED: use 'start.gnoland' instead of 'gnoland.start'" + go run ./cmd/gnoland start + +.PHONY: start.gnoland +start.gnoland:; go run ./cmd/gnoland start + +.PHONY: start.gnoweb +start.gnoweb:; go run ./cmd/gnoweb .PHONY: build build: build.gnoland build.gnokey build.gnoweb build.gnofaucet build.gnotxsync build.genesis diff --git a/misc/loop/Makefile b/misc/loop/Makefile index ab79b058ffb..d18c83fb84a 100644 --- a/misc/loop/Makefile +++ b/misc/loop/Makefile @@ -15,11 +15,11 @@ gnoland_dir := $(abspath ../../gno.land) all: loop -gnoland.start: +start.gnoland: cd $(gnoland_dir) && $(gnoland_bin) start -skip-failing-genesis-txs -genesis-txs-file $(HISTORY_OUTPUT) -gnoland.clean: +clean.gnoland: make -C $(gnoland_dir) fclean -.PHONY: gnoland.start gnoland.clean +.PHONY: start.gnoland clean.gnoland # Starts the backup service # and backs up transactions into a file @@ -36,7 +36,7 @@ save.history: cat $(BACKUP_FILE) >> $(HISTORY_OUTPUT) .PHONY: save.history -loop: gnoland.clean +loop: clean.gnoland # backup history, if needed $(MAKE) save.history || true # run our dev loop diff --git a/misc/loop/run_loop.sh b/misc/loop/run_loop.sh index 99d1e5fbc04..4b186df9a71 100755 --- a/misc/loop/run_loop.sh +++ b/misc/loop/run_loop.sh @@ -22,7 +22,7 @@ echo "Running local development setup" # - start the backup service for transactions ( echo "Starting Gno node..." - make gnoland.start + make start.gnoland teardown ) & ( From 63ff15b06f465950248af882f06ea11c9bfefe6f Mon Sep 17 00:00:00 2001 From: Guilhem Fanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 7 Dec 2023 18:47:36 +0100 Subject: [PATCH 2/4] fix: Improve BFT Tests Stability (#1385) ref #1320 This draft PR demonstrates a way for fixing flaky BFT tests. As mentioned in #1320 the main issue is that some channels are not fully consumed, leading to deadlocks. By using buffered channels as proxy in state tests, we can easily avoid these deadlocks. However, I don't think this is the best approach, as these channels were originally set to zero (capacity) for good reasons, but perhaps it could make sense for testing purposes. In any case, if anyone has a better idea for a solution, your suggestions are welcome.
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](https://gnoland.github.io/benchmarks), if any. More info [here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
--------- Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> Co-authored-by: Manfred Touron <94029+moul@users.noreply.github.com> --- tm2/pkg/bft/consensus/common_test.go | 38 +++++++++++++++++++++++++++- tm2/pkg/bft/consensus/state_test.go | 5 ---- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/tm2/pkg/bft/consensus/common_test.go b/tm2/pkg/bft/consensus/common_test.go index ba19881aace..db54359f76e 100644 --- a/tm2/pkg/bft/consensus/common_test.go +++ b/tm2/pkg/bft/consensus/common_test.go @@ -6,6 +6,7 @@ import ( "os" "path" "path/filepath" + "reflect" "sort" "sync" "testing" @@ -245,8 +246,34 @@ func validatePrevoteAndPrecommit(t *testing.T, cs *ConsensusState, thisRound, lo validatePrecommit(t, cs, thisRound, lockRound, privVal, votedBlockHash, lockedBlockHash) } +func bufferedEventChannel(in <-chan events.Event, size int) (out chan events.Event) { + out = make(chan events.Event, size) + go func() { + defer close(out) + for evt := range in { + out <- evt + } + }() + + return out +} + +func subscribe(evsw events.EventSwitch, protoevent events.Event) <-chan events.Event { + name := reflect.ValueOf(protoevent).Type().Name() + listenerID := fmt.Sprintf("%s-%s", testSubscriber, name) + ch := events.SubscribeToEvent(evsw, listenerID, protoevent) + + // Similar to subscribeToVoter, this modification introduces + // a buffered channel to ensures that events are consumed + // asynchronously, thereby avoiding the deadlock situation described in + // #1320 where the eventSwitch.FireEvent method was blocked. + bch := bufferedEventChannel(ch, 16) + + return bch +} + func subscribeToVoter(cs *ConsensusState, addr crypto.Address) <-chan events.Event { - return events.SubscribeFiltered(cs.evsw, testSubscriber, func(event events.Event) bool { + ch := events.SubscribeFiltered(cs.evsw, testSubscriber, func(event events.Event) bool { if vote, ok := event.(types.EventVote); ok { if vote.Vote.ValidatorAddress == addr { return true @@ -254,6 +281,15 @@ func subscribeToVoter(cs *ConsensusState, addr crypto.Address) <-chan events.Eve } return false }) + + // This modification addresses the deadlock issue outlined in issue + // #1320. By creating a buffered channel, we ensure that events are + // consumed even if the main thread is blocked. This prevents the + // deadlock that occurred when eventSwitch.FireEvent was blocked due to + // no available consumers for the event. + bch := bufferedEventChannel(ch, 16) + + return bch } // ------------------------------------------------------------------------------- diff --git a/tm2/pkg/bft/consensus/state_test.go b/tm2/pkg/bft/consensus/state_test.go index 35877837ab3..e31817cf9cc 100644 --- a/tm2/pkg/bft/consensus/state_test.go +++ b/tm2/pkg/bft/consensus/state_test.go @@ -11,7 +11,6 @@ import ( cstypes "github.com/gnolang/gno/tm2/pkg/bft/consensus/types" "github.com/gnolang/gno/tm2/pkg/bft/types" - "github.com/gnolang/gno/tm2/pkg/events" p2pmock "github.com/gnolang/gno/tm2/pkg/p2p/mock" "github.com/gnolang/gno/tm2/pkg/random" "github.com/gnolang/gno/tm2/pkg/testutils" @@ -1778,7 +1777,3 @@ func TestStateOutputVoteStats(t *testing.T) { case <-time.After(50 * time.Millisecond): } } - -func subscribe(evsw events.EventSwitch, protoevent events.Event) <-chan events.Event { - return events.SubscribeToEvent(evsw, testSubscriber, protoevent) -} From a338929c764bd72f04283f8ec2e691c4d426ca97 Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Thu, 7 Dec 2023 23:19:35 +0100 Subject: [PATCH 3/4] =?UTF-8?q?feat(examples):=20wugnot=20(grc20=E2=80=99s?= =?UTF-8?q?=20wrapped=20ugnot)=20(#1356)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR presents a GNOT wrapper that conforms to the GRC20 interface. Optimally, the txtar file should be relocated to the wugnot directory, pending resolution of [issue #1269](https://github.com/gnolang/gno/issues/1269#issuecomment-1806386069). Enabling the use of `gnokey maketx call -func WUGNOT.Transfer` over `-func Transfer` would streamline the wrapper, reducing it to just the initial 55 lines.
Contributors' checklist... - [x] Added new tests, or not needed, or not feasible - [x] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [x] Updated the official documentation or not needed - [x] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [x] Added references to related issues and PRs - [x] Provided any useful hints for running manual tests - [x] Added new benchmarks to [generated graphs](https://gnoland.github.io/benchmarks), if any. More info [here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
--------- Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/r/demo/wugnot/gno.mod | 6 + examples/gno.land/r/demo/wugnot/wugnot.gno | 126 ++++++++++++++++++ .../gno.land/r/demo/wugnot/z0_filetest.gno | 75 +++++++++++ gno.land/cmd/gnoland/testdata/wugnot.txtar | 43 ++++++ 4 files changed, 250 insertions(+) create mode 100644 examples/gno.land/r/demo/wugnot/gno.mod create mode 100644 examples/gno.land/r/demo/wugnot/wugnot.gno create mode 100644 examples/gno.land/r/demo/wugnot/z0_filetest.gno create mode 100644 gno.land/cmd/gnoland/testdata/wugnot.txtar diff --git a/examples/gno.land/r/demo/wugnot/gno.mod b/examples/gno.land/r/demo/wugnot/gno.mod new file mode 100644 index 00000000000..1f03ded515c --- /dev/null +++ b/examples/gno.land/r/demo/wugnot/gno.mod @@ -0,0 +1,6 @@ +module gno.land/r/demo/wugnot + +require ( + gno.land/p/demo/grc/grc20 v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest +) diff --git a/examples/gno.land/r/demo/wugnot/wugnot.gno b/examples/gno.land/r/demo/wugnot/wugnot.gno new file mode 100644 index 00000000000..82c3c43db89 --- /dev/null +++ b/examples/gno.land/r/demo/wugnot/wugnot.gno @@ -0,0 +1,126 @@ +package wugnot + +import ( + "std" + "strings" + + "gno.land/p/demo/grc/grc20" + "gno.land/p/demo/ufmt" +) + +var ( + // wugnot is the admin token, able to mint and burn. + wugnot *grc20.AdminToken = grc20.NewAdminToken("wrapped GNOT", "wugnot", 0) + // WUGNOT is the banker usable by users directly. + WUGNOT = wugnot.GRC20() +) + +const ( + ugnotMinDeposit uint64 = 1000 + wugnotMinDeposit uint64 = 1 +) + +// wrapper. +// + +func Deposit() { + caller := std.PrevRealm().Addr() + sent := std.GetOrigSend() + amount := sent.AmountOf("ugnot") + + if uint64(amount) < ugnotMinDeposit { + panic(ufmt.Sprintf("Deposit below minimum: %d/%d ugnot.", amount, ugnotMinDeposit)) + } + wugnot.Mint(caller, uint64(amount)) +} + +func Withdraw(amount uint64) { + if amount < wugnotMinDeposit { + panic(ufmt.Sprintf("Deposit below minimum: %d/%d wugnot.", amount, wugnotMinDeposit)) + } + + caller := std.PrevRealm().Addr() + pkgaddr := std.GetOrigPkgAddr() + + callerBal, _ := wugnot.BalanceOf(caller) + if callerBal < amount { + panic(ufmt.Sprintf("Insufficient balance: %d available, %d needed.", callerBal, amount)) + } + + // send swapped ugnots to caller + banker := std.GetBanker(std.BankerTypeRealmSend) + send := std.Coins{{"ugnot", int64(amount)}} + banker.SendCoins(pkgaddr, caller, send) + wugnot.Burn(caller, amount) +} + +// render. +// + +func Render(path string) string { + parts := strings.Split(path, "/") + c := len(parts) + + switch { + case path == "": + return wugnot.RenderHome() + case c == 2 && parts[0] == "balance": + owner := std.Address(parts[1]) + balance, _ := wugnot.BalanceOf(owner) + return ufmt.Sprintf("%d\n", balance) + default: + return "404\n" + } +} + +// XXX: if we could call WUGNOT.XXX instead of XXX from gnokey, then, all the following lines would not be needed. + +// direct getters. +// XXX: remove them in favor of q_call wugnot.XXX + +func TotalSupply() uint64 { + return wugnot.TotalSupply() +} + +func BalanceOf(owner std.Address) uint64 { + balance, err := wugnot.BalanceOf(owner) + if err != nil { + panic(err) + } + return balance +} + +func Allowance(owner, spender std.Address) uint64 { + allowance, err := wugnot.Allowance(owner, spender) + if err != nil { + panic(err) + } + return allowance +} + +// setters. +// + +func Transfer(to std.Address, amount uint64) { + caller := std.PrevRealm().Addr() + err := wugnot.Transfer(caller, to, amount) + if err != nil { + panic(err) + } +} + +func Approve(spender std.Address, amount uint64) { + caller := std.PrevRealm().Addr() + err := wugnot.Approve(caller, spender, amount) + if err != nil { + panic(err) + } +} + +func TransferFrom(from, to std.Address, amount uint64) { + caller := std.PrevRealm().Addr() + err := wugnot.TransferFrom(caller, from, to, amount) + if err != nil { + panic(err) + } +} diff --git a/examples/gno.land/r/demo/wugnot/z0_filetest.gno b/examples/gno.land/r/demo/wugnot/z0_filetest.gno new file mode 100644 index 00000000000..fa2f45682b1 --- /dev/null +++ b/examples/gno.land/r/demo/wugnot/z0_filetest.gno @@ -0,0 +1,75 @@ +// PKGPATH: gno.land/r/demo/wugnot_test +package wugnot_test + +import ( + "fmt" + "std" + + "gno.land/p/demo/testutils" + "gno.land/r/demo/wugnot" +) + +var ( + addr1 = testutils.TestAddress("test1") + addrc = std.DerivePkgAddr("gno.land/r/demo/wugnot") + addrt = std.DerivePkgAddr("gno.land/r/demo/wugnot_test") +) + +func main() { + std.TestSetOrigPkgAddr(addrc) + std.TestIssueCoins(addrc, std.Coins{{"ugnot", 100000001}}) // TODO: remove this + + // issue ugnots + std.TestIssueCoins(addr1, std.Coins{{"ugnot", 100000001}}) + + // print initial state + printBalances() + // println(wugnot.Render("queues")) + // println("A -", wugnot.Render("")) + + std.TestSetOrigCaller(addr1) + std.TestSetOrigSend(std.Coins{{"ugnot", 123_400}}, nil) + wugnot.Deposit() + printBalances() + wugnot.Withdraw(4242) + printBalances() +} + +func printBalances() { + printSingleBalance := func(name string, addr std.Address) { + wugnotBal := wugnot.BalanceOf(addr) + std.TestSetOrigCaller(addr) + abanker := std.GetBanker(std.BankerTypeOrigSend) + acoins := abanker.GetCoins(addr).AmountOf("ugnot") + bbanker := std.GetBanker(std.BankerTypeRealmIssue) + bcoins := bbanker.GetCoins(addr).AmountOf("ugnot") + cbanker := std.GetBanker(std.BankerTypeRealmSend) + ccoins := cbanker.GetCoins(addr).AmountOf("ugnot") + dbanker := std.GetBanker(std.BankerTypeReadonly) + dcoins := dbanker.GetCoins(addr).AmountOf("ugnot") + fmt.Printf("| %-13s | addr=%s | wugnot=%-5d | ugnot=%-9d %-9d %-9d %-9d |\n", + name, addr, wugnotBal, acoins, bcoins, ccoins, dcoins) + } + println("-----------") + printSingleBalance("wugnot_test", addrt) + printSingleBalance("wugnot", addrc) + printSingleBalance("addr1", addr1) + println("-----------") +} + +// Output: +// ----------- +// | wugnot_test | addr=g19rmydykafrqyyegc8uuaxxpzqwzcnxraj2dev9 | wugnot=0 | ugnot=200000000 200000000 200000000 200000000 | +// | wugnot | addr=g1pf6dv9fjk3rn0m4jjcne306ga4he3mzmupfjl6 | wugnot=0 | ugnot=100000001 100000001 100000001 100000001 | +// | addr1 | addr=g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7 | wugnot=0 | ugnot=100000001 100000001 100000001 100000001 | +// ----------- +// ----------- +// | wugnot_test | addr=g19rmydykafrqyyegc8uuaxxpzqwzcnxraj2dev9 | wugnot=123400 | ugnot=200000000 200000000 200000000 200000000 | +// | wugnot | addr=g1pf6dv9fjk3rn0m4jjcne306ga4he3mzmupfjl6 | wugnot=0 | ugnot=100000001 100000001 100000001 100000001 | +// | addr1 | addr=g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7 | wugnot=0 | ugnot=100000001 100000001 100000001 100000001 | +// ----------- +// ----------- +// | wugnot_test | addr=g19rmydykafrqyyegc8uuaxxpzqwzcnxraj2dev9 | wugnot=119158 | ugnot=200004242 200004242 200004242 200004242 | +// | wugnot | addr=g1pf6dv9fjk3rn0m4jjcne306ga4he3mzmupfjl6 | wugnot=0 | ugnot=99995759 99995759 99995759 99995759 | +// | addr1 | addr=g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7 | wugnot=0 | ugnot=100000001 100000001 100000001 100000001 | +// ----------- diff --git a/gno.land/cmd/gnoland/testdata/wugnot.txtar b/gno.land/cmd/gnoland/testdata/wugnot.txtar new file mode 100644 index 00000000000..5c2d7d3cb90 --- /dev/null +++ b/gno.land/cmd/gnoland/testdata/wugnot.txtar @@ -0,0 +1,43 @@ +gnoland start + +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout '# wrapped GNOT \(\$wugnot\)' +stdout 'Decimals..: 0' +stdout 'Total supply..: 0' +stdout 'Known accounts..: 0' +stdout 'OK!' + +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Deposit -send 12345678ugnot -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 +stdout 'OK!' + +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout 'Total supply..: 12345678' +stdout 'Known accounts..: 1' +stdout 'OK!' + +# XXX: use test2 instead (depends on https://github.com/gnolang/gno/issues/1269#issuecomment-1806386069) +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Deposit -send 12345678ugnot -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 +stdout 'OK!' + +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout 'Total supply..: 24691356' +stdout 'Known accounts..: 1' # should be 2 once we can use test2 +stdout 'OK!' + +# XXX: replace hardcoded address with test3 +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Transfer -gas-fee 1000000ugnot -gas-wanted 2000000 -args 'g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq' -args '10000000' -broadcast -chainid=tendermint_test test1 +stdout 'OK!' + +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout 'Total supply..: 24691356' +stdout 'Known accounts..: 2' # should be 3 once we can use test2 +stdout 'OK!' + +# XXX: use test3 instead (depends on https://github.com/gnolang/gno/issues/1269#issuecomment-1806386069) +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Withdraw -args 10000000 -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 +stdout 'OK!' + +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout 'Total supply..: 14691356' +stdout 'Known accounts..: 2' # should be 3 once we can use test2 +stdout 'OK!' From eef0c980676b8c5909723698a39f0e3679ecfb40 Mon Sep 17 00:00:00 2001 From: Albert Le Batteux Date: Thu, 14 Dec 2023 16:25:40 +0000 Subject: [PATCH 4/4] chore: import secp256k1 for amino parsing (#1178) ## Amino failed to parse crypto.PubKey ### Description This simple client isn't working to get account because amino don't know the struct `BaseAccount.Pubkey crypto.PubKey` This simple main can be fixed by importing, i believe it will make dev experience easier by putting it in `std` rather than clients ``` _ "github.com/gnolang/gno/tm2/pkg/crypto/secp256k1" ``` ```go package main import ( "fmt" "github.com/gnolang/gno/tm2/pkg/amino" rpcClient "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" "github.com/gnolang/gno/tm2/pkg/std" ) // Client is the TM2 HTTP client type Client struct { client rpcClient.Client } // NewClient creates a new TM2 HTTP client func NewClient(remote string) *Client { return &Client{ client: rpcClient.NewHTTP(remote, ""), } } func (c *Client) GetAccount(address string) (std.Account, error) { path := fmt.Sprintf("auth/accounts/%s", address) queryResponse, err := c.client.ABCIQuery(path, []byte{}) if err != nil { return nil, fmt.Errorf("unable to execute ABCI query, %w", err) } var queryData struct{ BaseAccount std.BaseAccount } if err := amino.UnmarshalJSON(queryResponse.Response.Data, &queryData); err != nil { return nil, err } return &queryData.BaseAccount, nil } func main() { c := NewClient("http://rpc.gnochess.com:80") acc, err := c.GetAccount("g1x90eh5ejc22548hjqznm2egyvn8ny36lqu460f") fmt.Println(acc, err) } ``` ``` runtime: goroutine stack exceeds 1000000000-byte limit runtime: sp=0xc020780390 stack=[0xc020780000, 0xc040780000] fatal error: stack overflow runtime stack: runtime.throw({0x9842fa?, 0xd4dea0?}) /nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/runtime/panic.go:1047 +0x5d fp=0x7f681368cc18 sp=0x7f681368cbe8 pc=0x43907d runtime.newstack() /nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/runtime/stack.go:1103 +0x5cc fp=0x7f681368cdd0 sp=0x7f681368cc18 pc=0x452d0c runtime.morestack() /nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/runtime/asm_amd64.s:570 +0x8b fp=0x7f681368cdd8 sp=0x7f681368cdd0 pc=0x46a32b goroutine 1 [running]: fmt.(*fmt).padString(0xc00de1ff20?, {0x8961be, 0x7}) /nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/fmt/format.go:108 +0x299 fp=0xc0207803a0 sp=0xc020780398 pc=0x4dac39 fmt.(*fmt).fmtS(0xc0207803f8?, {0x8961be?, 0x8961bd?}) /nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/fmt/format.go:359 +0x3f fp=0xc0207803d8 sp=0xc0207803a0 pc=0x4db75f fmt.(*pp).fmtString(0x8ca000?, {0x8961be?, 0x8ca000?}, 0x0?) /nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/fmt/print.go:474 +0x86 fp=0xc020780428 sp=0xc0207803d8 pc=0x4de566 fmt.(*pp).handleMethods(0xc00de1fee0, 0x100800?) /nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/fmt/print.go:65 [....] It's a stack overflow due to recursion ```
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](https://gnoland.github.io/benchmarks), if any. More info [here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
--- tm2/pkg/std/account.go | 5 +++++ tm2/pkg/std/package_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 tm2/pkg/std/package_test.go diff --git a/tm2/pkg/std/account.go b/tm2/pkg/std/account.go index 604444b4046..c70f43d22e9 100644 --- a/tm2/pkg/std/account.go +++ b/tm2/pkg/std/account.go @@ -5,6 +5,11 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/errors" + + _ "github.com/gnolang/gno/tm2/pkg/crypto/ed25519" + _ "github.com/gnolang/gno/tm2/pkg/crypto/mock" + _ "github.com/gnolang/gno/tm2/pkg/crypto/multisig" + _ "github.com/gnolang/gno/tm2/pkg/crypto/secp256k1" ) // Account is an interface used to store coins at a given address within state. diff --git a/tm2/pkg/std/package_test.go b/tm2/pkg/std/package_test.go new file mode 100644 index 00000000000..0a21188737b --- /dev/null +++ b/tm2/pkg/std/package_test.go @@ -0,0 +1,26 @@ +package std_test + +import ( + "testing" + + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/std" + "github.com/stretchr/testify/require" +) + +func TestAminoBaseAccount(t *testing.T) { + b := []byte(`{ + "address": "g1x90eh5ejc22548hjqznm2egyvn8ny36lqu460f", + "coins": "4200000ugnot", + "public_key": { + "@type": "/tm.PubKeySecp256k1", + "value": "AwMzujfppqEi8lozMVD8ORENUR8SIE06VLNP8FGL0aQ2" + }, + "account_number": "159", + "sequence": "33" +}`) + acc := std.BaseAccount{} + + err := amino.UnmarshalJSON(b, &acc) + require.NoError(t, err) +}