From a85a53d5b38f0a21d66262a823a8b07f4f836b68 Mon Sep 17 00:00:00 2001
From: piux2 <90544084+piux2@users.noreply.github.com>
Date: Wed, 11 Dec 2024 15:19:04 -0800
Subject: [PATCH 1/2] fix: prevent false positive return for guarding dao
member store (#3121)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
If we want to guard the MemStore by checking the active DAO realm,
m.daoPkgPath must first be assigned a realm package path; otherwise, the
isCallerDAORealm() method may return a false positive, failing to
protect the MemStore.
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
---------
Co-authored-by: Miloš Živković
---
examples/gno.land/p/demo/membstore/membstore.gno | 2 +-
examples/gno.land/r/gov/dao/v2/dao.gno | 4 +++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/examples/gno.land/p/demo/membstore/membstore.gno b/examples/gno.land/p/demo/membstore/membstore.gno
index 6e1932978d9..ca721d078e6 100644
--- a/examples/gno.land/p/demo/membstore/membstore.gno
+++ b/examples/gno.land/p/demo/membstore/membstore.gno
@@ -205,5 +205,5 @@ func (m *MembStore) TotalPower() uint64 {
// the API of the member store is public and callable
// by anyone who has a reference to the member store instance.
func (m *MembStore) isCallerDAORealm() bool {
- return m.daoPkgPath == "" || std.CurrentRealm().PkgPath() == m.daoPkgPath
+ return m.daoPkgPath != "" && std.CurrentRealm().PkgPath() == m.daoPkgPath
}
diff --git a/examples/gno.land/r/gov/dao/v2/dao.gno b/examples/gno.land/r/gov/dao/v2/dao.gno
index 9263d8d440b..5ee8e63236a 100644
--- a/examples/gno.land/r/gov/dao/v2/dao.gno
+++ b/examples/gno.land/r/gov/dao/v2/dao.gno
@@ -13,6 +13,8 @@ var (
members membstore.MemberStore // the member store
)
+const daoPkgPath = "gno.land/r/gov/dao/v2"
+
func init() {
// Example initial member set (just test addresses)
set := []membstore.Member{
@@ -23,7 +25,7 @@ func init() {
}
// Set the member store
- members = membstore.NewMembStore(membstore.WithInitialMembers(set))
+ members = membstore.NewMembStore(membstore.WithInitialMembers(set), membstore.WithDAOPkgPath(daoPkgPath))
// Set the DAO implementation
d = simpledao.New(members)
From 79ca9a958dea7c94aaf6c3dc74f522c5f05e791e Mon Sep 17 00:00:00 2001
From: Mikael VALLENET
Date: Thu, 12 Dec 2024 16:47:27 +0100
Subject: [PATCH 2/2] fix(std): add full denom in banker issue & remove coin
(#3239)
fix #2107
--------------------------
## Problem
> From [#875
(review)](https://github.com/gnolang/gno/pull/875#pullrequestreview-2043984930):
>
> > That said, soon after this is merged, I think we'll need to change
this API again. This current implementation creates an inconsistency
within the Banker API. All other banker methods now require you to pass
in the full realm path to the token you're referring to, but IssueCoin
and RemoveCoin do not.
> > Thus, I think a few more changes are in order:
> >
> > 1. There should be a `RealmDenom(pkgpath, denom string)` function in
`std`, which creates a realm denomination (ie.
`/gno.land/r/morgan:bitcoin`). There can be a helper method
`Realm.Denom(denom string)` (so you can do
`std.CurrentRealm().Denom("bitcoin")`
> > 2. Instead of modifying `denom`'s value in the native function, we
should check it matches what we expect. ie. `strings.HasPrefix(denom,
RealmDenom(std.CurrentRealm().PkgPath())`, then check the last part of
the denom to see that it matches the Gno regex. (This can all be done in
gno, without needing to put it in native code)
>
> Related with #1475 #1576
-------------------------
## Solution
BREAKING CHANGE: All previous realm calling IssueCoin or RemoveCoin are
now expected to append the prefix "/" + realmPkgPath + ":" before the
denom, it should be done by using ``std.CurrentRealm().CoinDenom(denom
string)`` or by using ``std.CoinDenom(pkgPath, denom string)``
For now to avoid to mix coins and fix security issues like being able to
issue coins from other realm, when a realm issue a coin, the pkg path of
the realm is added as a prefix to the coin.
the thing is some function expect only the base denom ``bitcoin`` (issue
& remove) but the others like get require the qualified denom
``gno.land/r/demo/banktest:bitcoin``. it can be confusing
I also answer the requirements of the comment @thehowl made:
- Two functions are now available ``std.CoinDenom(pkgpath, demon
string)`` && the method ``std.Realm.CoinDenom(denom string)``
- the denom's value is changed in the `.gno` file and not the native.
Here is an example of how it looks like:
```go
func IssueNewCoin(denom string, amount int64) string {
std.AssertOriginCall()
banker := std.GetBanker(std.BankerTypeRealmIssue)
addr := std.PrevRealm().Addr()
banker.IssueCoin(addr, std.CurrentRealm().CoinDenom(denom), amount)
return std.CurrentRealm().Denom(denom)
}
func RemoveCoin(denom string, amount int64) {
std.AssertOriginCall()
banker := std.GetBanker(std.BankerTypeRealmIssue)
addr := std.PrevRealm().Addr()
banker.RemoveCoin(addr, std.CurrentRealm().CoinDenom(denom), amount)
}
func GetCoins(denom string) uint64 {
banker := std.GetBanker(std.BankerTypeReadonly)
addr := std.PrevRealm().Addr()
coins := banker.GetCoins(addr)
for _, coin := range coins {
if coin.Denom == std.CurrentRealm().CoinDenom(denom) {
return uint64(coin.Amount)
}
}
return 0
}
```
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
---------
Co-authored-by: Morgan Bazalgette
Co-authored-by: Leon Hudak <33522493+leohhhn@users.noreply.github.com>
---
docs/reference/stdlibs/std/banker.md | 4 ++
docs/reference/stdlibs/std/chain.md | 16 ++++++++
docs/reference/stdlibs/std/realm.md | 13 ++++++
.../gnoland/testdata/assertorigincall.txtar | 40 +++++++++----------
.../cmd/gnoland/testdata/grc20_registry.txtar | 8 ++--
gno.land/cmd/gnoland/testdata/prevrealm.txtar | 22 +++++-----
.../realm_banker_issued_coin_denom.txtar | 40 ++++++++++++++++++-
gno.land/pkg/gnoclient/integration_test.go | 14 +++----
gnovm/stdlibs/std/banker.gno | 32 +++++++++++++++
gnovm/stdlibs/std/banker.go | 30 +-------------
gnovm/stdlibs/std/frame.gno | 12 ++++++
gnovm/tests/files/std5.gno | 2 +-
gnovm/tests/files/std8.gno | 2 +-
gnovm/tests/files/zrealm_natbind0.gno | 2 +-
14 files changed, 162 insertions(+), 75 deletions(-)
diff --git a/docs/reference/stdlibs/std/banker.md b/docs/reference/stdlibs/std/banker.md
index 71eb3709ea2..b60b55ee93b 100644
--- a/docs/reference/stdlibs/std/banker.md
+++ b/docs/reference/stdlibs/std/banker.md
@@ -38,6 +38,10 @@ Returns `Banker` of the specified type.
```go
banker := std.GetBanker(std.)
```
+
+:::info `Banker` methods expect qualified denomination of the coins. Read more [here](./realm.md#coindenom).
+:::
+
---
## GetCoins
diff --git a/docs/reference/stdlibs/std/chain.md b/docs/reference/stdlibs/std/chain.md
index b1791e65608..6a1da6483fd 100644
--- a/docs/reference/stdlibs/std/chain.md
+++ b/docs/reference/stdlibs/std/chain.md
@@ -162,3 +162,19 @@ Derives the Realm address from its `pkgpath` parameter.
```go
realmAddr := std.DerivePkgAddr("gno.land/r/demo/tamagotchi") // g1a3tu874agjlkrpzt9x90xv3uzncapcn959yte4
```
+---
+
+## CoinDenom
+```go
+func CoinDenom(pkgPath, coinName string) string
+```
+Composes a qualified denomination string from the realm's `pkgPath` and the provided coin name, e.g. `/gno.land/r/demo/blog:blgcoin`. This method should be used to get fully qualified denominations of coins when interacting with the `Banker` module. It can also be used as a method of the `Realm` object, Read more[here](./realm.md#coindenom).
+
+#### Parameters
+- `pkgPath` **string** - package path of the realm
+- `coinName` **string** - The coin name used to build the qualified denomination. Must start with a lowercase letter, followed by 2–15 lowercase letters or digits.
+
+#### Usage
+```go
+denom := std.CoinDenom("gno.land/r/demo/blog", "blgcoin") // /gno.land/r/demo/blog:blgcoin
+```
diff --git a/docs/reference/stdlibs/std/realm.md b/docs/reference/stdlibs/std/realm.md
index 0c99b7134ea..f69cd874c75 100644
--- a/docs/reference/stdlibs/std/realm.md
+++ b/docs/reference/stdlibs/std/realm.md
@@ -14,6 +14,7 @@ type Realm struct {
func (r Realm) Addr() Address {...}
func (r Realm) PkgPath() string {...}
func (r Realm) IsUser() bool {...}
+func (r Realm) CoinDenom(coinName string) string {...}
```
## Addr
@@ -39,3 +40,15 @@ Checks if the realm it was called upon is a user realm.
```go
if r.IsUser() {...}
```
+---
+## CoinDenom
+Composes a qualified denomination string from the realm's `pkgPath` and the provided coin name, e.g. `/gno.land/r/demo/blog:blgcoin`. This method should be used to get fully qualified denominations of coins when interacting with the `Banker` module.
+
+#### Parameters
+- `coinName` **string** - The coin name used to build the qualified denomination. Must start with a lowercase letter, followed by 2–15 lowercase letters or digits.
+
+#### Usage
+```go
+// in "gno.land/r/gnoland/blog"
+denom := r.CoinDenom("blgcoin") // /gno.land/r/gnoland/blog:blgcoin
+```
diff --git a/gno.land/cmd/gnoland/testdata/assertorigincall.txtar b/gno.land/cmd/gnoland/testdata/assertorigincall.txtar
index 62d660a9215..1a5664d6bef 100644
--- a/gno.land/cmd/gnoland/testdata/assertorigincall.txtar
+++ b/gno.land/cmd/gnoland/testdata/assertorigincall.txtar
@@ -33,85 +33,85 @@ gnoland start
# Test cases
## 1. MsgCall -> myrlm.A: PANIC
-! gnokey maketx call -pkgpath gno.land/r/myrlm -func A -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
+! gnokey maketx call -pkgpath gno.land/r/myrlm -func A -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1
stderr 'invalid non-origin call'
## 2. MsgCall -> myrlm.B: PASS
-gnokey maketx call -pkgpath gno.land/r/myrlm -func B -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/myrlm -func B -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1
stdout 'OK!'
## 3. MsgCall -> myrlm.C: PASS
-gnokey maketx call -pkgpath gno.land/r/myrlm -func C -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/myrlm -func C -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1
stdout 'OK!'
## 4. MsgCall -> r/foo.A -> myrlm.A: PANIC
-! gnokey maketx call -pkgpath gno.land/r/foo -func A -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
+! gnokey maketx call -pkgpath gno.land/r/foo -func A -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1
stderr 'invalid non-origin call'
## 5. MsgCall -> r/foo.B -> myrlm.B: PASS
-gnokey maketx call -pkgpath gno.land/r/foo -func B -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/foo -func B -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1
stdout 'OK!'
## 6. MsgCall -> r/foo.C -> myrlm.C: PANIC
-! gnokey maketx call -pkgpath gno.land/r/foo -func C -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
+! gnokey maketx call -pkgpath gno.land/r/foo -func C -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1
stderr 'invalid non-origin call'
## remove due to update to maketx call can only call realm (case 7,8,9)
## 7. MsgCall -> p/demo/bar.A: PANIC
-## ! gnokey maketx call -pkgpath gno.land/p/demo/bar -func A -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
+## ! gnokey maketx call -pkgpath gno.land/p/demo/bar -func A -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1
## stderr 'invalid non-origin call'
## 8. MsgCall -> p/demo/bar.B: PASS
-## gnokey maketx call -pkgpath gno.land/p/demo/bar -func B -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
+## gnokey maketx call -pkgpath gno.land/p/demo/bar -func B -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1
## stdout 'OK!'
## 9. MsgCall -> p/demo/bar.C: PANIC
-## ! gnokey maketx call -pkgpath gno.land/p/demo/bar -func C -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
+## ! gnokey maketx call -pkgpath gno.land/p/demo/bar -func C -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1
## stderr 'invalid non-origin call'
## 10. MsgRun -> run.main -> myrlm.A: PANIC
-! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmA.gno
+! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmA.gno
stderr 'invalid non-origin call'
## 11. MsgRun -> run.main -> myrlm.B: PASS
-gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmB.gno
+gnokey maketx run -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmB.gno
stdout 'OK!'
## 12. MsgRun -> run.main -> myrlm.C: PANIC
-! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmC.gno
+! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmC.gno
stderr 'invalid non-origin call'
## 13. MsgRun -> run.main -> foo.A: PANIC
-! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooA.gno
+! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooA.gno
stderr 'invalid non-origin call'
## 14. MsgRun -> run.main -> foo.B: PASS
-gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooB.gno
+gnokey maketx run -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooB.gno
stdout 'OK!'
## 15. MsgRun -> run.main -> foo.C: PANIC
-! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooC.gno
+! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooC.gno
stderr 'invalid non-origin call'
## 16. MsgRun -> run.main -> bar.A: PANIC
-! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/barA.gno
+! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1 $WORK/run/barA.gno
stderr 'invalid non-origin call'
## 17. MsgRun -> run.main -> bar.B: PASS
-gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/barB.gno
+gnokey maketx run -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1 $WORK/run/barB.gno
stdout 'OK!'
## 18. MsgRun -> run.main -> bar.C: PANIC
-! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/barC.gno
+! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1 $WORK/run/barC.gno
stderr 'invalid non-origin call'
## remove testcase 19 due to maketx call forced to call a realm
## 19. MsgCall -> std.AssertOriginCall: pass
-## gnokey maketx call -pkgpath std -func AssertOriginCall -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
+## gnokey maketx call -pkgpath std -func AssertOriginCall -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1
## stdout 'OK!'
## 20. MsgRun -> std.AssertOriginCall: PANIC
-! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/baz.gno
+! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1 $WORK/run/baz.gno
stderr 'invalid non-origin call'
diff --git a/gno.land/cmd/gnoland/testdata/grc20_registry.txtar b/gno.land/cmd/gnoland/testdata/grc20_registry.txtar
index a5f7ad5eee3..417ab04539d 100644
--- a/gno.land/cmd/gnoland/testdata/grc20_registry.txtar
+++ b/gno.land/cmd/gnoland/testdata/grc20_registry.txtar
@@ -6,15 +6,15 @@ loadpkg gno.land/r/registry $WORK/registry
gnoland start
# we call Transfer with foo20, before it's registered
-gnokey maketx call -pkgpath gno.land/r/registry -func TransferByName -args 'foo20' -args 'g123456789' -args '42' -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/registry -func TransferByName -args 'foo20' -args 'g123456789' -args '42' -gas-fee 1000000ugnot -gas-wanted 5000000 -broadcast -chainid=tendermint_test test1
stdout 'not found'
# add foo20, and foo20wrapper
-gnokey maketx addpkg -pkgdir $WORK/foo20 -pkgpath gno.land/r/foo20 -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
-gnokey maketx addpkg -pkgdir $WORK/foo20wrapper -pkgpath gno.land/r/foo20wrapper -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx addpkg -pkgdir $WORK/foo20 -pkgpath gno.land/r/foo20 -gas-fee 1000000ugnot -gas-wanted 5000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx addpkg -pkgdir $WORK/foo20wrapper -pkgpath gno.land/r/foo20wrapper -gas-fee 1000000ugnot -gas-wanted 5000000 -broadcast -chainid=tendermint_test test1
# we call Transfer with foo20, after it's registered
-gnokey maketx call -pkgpath gno.land/r/registry -func TransferByName -args 'foo20' -args 'g123456789' -args '42' -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/registry -func TransferByName -args 'foo20' -args 'g123456789' -args '42' -gas-fee 1000000ugnot -gas-wanted 5000000 -broadcast -chainid=tendermint_test test1
stdout 'same address, success!'
-- registry/registry.gno --
diff --git a/gno.land/cmd/gnoland/testdata/prevrealm.txtar b/gno.land/cmd/gnoland/testdata/prevrealm.txtar
index 4a7cece6d62..58b0cdce1d6 100644
--- a/gno.land/cmd/gnoland/testdata/prevrealm.txtar
+++ b/gno.land/cmd/gnoland/testdata/prevrealm.txtar
@@ -34,19 +34,19 @@ env RFOO_ADDR=g1evezrh92xaucffmtgsaa3rvmz5s8kedffsg469
# Test cases
## 1. MsgCall -> myrlm.A: user address
-gnokey maketx call -pkgpath gno.land/r/myrlm -func A -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/myrlm -func A -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1
stdout ${USER_ADDR_test1}
## 2. MsgCall -> myrealm.B -> myrlm.A: user address
-gnokey maketx call -pkgpath gno.land/r/myrlm -func B -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/myrlm -func B -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1
stdout ${USER_ADDR_test1}
## 3. MsgCall -> r/foo.A -> myrlm.A: r/foo
-gnokey maketx call -pkgpath gno.land/r/foo -func A -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/foo -func A -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1
stdout ${RFOO_ADDR}
## 4. MsgCall -> r/foo.B -> myrlm.B -> r/foo.A: r/foo
-gnokey maketx call -pkgpath gno.land/r/foo -func B -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/foo -func B -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1
stdout ${RFOO_ADDR}
## remove due to update to maketx call can only call realm (case 5, 6, 13)
@@ -59,27 +59,27 @@ stdout ${RFOO_ADDR}
## stdout ${USER_ADDR_test1}
## 7. MsgRun -> myrlm.A: user address
-gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmA.gno
+gnokey maketx run -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmA.gno
stdout ${USER_ADDR_test1}
## 8. MsgRun -> myrealm.B -> myrlm.A: user address
-gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmB.gno
+gnokey maketx run -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmB.gno
stdout ${USER_ADDR_test1}
## 9. MsgRun -> r/foo.A -> myrlm.A: r/foo
-gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooA.gno
+gnokey maketx run -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooA.gno
stdout ${RFOO_ADDR}
## 10. MsgRun -> r/foo.B -> myrlm.B -> r/foo.A: r/foo
-gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooB.gno
+gnokey maketx run -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooB.gno
stdout ${RFOO_ADDR}
## 11. MsgRun -> p/demo/bar.A: user address
-gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/barA.gno
+gnokey maketx run -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1 $WORK/run/barA.gno
stdout ${USER_ADDR_test1}
## 12. MsgRun -> p/demo/bar.B: user address
-gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/barB.gno
+gnokey maketx run -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1 $WORK/run/barB.gno
stdout ${USER_ADDR_test1}
## 13. MsgCall -> std.PrevRealm(): user address
@@ -87,7 +87,7 @@ stdout ${USER_ADDR_test1}
## stdout ${USER_ADDR_test1}
## 14. MsgRun -> std.PrevRealm(): user address
-gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/baz.gno
+gnokey maketx run -gas-fee 100000ugnot -gas-wanted 5000000 -broadcast -chainid tendermint_test test1 $WORK/run/baz.gno
stdout ${USER_ADDR_test1}
-- r/myrlm/myrlm.gno --
diff --git a/gno.land/cmd/gnoland/testdata/realm_banker_issued_coin_denom.txtar b/gno.land/cmd/gnoland/testdata/realm_banker_issued_coin_denom.txtar
index 71ef6400471..be9a686bac6 100644
--- a/gno.land/cmd/gnoland/testdata/realm_banker_issued_coin_denom.txtar
+++ b/gno.land/cmd/gnoland/testdata/realm_banker_issued_coin_denom.txtar
@@ -12,6 +12,9 @@ gnokey maketx addpkg -pkgdir $WORK/short -pkgpath gno.land/r/test/realm_banker -
## add realm_banker with long package_name
gnokey maketx addpkg -pkgdir $WORK/long -pkgpath gno.land/r/test/package89_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_1234567890 -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test test1
+## add invalid realm_denom
+gnokey maketx addpkg -pkgdir $WORK/invalid_realm_denom -pkgpath gno.land/r/test/invalid_realm_denom -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test test1
+
## test2 spend all balance
gnokey maketx send -send "9999999ugnot" -to g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5 -gas-fee 1ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test2
@@ -52,6 +55,22 @@ gnokey maketx call -pkgpath gno.land/r/test/package89_123456789_123456789_123456
gnokey query bank/balances/g1cq2ecdq3eyn5qa0fzznpurg87zq3k77g63q6u7
stdout '"100/gno.land/r/test/package89_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_1234567890:ugnot"'
+## mint invalid base denom
+! gnokey maketx call -pkgpath gno.land/r/test/realm_banker -func Mint -args "g1cq2ecdq3eyn5qa0fzznpurg87zq3k77g63q6u7" -args "2gnot" -args "100" -gas-fee 1000000ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test1
+stderr 'cannot issue coins with invalid denom base name, it should start by a lowercase letter and be followed by 2-15 lowercase letters or digits'
+
+## burn invalid base denom
+! gnokey maketx call -pkgpath gno.land/r/test/realm_banker -func Burn -args "g1cq2ecdq3eyn5qa0fzznpurg87zq3k77g63q6u7" -args "2gnot" -args "100" -gas-fee 1000000ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test1
+stderr 'cannot issue coins with invalid denom base name, it should start by a lowercase letter and be followed by 2-15 lowercase letters or digits'
+
+## mint invalid realm denom
+! gnokey maketx call -pkgpath gno.land/r/test/invalid_realm_denom -func Mint -args "g1cq2ecdq3eyn5qa0fzznpurg87zq3k77g63q6u7" -args "ugnot" -args "100" -gas-fee 1000000ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test1
+stderr 'invalid denom, can only issue/remove coins with the realm.s prefix'
+
+## burn invalid realm denom
+! gnokey maketx call -pkgpath gno.land/r/test/invalid_realm_denom -func Burn -args "g1cq2ecdq3eyn5qa0fzznpurg87zq3k77g63q6u7" -args "ugnot" -args "100" -gas-fee 1000000ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test1
+stderr 'invalid denom, can only issue/remove coins with the realm.s prefix'
+
-- short/realm_banker.gno --
package realm_banker
@@ -61,12 +80,12 @@ import (
func Mint(addr std.Address, denom string, amount int64) {
banker := std.GetBanker(std.BankerTypeRealmIssue)
- banker.IssueCoin(addr, denom, amount)
+ banker.IssueCoin(addr, std.CurrentRealm().CoinDenom(denom), amount)
}
func Burn(addr std.Address, denom string, amount int64) {
banker := std.GetBanker(std.BankerTypeRealmIssue)
- banker.RemoveCoin(addr, denom, amount)
+ banker.RemoveCoin(addr, std.CurrentRealm().CoinDenom(denom), amount)
}
-- long/realm_banker.gno --
@@ -77,6 +96,23 @@ import (
"std"
)
+func Mint(addr std.Address, denom string, amount int64) {
+ banker := std.GetBanker(std.BankerTypeRealmIssue)
+ banker.IssueCoin(addr, std.CurrentRealm().CoinDenom(denom), amount)
+}
+
+func Burn(addr std.Address, denom string, amount int64) {
+ banker := std.GetBanker(std.BankerTypeRealmIssue)
+ banker.RemoveCoin(addr, std.CurrentRealm().CoinDenom(denom), amount)
+}
+
+-- invalid_realm_denom/realm_banker.gno --
+package invalid_realm_denom
+
+import (
+ "std"
+)
+
func Mint(addr std.Address, denom string, amount int64) {
banker := std.GetBanker(std.BankerTypeRealmIssue)
banker.IssueCoin(addr, denom, amount)
diff --git a/gno.land/pkg/gnoclient/integration_test.go b/gno.land/pkg/gnoclient/integration_test.go
index 0a06eb4756a..945121fbacf 100644
--- a/gno.land/pkg/gnoclient/integration_test.go
+++ b/gno.land/pkg/gnoclient/integration_test.go
@@ -40,7 +40,7 @@ func TestCallSingle_Integration(t *testing.T) {
// Make Tx config
baseCfg := BaseTxCfg{
GasFee: ugnot.ValueString(10000),
- GasWanted: 8000000,
+ GasWanted: 9000000,
AccountNumber: 0,
SequenceNumber: 0,
Memo: "",
@@ -93,7 +93,7 @@ func TestCallMultiple_Integration(t *testing.T) {
// Make Tx config
baseCfg := BaseTxCfg{
GasFee: ugnot.ValueString(10000),
- GasWanted: 8000000,
+ GasWanted: 9000000,
AccountNumber: 0,
SequenceNumber: 0,
Memo: "",
@@ -155,7 +155,7 @@ func TestSendSingle_Integration(t *testing.T) {
// Make Tx config
baseCfg := BaseTxCfg{
GasFee: ugnot.ValueString(10000),
- GasWanted: 8000000,
+ GasWanted: 9000000,
AccountNumber: 0,
SequenceNumber: 0,
Memo: "",
@@ -219,7 +219,7 @@ func TestSendMultiple_Integration(t *testing.T) {
// Make Tx config
baseCfg := BaseTxCfg{
GasFee: ugnot.ValueString(10000),
- GasWanted: 8000000,
+ GasWanted: 9000000,
AccountNumber: 0,
SequenceNumber: 0,
Memo: "",
@@ -291,7 +291,7 @@ func TestRunSingle_Integration(t *testing.T) {
// Make Tx config
baseCfg := BaseTxCfg{
GasFee: ugnot.ValueString(10000),
- GasWanted: 8000000,
+ GasWanted: 9000000,
AccountNumber: 0,
SequenceNumber: 0,
Memo: "",
@@ -452,7 +452,7 @@ func TestAddPackageSingle_Integration(t *testing.T) {
// Make Tx config
baseCfg := BaseTxCfg{
GasFee: ugnot.ValueString(10000),
- GasWanted: 8000000,
+ GasWanted: 9000000,
AccountNumber: 0,
SequenceNumber: 0,
Memo: "",
@@ -537,7 +537,7 @@ func TestAddPackageMultiple_Integration(t *testing.T) {
// Make Tx config
baseCfg := BaseTxCfg{
GasFee: ugnot.ValueString(10000),
- GasWanted: 8000000,
+ GasWanted: 9000000,
AccountNumber: 0,
SequenceNumber: 0,
Memo: "",
diff --git a/gnovm/stdlibs/std/banker.gno b/gnovm/stdlibs/std/banker.gno
index 5412b73281c..4c20e8d4b61 100644
--- a/gnovm/stdlibs/std/banker.gno
+++ b/gnovm/stdlibs/std/banker.gno
@@ -2,6 +2,7 @@ package std
import (
"strconv"
+ "strings"
)
// Realm functions can call std.GetBanker(options) to get
@@ -126,6 +127,7 @@ func (b banker) IssueCoin(addr Address, denom string, amount int64) {
if b.bt != BankerTypeRealmIssue {
panic(b.bt.String() + " cannot issue coins")
}
+ assertCoinDenom(denom)
bankerIssueCoin(uint8(b.bt), string(addr), denom, amount)
}
@@ -133,5 +135,35 @@ func (b banker) RemoveCoin(addr Address, denom string, amount int64) {
if b.bt != BankerTypeRealmIssue {
panic(b.bt.String() + " cannot remove coins")
}
+ assertCoinDenom(denom)
bankerRemoveCoin(uint8(b.bt), string(addr), denom, amount)
}
+
+func assertCoinDenom(denom string) {
+ prefix := "/" + CurrentRealm().PkgPath() + ":"
+ if !strings.HasPrefix(denom, prefix) {
+ panic("invalid denom, can only issue/remove coins with the realm's prefix: " + prefix)
+ }
+
+ baseDenom := denom[len(prefix):]
+ if !isValidBaseDenom(baseDenom) {
+ panic("cannot issue coins with invalid denom base name, it should start by a lowercase letter and be followed by 2-15 lowercase letters or digits")
+ }
+}
+
+// check start by a lowercase letter and be followed by 2-15 lowercase letters or digits
+func isValidBaseDenom(denom string) bool {
+ length := len(denom)
+ if length < 3 || length > 16 {
+ return false
+ }
+ for i, c := range denom {
+ switch {
+ case c >= 'a' && c <= 'z',
+ i > 0 && (c >= '0' && c <= '9'): // continue
+ default:
+ return false
+ }
+ }
+ return true
+}
diff --git a/gnovm/stdlibs/std/banker.go b/gnovm/stdlibs/std/banker.go
index 892af94777f..c57ba8529ed 100644
--- a/gnovm/stdlibs/std/banker.go
+++ b/gnovm/stdlibs/std/banker.go
@@ -2,7 +2,6 @@ package std
import (
"fmt"
- "regexp"
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/tm2/pkg/crypto"
@@ -33,9 +32,6 @@ const (
btRealmIssue
)
-// regexp for denom format
-var reDenom = regexp.MustCompile("[a-z][a-z0-9]{2,15}")
-
func X_bankerGetCoins(m *gno.Machine, bt uint8, addr string) (denoms []string, amounts []int64) {
coins := GetContext(m).Banker.GetCoins(crypto.Bech32Address(addr))
return ExpandCoins(coins)
@@ -74,31 +70,9 @@ func X_bankerTotalCoin(m *gno.Machine, bt uint8, denom string) int64 {
}
func X_bankerIssueCoin(m *gno.Machine, bt uint8, addr string, denom string, amount int64) {
- // gno checks for bt == RealmIssue
-
- // check origin denom format
- matched := reDenom.MatchString(denom)
- if !matched {
- m.Panic(typedString("invalid denom format to issue coin, must be " + reDenom.String()))
- return
- }
-
- // Similar to ibc spec
- // ibc_denom := 'ibc/' + hash('path' + 'base_denom')
- // gno_realm_denom := '/' + 'pkg_path' + ':' + 'base_denom'
- newDenom := "/" + m.Realm.Path + ":" + denom
- GetContext(m).Banker.IssueCoin(crypto.Bech32Address(addr), newDenom, amount)
+ GetContext(m).Banker.IssueCoin(crypto.Bech32Address(addr), denom, amount)
}
func X_bankerRemoveCoin(m *gno.Machine, bt uint8, addr string, denom string, amount int64) {
- // gno checks for bt == RealmIssue
-
- matched := reDenom.MatchString(denom)
- if !matched {
- m.Panic(typedString("invalid denom format to remove coin, must be " + reDenom.String()))
- return
- }
-
- newDenom := "/" + m.Realm.Path + ":" + denom
- GetContext(m).Banker.RemoveCoin(crypto.Bech32Address(addr), newDenom, amount)
+ GetContext(m).Banker.RemoveCoin(crypto.Bech32Address(addr), denom, amount)
}
diff --git a/gnovm/stdlibs/std/frame.gno b/gnovm/stdlibs/std/frame.gno
index bc3a000f5a0..1709f8cb8b5 100644
--- a/gnovm/stdlibs/std/frame.gno
+++ b/gnovm/stdlibs/std/frame.gno
@@ -16,3 +16,15 @@ func (r Realm) PkgPath() string {
func (r Realm) IsUser() bool {
return r.pkgPath == ""
}
+
+func (r Realm) CoinDenom(coinName string) string {
+ return CoinDenom(r.pkgPath, coinName)
+}
+
+func CoinDenom(pkgPath, coinName string) string {
+ // TODO: Possibly remove after https://github.com/gnolang/gno/issues/3164
+ // Similar to ibc spec
+ // ibc_denom := 'ibc/' + hash('path' + 'base_denom')
+ // gno_qualified_denom := '/' + 'pkg_path' + ':' + 'base_denom'
+ return "/" + pkgPath + ":" + coinName
+}
diff --git a/gnovm/tests/files/std5.gno b/gnovm/tests/files/std5.gno
index 2baba6b5005..e339d7a6364 100644
--- a/gnovm/tests/files/std5.gno
+++ b/gnovm/tests/files/std5.gno
@@ -13,7 +13,7 @@ func main() {
// Stacktrace:
// panic: frame not found
-// callerAt(n)
+// callerAt(n)
// gonative:std.callerAt
// std.GetCallerAt(2)
// std/native.gno:45
diff --git a/gnovm/tests/files/std8.gno b/gnovm/tests/files/std8.gno
index 4f749c3a6e1..ee717bf16be 100644
--- a/gnovm/tests/files/std8.gno
+++ b/gnovm/tests/files/std8.gno
@@ -23,7 +23,7 @@ func main() {
// Stacktrace:
// panic: frame not found
-// callerAt(n)
+// callerAt(n)
// gonative:std.callerAt
// std.GetCallerAt(4)
// std/native.gno:45
diff --git a/gnovm/tests/files/zrealm_natbind0.gno b/gnovm/tests/files/zrealm_natbind0.gno
index 16a374164d5..8e5f641e734 100644
--- a/gnovm/tests/files/zrealm_natbind0.gno
+++ b/gnovm/tests/files/zrealm_natbind0.gno
@@ -69,7 +69,7 @@ func main() {
// "Closure": {
// "@type": "/gno.RefValue",
// "Escaped": true,
-// "ObjectID": "a7f5397443359ea76c50be82c77f1f893a060925:8"
+// "ObjectID": "a7f5397443359ea76c50be82c77f1f893a060925:9"
// },
// "FileName": "native.gno",
// "IsMethod": false,