` (default is page 1 and size 10).
func Render(path string) string {
- p := pager.NewPager(tree, 10) // Default page size is 10
+ p := pager.NewPager(tree, 10, false) // Default page size is 10
page := p.MustGetPageByPath(path)
// Header and pagination info
result := "# Paginated Items\n"
result += "Page " + strconv.Itoa(page.PageNumber) + " of " + strconv.Itoa(page.TotalPages) + "\n\n"
- result += page.Selector() + "\n\n"
+ result += page.Picker() + "\n\n"
// Display items on the current page
for _, item := range page.Items {
result += "- " + item.Key + ": " + item.Value.(string) + "\n"
}
- result += "\n" + page.Selector() // Repeat selector for ease of navigation
+ result += "\n" + page.Picker() // Repeat page picker for ease of navigation
return result
}
diff --git a/examples/gno.land/r/leon/hof/render.gno b/examples/gno.land/r/leon/hof/render.gno
index b4d51d03362..0721c7d6e72 100644
--- a/examples/gno.land/r/leon/hof/render.gno
+++ b/examples/gno.land/r/leon/hof/render.gno
@@ -38,7 +38,7 @@ func (e Exhibition) Render(path string, dashboard bool) string {
out += "\n\n"
- page := pager.NewPager(e.itemsSorted, pageSize).MustGetPageByPath(path)
+ page := pager.NewPager(e.itemsSorted, pageSize, false).MustGetPageByPath(path)
for i := len(page.Items) - 1; i >= 0; i-- {
item := page.Items[i]
@@ -52,7 +52,7 @@ func (e Exhibition) Render(path string, dashboard bool) string {
out += "
\n\n"
- out += page.Selector()
+ out += page.Picker()
return out
}
From e35fc9a1c6f5e561660a29988bfc3eefa9280c4f Mon Sep 17 00:00:00 2001
From: Leon Hudak <33522493+leohhhn@users.noreply.github.com>
Date: Sun, 8 Dec 2024 17:32:23 +0100
Subject: [PATCH 05/22] docs: add `std.ChainDomain()` reference (#3301)
## Description
Adds the reference docs on `std.ChainDomain()`.
Co-authored-by: Antoine Eddi <5222525+aeddi@users.noreply.github.com>
---
docs/reference/stdlibs/std/chain.md | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/docs/reference/stdlibs/std/chain.md b/docs/reference/stdlibs/std/chain.md
index 0e5ead338c5..b1791e65608 100644
--- a/docs/reference/stdlibs/std/chain.md
+++ b/docs/reference/stdlibs/std/chain.md
@@ -28,6 +28,18 @@ std.AssertOriginCall()
```
---
+## ChainDomain
+```go
+func ChainDomain() string
+```
+Returns the chain domain. Currently only `gno.land` is supported.
+
+#### Usage
+```go
+domain := std.ChainDomain() // gno.land
+```
+---
+
## Emit
```go
func Emit(typ string, attrs ...string)
From e46f457e70ac2881a8d2c0e3fe625ca9ecdda183 Mon Sep 17 00:00:00 2001
From: Manfred Touron <94029+moul@users.noreply.github.com>
Date: Sun, 8 Dec 2024 17:51:01 +0100
Subject: [PATCH 06/22] chore: remove PR template (#3300)
See https://github.com/gnolang/gno/issues/3238#issuecomment-2526138055
Signed-off-by: moul <94029+moul@users.noreply.github.com>
---
.github/pull_request_template.md | 11 -----------
1 file changed, 11 deletions(-)
delete mode 100644 .github/pull_request_template.md
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
deleted file mode 100644
index 12e07a9cde6..00000000000
--- a/.github/pull_request_template.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-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
-
From 666c54af3d4a7cf354546028de89ec99fe1ce984 Mon Sep 17 00:00:00 2001
From: Marc Vertes
Date: Sun, 8 Dec 2024 19:20:00 +0100
Subject: [PATCH 07/22] chore: import gnolang/overflow into
gno/tm2/pkg/overflow (#3302)
We remove the dependency on gnolang/overflow, a clone of
https://github.com/JohnCGriffin/overflow which is now unmaintained, and
import it into gno/tm2/pkg/overflow. We can now have full control on it,
fix it and improve it.
This PR just changes the import path, no content change is done yet.
---------
Co-authored-by: Morgan Bazalgette
---
contribs/gnodev/go.mod | 1 -
contribs/gnodev/go.sum | 2 -
contribs/gnofaucet/go.mod | 1 -
contribs/gnofaucet/go.sum | 2 -
contribs/gnogenesis/go.mod | 1 -
contribs/gnogenesis/go.sum | 2 -
contribs/gnohealth/go.mod | 1 -
contribs/gnohealth/go.sum | 2 -
contribs/gnokeykc/go.mod | 1 -
contribs/gnokeykc/go.sum | 2 -
contribs/gnomigrate/go.mod | 1 -
contribs/gnomigrate/go.sum | 2 -
gnovm/pkg/gnolang/machine.go | 3 +-
go.mod | 1 -
go.sum | 2 -
misc/autocounterd/go.mod | 1 -
misc/autocounterd/go.sum | 2 -
misc/loop/go.mod | 1 -
misc/loop/go.sum | 4 -
tm2/pkg/overflow/README.md | 66 +++++
tm2/pkg/overflow/overflow.go | 131 ++++++++++
tm2/pkg/overflow/overflow_impl.go | 360 ++++++++++++++++++++++++++
tm2/pkg/overflow/overflow_template.sh | 112 ++++++++
tm2/pkg/overflow/overflow_test.go | 115 ++++++++
tm2/pkg/std/coin.go | 2 +-
tm2/pkg/store/gas/store.go | 2 +-
tm2/pkg/store/types/gas.go | 2 +-
tm2/pkg/store/types/gas_test.go | 2 +-
28 files changed, 789 insertions(+), 35 deletions(-)
create mode 100644 tm2/pkg/overflow/README.md
create mode 100644 tm2/pkg/overflow/overflow.go
create mode 100644 tm2/pkg/overflow/overflow_impl.go
create mode 100755 tm2/pkg/overflow/overflow_template.sh
create mode 100644 tm2/pkg/overflow/overflow_test.go
diff --git a/contribs/gnodev/go.mod b/contribs/gnodev/go.mod
index a315d88591c..2053a61db6c 100644
--- a/contribs/gnodev/go.mod
+++ b/contribs/gnodev/go.mod
@@ -50,7 +50,6 @@ require (
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
- github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
diff --git a/contribs/gnodev/go.sum b/contribs/gnodev/go.sum
index e38c3621483..f9250d34462 100644
--- a/contribs/gnodev/go.sum
+++ b/contribs/gnodev/go.sum
@@ -101,8 +101,6 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
diff --git a/contribs/gnofaucet/go.mod b/contribs/gnofaucet/go.mod
index c5bb1ad0d81..eab9fc90c50 100644
--- a/contribs/gnofaucet/go.mod
+++ b/contribs/gnofaucet/go.mod
@@ -20,7 +20,6 @@ require (
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
- github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect
github.com/go-chi/chi/v5 v5.1.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
diff --git a/contribs/gnofaucet/go.sum b/contribs/gnofaucet/go.sum
index f4bdc65d7ec..aabe858e893 100644
--- a/contribs/gnofaucet/go.sum
+++ b/contribs/gnofaucet/go.sum
@@ -49,8 +49,6 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gnolang/faucet v0.3.2 h1:3QBrdmnQszRaAZbxgO5xDDm3czNa0L/RFmhnCkbxy5I=
github.com/gnolang/faucet v0.3.2/go.mod h1:/wbw9h4ooMzzyNBuM0X+ol7CiPH2OFjAFF3bYAXqA7U=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
diff --git a/contribs/gnogenesis/go.mod b/contribs/gnogenesis/go.mod
index b777cc6e5eb..f1b316c2bee 100644
--- a/contribs/gnogenesis/go.mod
+++ b/contribs/gnogenesis/go.mod
@@ -18,7 +18,6 @@ require (
github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
- github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
diff --git a/contribs/gnogenesis/go.sum b/contribs/gnogenesis/go.sum
index 3c6127ac216..7ba3aede534 100644
--- a/contribs/gnogenesis/go.sum
+++ b/contribs/gnogenesis/go.sum
@@ -50,8 +50,6 @@ github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHqu
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
diff --git a/contribs/gnohealth/go.mod b/contribs/gnohealth/go.mod
index e6d9f119c7b..4f5862a0d2e 100644
--- a/contribs/gnohealth/go.mod
+++ b/contribs/gnohealth/go.mod
@@ -12,7 +12,6 @@ require (
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
- github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/uuid v1.6.0 // indirect
diff --git a/contribs/gnohealth/go.sum b/contribs/gnohealth/go.sum
index 116cfbff021..dd287d9ca84 100644
--- a/contribs/gnohealth/go.sum
+++ b/contribs/gnohealth/go.sum
@@ -45,8 +45,6 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
diff --git a/contribs/gnokeykc/go.mod b/contribs/gnokeykc/go.mod
index 0c794afd54c..479daed22f6 100644
--- a/contribs/gnokeykc/go.mod
+++ b/contribs/gnokeykc/go.mod
@@ -21,7 +21,6 @@ require (
github.com/danieljoos/wincred v1.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
- github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
diff --git a/contribs/gnokeykc/go.sum b/contribs/gnokeykc/go.sum
index 50eb5add218..cacf6788d45 100644
--- a/contribs/gnokeykc/go.sum
+++ b/contribs/gnokeykc/go.sum
@@ -54,8 +54,6 @@ github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHqu
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
diff --git a/contribs/gnomigrate/go.mod b/contribs/gnomigrate/go.mod
index a81c2de4ba0..cd31adc4f6f 100644
--- a/contribs/gnomigrate/go.mod
+++ b/contribs/gnomigrate/go.mod
@@ -17,7 +17,6 @@ require (
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
- github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
diff --git a/contribs/gnomigrate/go.sum b/contribs/gnomigrate/go.sum
index 3c6127ac216..7ba3aede534 100644
--- a/contribs/gnomigrate/go.sum
+++ b/contribs/gnomigrate/go.sum
@@ -50,8 +50,6 @@ github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHqu
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go
index b48c0742e6f..a497648dbc8 100644
--- a/gnovm/pkg/gnolang/machine.go
+++ b/gnovm/pkg/gnolang/machine.go
@@ -11,10 +11,9 @@ import (
"strings"
"sync"
- "github.com/gnolang/overflow"
-
"github.com/gnolang/gno/gnovm"
"github.com/gnolang/gno/tm2/pkg/errors"
+ "github.com/gnolang/gno/tm2/pkg/overflow"
"github.com/gnolang/gno/tm2/pkg/store"
)
diff --git a/go.mod b/go.mod
index f73ba1926e6..f389e60b988 100644
--- a/go.mod
+++ b/go.mod
@@ -13,7 +13,6 @@ require (
github.com/davecgh/go-spew v1.1.1
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0
github.com/fortytw2/leaktest v1.3.0
- github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216
github.com/google/gofuzz v1.2.0
github.com/gorilla/mux v1.8.1
github.com/gorilla/websocket v1.5.3
diff --git a/go.sum b/go.sum
index 78d60eeea90..b987535607e 100644
--- a/go.sum
+++ b/go.sum
@@ -51,8 +51,6 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
diff --git a/misc/autocounterd/go.mod b/misc/autocounterd/go.mod
index 5de1d3c2974..30a6f23b458 100644
--- a/misc/autocounterd/go.mod
+++ b/misc/autocounterd/go.mod
@@ -14,7 +14,6 @@ require (
github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
- github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
diff --git a/misc/autocounterd/go.sum b/misc/autocounterd/go.sum
index b34cbde0c00..5d624ca18cb 100644
--- a/misc/autocounterd/go.sum
+++ b/misc/autocounterd/go.sum
@@ -50,8 +50,6 @@ github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHqu
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
diff --git a/misc/loop/go.mod b/misc/loop/go.mod
index a6bbdad3c82..70e9d21734b 100644
--- a/misc/loop/go.mod
+++ b/misc/loop/go.mod
@@ -29,7 +29,6 @@ require (
github.com/distribution/reference v0.5.0 // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
- github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
diff --git a/misc/loop/go.sum b/misc/loop/go.sum
index 740cc629a21..8e0feb11e4a 100644
--- a/misc/loop/go.sum
+++ b/misc/loop/go.sum
@@ -68,8 +68,6 @@ github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHqu
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8=
github.com/gnolang/tx-archive v0.4.0 h1:+1Rgo0U0HjLQLq/xqeGdJwtAzo9xWj09t1oZLvrL3bU=
github.com/gnolang/tx-archive v0.4.0/go.mod h1:seKHGnvxUnDgH/mSsCEdwG0dHY/FrpbUm6Hd0+KMd9w=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -258,8 +256,6 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
-golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/tm2/pkg/overflow/README.md b/tm2/pkg/overflow/README.md
new file mode 100644
index 00000000000..55a9ba4c327
--- /dev/null
+++ b/tm2/pkg/overflow/README.md
@@ -0,0 +1,66 @@
+# overflow
+
+Check for int/int8/int16/int64/int32 integer overflow in Golang arithmetic.
+
+Forked from https://github.com/JohnCGriffin/overflow
+
+### Install
+```
+go get github.com/johncgriffin/overflow
+```
+Note that because Go has no template types, the majority of repetitive code is
+generated by overflow_template.sh. If you have to change an
+algorithm, change it there and regenerate the Go code via:
+```
+go generate
+```
+### Synopsis
+
+```
+package main
+
+import "fmt"
+import "math"
+import "github.com/JohnCGriffin/overflow"
+
+func main() {
+
+ addend := math.MaxInt64 - 5
+
+ for i := 0; i < 10; i++ {
+ sum, ok := overflow.Add(addend, i)
+ fmt.Printf("%v+%v -> (%v,%v)\n",
+ addend, i, sum, ok)
+ }
+
+}
+```
+yields the output
+```
+9223372036854775802+0 -> (9223372036854775802,true)
+9223372036854775802+1 -> (9223372036854775803,true)
+9223372036854775802+2 -> (9223372036854775804,true)
+9223372036854775802+3 -> (9223372036854775805,true)
+9223372036854775802+4 -> (9223372036854775806,true)
+9223372036854775802+5 -> (9223372036854775807,true)
+9223372036854775802+6 -> (0,false)
+9223372036854775802+7 -> (0,false)
+9223372036854775802+8 -> (0,false)
+9223372036854775802+9 -> (0,false)
+```
+
+For int, int64, and int32 types, provide Add, Add32, Add64, Sub, Sub32, Sub64, etc.
+Unsigned types not covered at the moment, but such additions are welcome.
+
+### Stay calm and panic
+
+There's a good case to be made that a panic is an unidiomatic but proper response. Iff you
+believe that there's no valid way to continue your program after math goes wayward, you can
+use the easier Addp, Mulp, Subp, and Divp versions which return the normal result or panic.
+
+
+
+
+
+
+
diff --git a/tm2/pkg/overflow/overflow.go b/tm2/pkg/overflow/overflow.go
new file mode 100644
index 00000000000..b476ea5776e
--- /dev/null
+++ b/tm2/pkg/overflow/overflow.go
@@ -0,0 +1,131 @@
+/*
+Package overflow offers overflow-checked integer arithmetic operations
+for int, int32, and int64. Each of the operations returns a
+result,bool combination. This was prompted by the need to know when
+to flow into higher precision types from the math.big library.
+
+For instance, assuing a 64 bit machine:
+
+10 + 20 -> 30
+int(math.MaxInt64) + 1 -> -9223372036854775808
+
+whereas
+
+overflow.Add(10,20) -> (30, true)
+overflow.Add(math.MaxInt64,1) -> (0, false)
+
+Add, Sub, Mul, Div are for int. Add64, Add32, etc. are specifically sized.
+
+If anybody wishes an unsigned version, submit a pull request for code
+and new tests.
+*/
+package overflow
+
+//go:generate ./overflow_template.sh
+
+import "math"
+
+func _is64Bit() bool {
+ maxU32 := uint(math.MaxUint32)
+ return ((maxU32 << 1) >> 1) == maxU32
+}
+
+/********** PARTIAL TEST COVERAGE FROM HERE DOWN *************
+
+The only way that I could see to do this is a combination of
+my normal 64 bit system and a GopherJS running on Node. My
+understanding is that its ints are 32 bit.
+
+So, FEEL FREE to carefully review the code visually.
+
+*************************************************************/
+
+// Unspecified size, i.e. normal signed int
+
+// Add sums two ints, returning the result and a boolean status.
+func Add(a, b int) (int, bool) {
+ if _is64Bit() {
+ r64, ok := Add64(int64(a), int64(b))
+ return int(r64), ok
+ }
+ r32, ok := Add32(int32(a), int32(b))
+ return int(r32), ok
+}
+
+// Sub returns the difference of two ints and a boolean status.
+func Sub(a, b int) (int, bool) {
+ if _is64Bit() {
+ r64, ok := Sub64(int64(a), int64(b))
+ return int(r64), ok
+ }
+ r32, ok := Sub32(int32(a), int32(b))
+ return int(r32), ok
+}
+
+// Mul returns the product of two ints and a boolean status.
+func Mul(a, b int) (int, bool) {
+ if _is64Bit() {
+ r64, ok := Mul64(int64(a), int64(b))
+ return int(r64), ok
+ }
+ r32, ok := Mul32(int32(a), int32(b))
+ return int(r32), ok
+}
+
+// Div returns the quotient of two ints and a boolean status
+func Div(a, b int) (int, bool) {
+ if _is64Bit() {
+ r64, ok := Div64(int64(a), int64(b))
+ return int(r64), ok
+ }
+ r32, ok := Div32(int32(a), int32(b))
+ return int(r32), ok
+}
+
+// Quotient returns the quotient, remainder and status of two ints
+func Quotient(a, b int) (int, int, bool) {
+ if _is64Bit() {
+ q64, r64, ok := Quotient64(int64(a), int64(b))
+ return int(q64), int(r64), ok
+ }
+ q32, r32, ok := Quotient32(int32(a), int32(b))
+ return int(q32), int(r32), ok
+}
+
+/************* Panic versions for int ****************/
+
+// Addp returns the sum of two ints, panicking on overflow
+func Addp(a, b int) int {
+ r, ok := Add(a, b)
+ if !ok {
+ panic("addition overflow")
+ }
+ return r
+}
+
+// Subp returns the difference of two ints, panicking on overflow.
+func Subp(a, b int) int {
+ r, ok := Sub(a, b)
+ if !ok {
+ panic("subtraction overflow")
+ }
+ return r
+}
+
+// Mulp returns the product of two ints, panicking on overflow.
+func Mulp(a, b int) int {
+ r, ok := Mul(a, b)
+ if !ok {
+ panic("multiplication overflow")
+ }
+ return r
+}
+
+// Divp returns the quotient of two ints, panicking on overflow.
+func Divp(a, b int) int {
+ r, ok := Div(a, b)
+ if !ok {
+ panic("division failure")
+ }
+ return r
+}
diff --git a/tm2/pkg/overflow/overflow_impl.go b/tm2/pkg/overflow/overflow_impl.go
new file mode 100644
index 00000000000..a9a90c43835
--- /dev/null
+++ b/tm2/pkg/overflow/overflow_impl.go
@@ -0,0 +1,360 @@
+package overflow
+
+// This is generated code, created by overflow_template.sh executed
+// by "go generate"
+
+// Add8 performs + operation on two int8 operands
+// returning a result and status
+func Add8(a, b int8) (int8, bool) {
+ c := a + b
+ if (c > a) == (b > 0) {
+ return c, true
+ }
+ return c, false
+}
+
+// Add8p is the unchecked panicing version of Add8
+func Add8p(a, b int8) int8 {
+ r, ok := Add8(a, b)
+ if !ok {
+ panic("addition overflow")
+ }
+ return r
+}
+
+// Sub8 performs - operation on two int8 operands
+// returning a result and status
+func Sub8(a, b int8) (int8, bool) {
+ c := a - b
+ if (c < a) == (b > 0) {
+ return c, true
+ }
+ return c, false
+}
+
+// Sub8p is the unchecked panicing version of Sub8
+func Sub8p(a, b int8) int8 {
+ r, ok := Sub8(a, b)
+ if !ok {
+ panic("subtraction overflow")
+ }
+ return r
+}
+
+// Mul8 performs * operation on two int8 operands
+// returning a result and status
+func Mul8(a, b int8) (int8, bool) {
+ if a == 0 || b == 0 {
+ return 0, true
+ }
+ c := a * b
+ if (c < 0) == ((a < 0) != (b < 0)) {
+ if c/b == a {
+ return c, true
+ }
+ }
+ return c, false
+}
+
+// Mul8p is the unchecked panicing version of Mul8
+func Mul8p(a, b int8) int8 {
+ r, ok := Mul8(a, b)
+ if !ok {
+ panic("multiplication overflow")
+ }
+ return r
+}
+
+// Div8 performs / operation on two int8 operands
+// returning a result and status
+func Div8(a, b int8) (int8, bool) {
+ q, _, ok := Quotient8(a, b)
+ return q, ok
+}
+
+// Div8p is the unchecked panicing version of Div8
+func Div8p(a, b int8) int8 {
+ r, ok := Div8(a, b)
+ if !ok {
+ panic("division failure")
+ }
+ return r
+}
+
+// Quotient8 performs + operation on two int8 operands
+// returning a quotient, a remainder and status
+func Quotient8(a, b int8) (int8, int8, bool) {
+ if b == 0 {
+ return 0, 0, false
+ }
+ c := a / b
+ status := (c < 0) == ((a < 0) != (b < 0))
+ return c, a % b, status
+}
+
+// Add16 performs + operation on two int16 operands
+// returning a result and status
+func Add16(a, b int16) (int16, bool) {
+ c := a + b
+ if (c > a) == (b > 0) {
+ return c, true
+ }
+ return c, false
+}
+
+// Add16p is the unchecked panicing version of Add16
+func Add16p(a, b int16) int16 {
+ r, ok := Add16(a, b)
+ if !ok {
+ panic("addition overflow")
+ }
+ return r
+}
+
+// Sub16 performs - operation on two int16 operands
+// returning a result and status
+func Sub16(a, b int16) (int16, bool) {
+ c := a - b
+ if (c < a) == (b > 0) {
+ return c, true
+ }
+ return c, false
+}
+
+// Sub16p is the unchecked panicing version of Sub16
+func Sub16p(a, b int16) int16 {
+ r, ok := Sub16(a, b)
+ if !ok {
+ panic("subtraction overflow")
+ }
+ return r
+}
+
+// Mul16 performs * operation on two int16 operands
+// returning a result and status
+func Mul16(a, b int16) (int16, bool) {
+ if a == 0 || b == 0 {
+ return 0, true
+ }
+ c := a * b
+ if (c < 0) == ((a < 0) != (b < 0)) {
+ if c/b == a {
+ return c, true
+ }
+ }
+ return c, false
+}
+
+// Mul16p is the unchecked panicing version of Mul16
+func Mul16p(a, b int16) int16 {
+ r, ok := Mul16(a, b)
+ if !ok {
+ panic("multiplication overflow")
+ }
+ return r
+}
+
+// Div16 performs / operation on two int16 operands
+// returning a result and status
+func Div16(a, b int16) (int16, bool) {
+ q, _, ok := Quotient16(a, b)
+ return q, ok
+}
+
+// Div16p is the unchecked panicing version of Div16
+func Div16p(a, b int16) int16 {
+ r, ok := Div16(a, b)
+ if !ok {
+ panic("division failure")
+ }
+ return r
+}
+
+// Quotient16 performs + operation on two int16 operands
+// returning a quotient, a remainder and status
+func Quotient16(a, b int16) (int16, int16, bool) {
+ if b == 0 {
+ return 0, 0, false
+ }
+ c := a / b
+ status := (c < 0) == ((a < 0) != (b < 0))
+ return c, a % b, status
+}
+
+// Add32 performs + operation on two int32 operands
+// returning a result and status
+func Add32(a, b int32) (int32, bool) {
+ c := a + b
+ if (c > a) == (b > 0) {
+ return c, true
+ }
+ return c, false
+}
+
+// Add32p is the unchecked panicing version of Add32
+func Add32p(a, b int32) int32 {
+ r, ok := Add32(a, b)
+ if !ok {
+ panic("addition overflow")
+ }
+ return r
+}
+
+// Sub32 performs - operation on two int32 operands
+// returning a result and status
+func Sub32(a, b int32) (int32, bool) {
+ c := a - b
+ if (c < a) == (b > 0) {
+ return c, true
+ }
+ return c, false
+}
+
+// Sub32p is the unchecked panicing version of Sub32
+func Sub32p(a, b int32) int32 {
+ r, ok := Sub32(a, b)
+ if !ok {
+ panic("subtraction overflow")
+ }
+ return r
+}
+
+// Mul32 performs * operation on two int32 operands
+// returning a result and status
+func Mul32(a, b int32) (int32, bool) {
+ if a == 0 || b == 0 {
+ return 0, true
+ }
+ c := a * b
+ if (c < 0) == ((a < 0) != (b < 0)) {
+ if c/b == a {
+ return c, true
+ }
+ }
+ return c, false
+}
+
+// Mul32p is the unchecked panicing version of Mul32
+func Mul32p(a, b int32) int32 {
+ r, ok := Mul32(a, b)
+ if !ok {
+ panic("multiplication overflow")
+ }
+ return r
+}
+
+// Div32 performs / operation on two int32 operands
+// returning a result and status
+func Div32(a, b int32) (int32, bool) {
+ q, _, ok := Quotient32(a, b)
+ return q, ok
+}
+
+// Div32p is the unchecked panicing version of Div32
+func Div32p(a, b int32) int32 {
+ r, ok := Div32(a, b)
+ if !ok {
+ panic("division failure")
+ }
+ return r
+}
+
+// Quotient32 performs + operation on two int32 operands
+// returning a quotient, a remainder and status
+func Quotient32(a, b int32) (int32, int32, bool) {
+ if b == 0 {
+ return 0, 0, false
+ }
+ c := a / b
+ status := (c < 0) == ((a < 0) != (b < 0))
+ return c, a % b, status
+}
+
+// Add64 performs + operation on two int64 operands
+// returning a result and status
+func Add64(a, b int64) (int64, bool) {
+ c := a + b
+ if (c > a) == (b > 0) {
+ return c, true
+ }
+ return c, false
+}
+
+// Add64p is the unchecked panicing version of Add64
+func Add64p(a, b int64) int64 {
+ r, ok := Add64(a, b)
+ if !ok {
+ panic("addition overflow")
+ }
+ return r
+}
+
+// Sub64 performs - operation on two int64 operands
+// returning a result and status
+func Sub64(a, b int64) (int64, bool) {
+ c := a - b
+ if (c < a) == (b > 0) {
+ return c, true
+ }
+ return c, false
+}
+
+// Sub64p is the unchecked panicing version of Sub64
+func Sub64p(a, b int64) int64 {
+ r, ok := Sub64(a, b)
+ if !ok {
+ panic("subtraction overflow")
+ }
+ return r
+}
+
+// Mul64 performs * operation on two int64 operands
+// returning a result and status
+func Mul64(a, b int64) (int64, bool) {
+ if a == 0 || b == 0 {
+ return 0, true
+ }
+ c := a * b
+ if (c < 0) == ((a < 0) != (b < 0)) {
+ if c/b == a {
+ return c, true
+ }
+ }
+ return c, false
+}
+
+// Mul64p is the unchecked panicing version of Mul64
+func Mul64p(a, b int64) int64 {
+ r, ok := Mul64(a, b)
+ if !ok {
+ panic("multiplication overflow")
+ }
+ return r
+}
+
+// Div64 performs / operation on two int64 operands
+// returning a result and status
+func Div64(a, b int64) (int64, bool) {
+ q, _, ok := Quotient64(a, b)
+ return q, ok
+}
+
+// Div64p is the unchecked panicing version of Div64
+func Div64p(a, b int64) int64 {
+ r, ok := Div64(a, b)
+ if !ok {
+ panic("division failure")
+ }
+ return r
+}
+
+// Quotient64 performs + operation on two int64 operands
+// returning a quotient, a remainder and status
+func Quotient64(a, b int64) (int64, int64, bool) {
+ if b == 0 {
+ return 0, 0, false
+ }
+ c := a / b
+ status := (c < 0) == ((a < 0) != (b < 0))
+ return c, a % b, status
+}
diff --git a/tm2/pkg/overflow/overflow_template.sh b/tm2/pkg/overflow/overflow_template.sh
new file mode 100755
index 00000000000..a2a85f2c581
--- /dev/null
+++ b/tm2/pkg/overflow/overflow_template.sh
@@ -0,0 +1,112 @@
+#!/bin/sh
+
+exec > overflow_impl.go
+
+echo "package overflow
+
+// This is generated code, created by overflow_template.sh executed
+// by \"go generate\"
+
+"
+
+
+for SIZE in 8 16 32 64
+do
+echo "
+
+// Add${SIZE} performs + operation on two int${SIZE} operands
+// returning a result and status
+func Add${SIZE}(a, b int${SIZE}) (int${SIZE}, bool) {
+ c := a + b
+ if (c > a) == (b > 0) {
+ return c, true
+ }
+ return c, false
+}
+
+// Add${SIZE}p is the unchecked panicing version of Add${SIZE}
+func Add${SIZE}p(a, b int${SIZE}) int${SIZE} {
+ r, ok := Add${SIZE}(a, b)
+ if !ok {
+ panic(\"addition overflow\")
+ }
+ return r
+}
+
+
+// Sub${SIZE} performs - operation on two int${SIZE} operands
+// returning a result and status
+func Sub${SIZE}(a, b int${SIZE}) (int${SIZE}, bool) {
+ c := a - b
+ if (c < a) == (b > 0) {
+ return c, true
+ }
+ return c, false
+}
+
+// Sub${SIZE}p is the unchecked panicing version of Sub${SIZE}
+func Sub${SIZE}p(a, b int${SIZE}) int${SIZE} {
+ r, ok := Sub${SIZE}(a, b)
+ if !ok {
+ panic(\"subtraction overflow\")
+ }
+ return r
+}
+
+
+// Mul${SIZE} performs * operation on two int${SIZE} operands
+// returning a result and status
+func Mul${SIZE}(a, b int${SIZE}) (int${SIZE}, bool) {
+ if a == 0 || b == 0 {
+ return 0, true
+ }
+ c := a * b
+ if (c < 0) == ((a < 0) != (b < 0)) {
+ if c/b == a {
+ return c, true
+ }
+ }
+ return c, false
+}
+
+// Mul${SIZE}p is the unchecked panicing version of Mul${SIZE}
+func Mul${SIZE}p(a, b int${SIZE}) int${SIZE} {
+ r, ok := Mul${SIZE}(a, b)
+ if !ok {
+ panic(\"multiplication overflow\")
+ }
+ return r
+}
+
+
+
+// Div${SIZE} performs / operation on two int${SIZE} operands
+// returning a result and status
+func Div${SIZE}(a, b int${SIZE}) (int${SIZE}, bool) {
+ q, _, ok := Quotient${SIZE}(a, b)
+ return q, ok
+}
+
+// Div${SIZE}p is the unchecked panicing version of Div${SIZE}
+func Div${SIZE}p(a, b int${SIZE}) int${SIZE} {
+ r, ok := Div${SIZE}(a, b)
+ if !ok {
+ panic(\"division failure\")
+ }
+ return r
+}
+
+// Quotient${SIZE} performs + operation on two int${SIZE} operands
+// returning a quotient, a remainder and status
+func Quotient${SIZE}(a, b int${SIZE}) (int${SIZE}, int${SIZE}, bool) {
+ if b == 0 {
+ return 0, 0, false
+ }
+ c := a / b
+ status := (c < 0) == ((a < 0) != (b < 0))
+ return c, a % b, status
+}
+"
+done
+
+go run -modfile ../../../misc/devdeps/go.mod mvdan.cc/gofumpt -w overflow_impl.go
diff --git a/tm2/pkg/overflow/overflow_test.go b/tm2/pkg/overflow/overflow_test.go
new file mode 100644
index 00000000000..2b2d345b55d
--- /dev/null
+++ b/tm2/pkg/overflow/overflow_test.go
@@ -0,0 +1,115 @@
+package overflow
+
+import (
+ "fmt"
+ "math"
+ "testing"
+)
+
+// sample all possibilities of 8 bit numbers
+// by checking against 64 bit numbers
+
+func TestAlgorithms(t *testing.T) {
+ errors := 0
+
+ for a64 := int64(math.MinInt8); a64 <= int64(math.MaxInt8); a64++ {
+ for b64 := int64(math.MinInt8); b64 <= int64(math.MaxInt8) && errors < 10; b64++ {
+ a8 := int8(a64)
+ b8 := int8(b64)
+
+ if int64(a8) != a64 || int64(b8) != b64 {
+ t.Fatal("LOGIC FAILURE IN TEST")
+ }
+
+ // ADDITION
+ {
+ r64 := a64 + b64
+
+ // now the verification
+ result, ok := Add8(a8, b8)
+ if ok && int64(result) != r64 {
+ t.Errorf("failed to fail on %v + %v = %v instead of %v\n",
+ a8, b8, result, r64)
+ errors++
+ }
+ if !ok && int64(result) == r64 {
+ t.Fail()
+ errors++
+ }
+ }
+
+ // SUBTRACTION
+ {
+ r64 := a64 - b64
+
+ // now the verification
+ result, ok := Sub8(a8, b8)
+ if ok && int64(result) != r64 {
+ t.Errorf("failed to fail on %v - %v = %v instead of %v\n",
+ a8, b8, result, r64)
+ }
+ if !ok && int64(result) == r64 {
+ t.Fail()
+ errors++
+ }
+ }
+
+ // MULTIPLICATION
+ {
+ r64 := a64 * b64
+
+ // now the verification
+ result, ok := Mul8(a8, b8)
+ if ok && int64(result) != r64 {
+ t.Errorf("failed to fail on %v * %v = %v instead of %v\n",
+ a8, b8, result, r64)
+ errors++
+ }
+ if !ok && int64(result) == r64 {
+ t.Fail()
+ errors++
+ }
+ }
+
+ // DIVISION
+ if b8 != 0 {
+ r64 := a64 / b64
+
+ // now the verification
+ result, _, ok := Quotient8(a8, b8)
+ if ok && int64(result) != r64 {
+ t.Errorf("failed to fail on %v / %v = %v instead of %v\n",
+ a8, b8, result, r64)
+ errors++
+ }
+ if !ok && result != 0 && int64(result) == r64 {
+ t.Fail()
+ errors++
+ }
+ }
+ }
+ }
+}
+
+func TestQuotient(t *testing.T) {
+ q, r, ok := Quotient(100, 3)
+ if r != 1 || q != 33 || !ok {
+ t.Errorf("expected 100/3 => 33, r=1")
+ }
+ if _, _, ok = Quotient(1, 0); ok {
+ t.Error("unexpected lack of failure")
+ }
+}
+
+//func TestAdditionInt(t *testing.T) {
+// fmt.Printf("\nminint8 = %v\n", math.MinInt8)
+// fmt.Printf("maxint8 = %v\n\n", math.MaxInt8)
+// fmt.Printf("maxint32 = %v\n", math.MaxInt32)
+// fmt.Printf("minint32 = %v\n\n", math.MinInt32)
+// fmt.Printf("maxint64 = %v\n", math.MaxInt64)
+// fmt.Printf("minint64 = %v\n\n", math.MinInt64)
+//}
+
+func Test64(t *testing.T) {
+ fmt.Println("64bit:", _is64Bit())
+}
diff --git a/tm2/pkg/std/coin.go b/tm2/pkg/std/coin.go
index 6457b193a6b..fba20a5ba78 100644
--- a/tm2/pkg/std/coin.go
+++ b/tm2/pkg/std/coin.go
@@ -8,7 +8,7 @@ import (
"strings"
"github.com/gnolang/gno/tm2/pkg/errors"
- "github.com/gnolang/overflow"
+ "github.com/gnolang/gno/tm2/pkg/overflow"
)
// -----------------------------------------------------------------------------
diff --git a/tm2/pkg/store/gas/store.go b/tm2/pkg/store/gas/store.go
index db5ea7a79b0..81e898a90d8 100644
--- a/tm2/pkg/store/gas/store.go
+++ b/tm2/pkg/store/gas/store.go
@@ -1,9 +1,9 @@
package gas
import (
+ "github.com/gnolang/gno/tm2/pkg/overflow"
"github.com/gnolang/gno/tm2/pkg/store/types"
"github.com/gnolang/gno/tm2/pkg/store/utils"
- "github.com/gnolang/overflow"
)
var _ types.Store = &Store{}
diff --git a/tm2/pkg/store/types/gas.go b/tm2/pkg/store/types/gas.go
index fd631dd3259..9d1f3d70c28 100644
--- a/tm2/pkg/store/types/gas.go
+++ b/tm2/pkg/store/types/gas.go
@@ -3,7 +3,7 @@ package types
import (
"math"
- "github.com/gnolang/overflow"
+ "github.com/gnolang/gno/tm2/pkg/overflow"
)
// Gas consumption descriptors.
diff --git a/tm2/pkg/store/types/gas_test.go b/tm2/pkg/store/types/gas_test.go
index 410ba0b7e92..115d347bd5e 100644
--- a/tm2/pkg/store/types/gas_test.go
+++ b/tm2/pkg/store/types/gas_test.go
@@ -4,7 +4,7 @@ import (
"math"
"testing"
- "github.com/gnolang/overflow"
+ "github.com/gnolang/gno/tm2/pkg/overflow"
"github.com/stretchr/testify/require"
)
From 5f16b8c703867c3311c9023b60105b010a9e97be Mon Sep 17 00:00:00 2001
From: Morgan
Date: Mon, 9 Dec 2024 10:14:40 +0100
Subject: [PATCH 08/22] fix(gnovm): use strconv.UnquoteChar to parse rune
literals (#3296)
This fixes a bug, as shown in rune3.gno, whereby rune literals which
would not be parsed correctly by `strconv.Unquote` are now parsed
correctly. Previously, the test would print out 65533, for the unicode
invalid code point.
---
gnovm/pkg/gnolang/nodes.go | 1 +
gnovm/pkg/gnolang/op_eval.go | 10 ++++------
gnovm/tests/files/rune3.gno | 10 ++++++++++
3 files changed, 15 insertions(+), 6 deletions(-)
create mode 100644 gnovm/tests/files/rune3.gno
diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go
index 8d3d6d8a2cc..0496d37ed72 100644
--- a/gnovm/pkg/gnolang/nodes.go
+++ b/gnovm/pkg/gnolang/nodes.go
@@ -2153,6 +2153,7 @@ type ValuePather interface {
// Utility
func (x *BasicLitExpr) GetString() string {
+ // Matches string literal parsing in go/constant.MakeFromLiteral.
str, err := strconv.Unquote(x.Value)
if err != nil {
panic("error in parsing string literal: " + err.Error())
diff --git a/gnovm/pkg/gnolang/op_eval.go b/gnovm/pkg/gnolang/op_eval.go
index 1beba1d6e3f..2aa13b21753 100644
--- a/gnovm/pkg/gnolang/op_eval.go
+++ b/gnovm/pkg/gnolang/op_eval.go
@@ -204,16 +204,14 @@ func (m *Machine) doOpEval() {
// and github.com/golang/go/issues/19921
panic("imaginaries are not supported")
case CHAR:
- cstr, err := strconv.Unquote(x.Value)
+ // Matching character literal parsing in go/constant.MakeFromLiteral.
+ val := x.Value
+ rne, _, _, err := strconv.UnquoteChar(val[1:len(val)-1], '\'')
if err != nil {
panic("error in parsing character literal: " + err.Error())
}
- runes := []rune(cstr)
- if len(runes) != 1 {
- panic(fmt.Sprintf("error in parsing character literal: 1 rune expected, but got %v (%s)", len(runes), cstr))
- }
tv := TypedValue{T: UntypedRuneType}
- tv.SetInt32(runes[0])
+ tv.SetInt32(rne)
m.PushValue(tv)
case STRING:
m.PushValue(TypedValue{
diff --git a/gnovm/tests/files/rune3.gno b/gnovm/tests/files/rune3.gno
new file mode 100644
index 00000000000..e848565e3a4
--- /dev/null
+++ b/gnovm/tests/files/rune3.gno
@@ -0,0 +1,10 @@
+package main
+
+const overflow = '\xff'
+
+func main() {
+ println(overflow)
+}
+
+// Output:
+// 255
From 1fba5cfa840d3499d9ce22507a7d2ada19abbdd4 Mon Sep 17 00:00:00 2001
From: Morgan
Date: Mon, 9 Dec 2024 11:13:38 +0100
Subject: [PATCH 09/22] feat(cmd/gno): perform type checking when calling
linter (#1730)
Depends on (in order):
1. #1700
2. #1702
This PR uses the type checker added in #1702 to perform Gno type
checking when calling `gno lint`. Additionally, it adds validation of
gno.mod indirectly (the parsed gno mod is used to determine if a package
is a draft, and if so skip type checking).
Because `gno lint` uses the TestStore, the resulting `MemPackage`s may
contain redefinitions, for overwriting standard libraries like
`AssertOriginCall`. I changed the type checker to filter out the
redefinitions before they reach the Go type checker.
Further improvements, which can be done after this:
- Add shims for gonative special libraries (`fmt`, `os`...)
- This will allow us to fully type check also tests and filetests
- Make the type checking on-chain (#1702) also typecheck tests
- as a consequence of the above.
---
gnovm/cmd/gno/lint.go | 205 ++++++++++++------
gnovm/cmd/gno/lint_test.go | 39 ++--
gnovm/cmd/gno/run_test.go | 19 +-
.../gno/testdata/{gno_fmt => fmt}/empty.txtar | 0
.../{gno_fmt => fmt}/import_cleaning.txtar | 0
.../testdata/{gno_fmt => fmt}/include.txtar | 0
.../{gno_fmt => fmt}/multi_import.txtar | 0
.../{gno_fmt => fmt}/noimport_format.txtar | 0
.../{gno_fmt => fmt}/parse_error.txtar | 0
.../{gno_fmt => fmt}/shadow_import.txtar | 0
.../gno/testdata/gno_lint/file_error_txtar | 20 --
.../{gno_lint => lint}/bad_import.txtar | 8 +-
.../{gno_lint => lint}/file_error.txtar | 5 +-
.../{gno_lint => lint}/no_error.txtar | 9 +-
.../{gno_lint => lint}/no_gnomod.txtar | 6 +-
.../{gno_lint => lint}/not_declared.txtar | 12 +-
.../{gno_test => test}/dir_not_exist.txtar | 0
.../{gno_test => test}/empty_dir.txtar | 0
.../{gno_test => test}/empty_gno1.txtar | 0
.../{gno_test => test}/empty_gno2.txtar | 0
.../{gno_test => test}/empty_gno3.txtar | 0
.../{gno_test => test}/error_correct.txtar | 0
.../{gno_test => test}/error_incorrect.txtar | 0
.../{gno_test => test}/error_sync.txtar | 0
.../{gno_test => test}/failing_filetest.txtar | 0
.../{gno_test => test}/failing_test.txtar | 0
.../{gno_test => test}/filetest_events.txtar | 0
.../flag_print-runtime-metrics.txtar | 0
.../{gno_test => test}/flag_run.txtar | 0
.../{gno_test => test}/flag_timeout.txtar | 0
.../{gno_test => test}/fmt_write_import.txtar | 0
.../testdata/{gno_test => test}/minim1.txtar | 0
.../testdata/{gno_test => test}/minim2.txtar | 0
.../testdata/{gno_test => test}/minim3.txtar | 0
.../{gno_test => test}/multitest_events.txtar | 0
.../testdata/{gno_test => test}/no_args.txtar | 0
.../{gno_test => test}/output_correct.txtar | 0
.../{gno_test => test}/output_incorrect.txtar | 0
.../{gno_test => test}/output_sync.txtar | 0
.../testdata/{gno_test => test}/panic.txtar | 0
.../pkg_underscore_test.txtar | 0
.../realm_boundmethod.txtar | 0
.../{gno_test => test}/realm_correct.txtar | 0
.../{gno_test => test}/realm_incorrect.txtar | 0
.../{gno_test => test}/realm_sync.txtar | 0
.../testdata/{gno_test => test}/recover.txtar | 0
.../testdata/{gno_test => test}/skip.txtar | 0
.../{gno_test => test}/unknown_package.txtar | 0
.../{gno_test => test}/valid_filetest.txtar | 0
.../{gno_test => test}/valid_test.txtar | 0
.../gobuild_flag_build_error.txtar | 0
.../gobuild_flag_parse_error.txtar | 0
.../invalid_import.txtar | 0
.../no_args.txtar | 0
.../parse_error.txtar | 0
.../valid_empty_dir.txtar | 0
.../valid_gobuild_file.txtar | 0
.../valid_gobuild_flag.txtar | 0
.../valid_output_flag.txtar | 0
.../valid_output_gobuild.txtar | 0
.../valid_transpile_file.txtar | 0
.../valid_transpile_package.txtar | 0
.../valid_transpile_tree.txtar | 0
gnovm/cmd/gno/testdata_test.go | 1 -
gnovm/cmd/gno/transpile_test.go | 20 --
gnovm/pkg/gnolang/go2gno.go | 59 ++++-
.../integ/typecheck_missing_return/gno.mod | 1 +
.../integ/typecheck_missing_return/main.gno | 5 +
68 files changed, 263 insertions(+), 146 deletions(-)
rename gnovm/cmd/gno/testdata/{gno_fmt => fmt}/empty.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_fmt => fmt}/import_cleaning.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_fmt => fmt}/include.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_fmt => fmt}/multi_import.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_fmt => fmt}/noimport_format.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_fmt => fmt}/parse_error.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_fmt => fmt}/shadow_import.txtar (100%)
delete mode 100644 gnovm/cmd/gno/testdata/gno_lint/file_error_txtar
rename gnovm/cmd/gno/testdata/{gno_lint => lint}/bad_import.txtar (54%)
rename gnovm/cmd/gno/testdata/{gno_lint => lint}/file_error.txtar (88%)
rename gnovm/cmd/gno/testdata/{gno_lint => lint}/no_error.txtar (68%)
rename gnovm/cmd/gno/testdata/{gno_lint => lint}/no_gnomod.txtar (60%)
rename gnovm/cmd/gno/testdata/{gno_lint => lint}/not_declared.txtar (55%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/dir_not_exist.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/empty_dir.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/empty_gno1.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/empty_gno2.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/empty_gno3.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/error_correct.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/error_incorrect.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/error_sync.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/failing_filetest.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/failing_test.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/filetest_events.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/flag_print-runtime-metrics.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/flag_run.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/flag_timeout.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/fmt_write_import.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/minim1.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/minim2.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/minim3.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/multitest_events.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/no_args.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/output_correct.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/output_incorrect.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/output_sync.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/panic.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/pkg_underscore_test.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/realm_boundmethod.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/realm_correct.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/realm_incorrect.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/realm_sync.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/recover.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/skip.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/unknown_package.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/valid_filetest.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_test => test}/valid_test.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_transpile => transpile}/gobuild_flag_build_error.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_transpile => transpile}/gobuild_flag_parse_error.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_transpile => transpile}/invalid_import.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_transpile => transpile}/no_args.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_transpile => transpile}/parse_error.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_transpile => transpile}/valid_empty_dir.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_transpile => transpile}/valid_gobuild_file.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_transpile => transpile}/valid_gobuild_flag.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_transpile => transpile}/valid_output_flag.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_transpile => transpile}/valid_output_gobuild.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_transpile => transpile}/valid_transpile_file.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_transpile => transpile}/valid_transpile_package.txtar (100%)
rename gnovm/cmd/gno/testdata/{gno_transpile => transpile}/valid_transpile_tree.txtar (100%)
create mode 100644 gnovm/tests/integ/typecheck_missing_return/gno.mod
create mode 100644 gnovm/tests/integ/typecheck_missing_return/main.gno
diff --git a/gnovm/cmd/gno/lint.go b/gnovm/cmd/gno/lint.go
index 6d5399ca932..a3e7f5310e1 100644
--- a/gnovm/cmd/gno/lint.go
+++ b/gnovm/cmd/gno/lint.go
@@ -6,17 +6,19 @@ import (
"flag"
"fmt"
"go/scanner"
+ "go/types"
"io"
"os"
"path/filepath"
"regexp"
"strings"
+ "github.com/gnolang/gno/gnovm"
"github.com/gnolang/gno/gnovm/pkg/gnoenv"
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
+ "github.com/gnolang/gno/gnovm/pkg/gnomod"
"github.com/gnolang/gno/gnovm/pkg/test"
"github.com/gnolang/gno/tm2/pkg/commands"
- osm "github.com/gnolang/gno/tm2/pkg/os"
"go.uber.org/multierr"
)
@@ -50,6 +52,31 @@ func (c *lintCfg) RegisterFlags(fs *flag.FlagSet) {
fs.StringVar(&c.rootDir, "root-dir", rootdir, "clone location of github.com/gnolang/gno (gno tries to guess it)")
}
+type lintCode int
+
+const (
+ lintUnknown lintCode = iota
+ lintGnoMod
+ lintGnoError
+ lintParserError
+ lintTypeCheckError
+
+ // TODO: add new linter codes here.
+)
+
+type lintIssue struct {
+ Code lintCode
+ Msg string
+ Confidence float64 // 1 is 100%
+ Location string // file:line, or equivalent
+ // TODO: consider writing fix suggestions
+}
+
+func (i lintIssue) String() string {
+ // TODO: consider crafting a doc URL based on Code.
+ return fmt.Sprintf("%s: %s (code=%d)", i.Location, i.Msg, i.Code)
+}
+
func execLint(cfg *lintCfg, args []string, io commands.IO) error {
if len(args) < 1 {
return flag.ErrHelp
@@ -72,37 +99,55 @@ func execLint(cfg *lintCfg, args []string, io commands.IO) error {
for _, pkgPath := range pkgPaths {
if verbose {
- fmt.Fprintf(io.Err(), "Linting %q...\n", pkgPath)
+ io.ErrPrintln(pkgPath)
+ }
+
+ info, err := os.Stat(pkgPath)
+ if err == nil && !info.IsDir() {
+ pkgPath = filepath.Dir(pkgPath)
}
// Check if 'gno.mod' exists
- gnoModPath := filepath.Join(pkgPath, "gno.mod")
- if !osm.FileExists(gnoModPath) {
- hasError = true
+ gmFile, err := gnomod.ParseAt(pkgPath)
+ if err != nil {
issue := lintIssue{
- Code: lintNoGnoMod,
+ Code: lintGnoMod,
Confidence: 1,
Location: pkgPath,
- Msg: "missing 'gno.mod' file",
+ Msg: err.Error(),
}
- fmt.Fprint(io.Err(), issue.String()+"\n")
+ io.ErrPrintln(issue)
+ hasError = true
}
- // Handle runtime errors
- hasError = catchRuntimeError(pkgPath, io.Err(), func() {
- stdout, stdin, stderr := io.Out(), io.In(), io.Err()
- _, testStore := test.Store(
- rootDir, false,
- stdin, stdout, stderr,
- )
-
- targetPath := pkgPath
- info, err := os.Stat(pkgPath)
- if err == nil && !info.IsDir() {
- targetPath = filepath.Dir(pkgPath)
+ stdout, stdin, stderr := io.Out(), io.In(), io.Err()
+ _, testStore := test.Store(
+ rootDir, false,
+ stdin, stdout, stderr,
+ )
+
+ memPkg, err := gno.ReadMemPackage(pkgPath, pkgPath)
+ if err != nil {
+ io.ErrPrintln(issueFromError(pkgPath, err).String())
+ hasError = true
+ continue
+ }
+
+ // Run type checking
+ if gmFile == nil || !gmFile.Draft {
+ foundErr, err := lintTypeCheck(io, memPkg, testStore)
+ if err != nil {
+ io.ErrPrintln(err)
+ hasError = true
+ } else if foundErr {
+ hasError = true
}
+ } else if verbose {
+ io.ErrPrintfln("%s: module is draft, skipping type check", pkgPath)
+ }
- memPkg := gno.MustReadMemPackage(targetPath, targetPath)
+ // Handle runtime errors
+ hasRuntimeErr := catchRuntimeError(pkgPath, io.Err(), func() {
tm := test.Machine(testStore, stdout, memPkg.Path)
defer tm.Release()
@@ -110,28 +155,13 @@ func execLint(cfg *lintCfg, args []string, io commands.IO) error {
tm.RunMemPackage(memPkg, true)
// Check test files
- testfiles := &gno.FileSet{}
- for _, mfile := range memPkg.Files {
- if !strings.HasSuffix(mfile.Name, ".gno") {
- continue // Skip non-GNO files
- }
+ testFiles := lintTestFiles(memPkg)
- n, _ := gno.ParseFile(mfile.Name, mfile.Body)
- if n == nil {
- continue // Skip empty files
- }
-
- // XXX: package ending with `_test` is not supported yet
- if strings.HasSuffix(mfile.Name, "_test.gno") && !strings.HasSuffix(string(n.PkgName), "_test") {
- // Keep only test files
- testfiles.AddFiles(n)
- }
- }
-
- tm.RunFiles(testfiles.Files...)
- }) || hasError
-
- // TODO: Add more checkers
+ tm.RunFiles(testFiles.Files...)
+ })
+ if hasRuntimeErr {
+ hasError = true
+ }
}
if hasError {
@@ -141,6 +171,66 @@ func execLint(cfg *lintCfg, args []string, io commands.IO) error {
return nil
}
+func lintTypeCheck(io commands.IO, memPkg *gnovm.MemPackage, testStore gno.Store) (errorsFound bool, err error) {
+ tcErr := gno.TypeCheckMemPackageTest(memPkg, testStore)
+ if tcErr == nil {
+ return false, nil
+ }
+
+ errs := multierr.Errors(tcErr)
+ for _, err := range errs {
+ switch err := err.(type) {
+ case types.Error:
+ io.ErrPrintln(lintIssue{
+ Code: lintTypeCheckError,
+ Msg: err.Msg,
+ Confidence: 1,
+ Location: err.Fset.Position(err.Pos).String(),
+ })
+ case scanner.ErrorList:
+ for _, scErr := range err {
+ io.ErrPrintln(lintIssue{
+ Code: lintParserError,
+ Msg: scErr.Msg,
+ Confidence: 1,
+ Location: scErr.Pos.String(),
+ })
+ }
+ case scanner.Error:
+ io.ErrPrintln(lintIssue{
+ Code: lintParserError,
+ Msg: err.Msg,
+ Confidence: 1,
+ Location: err.Pos.String(),
+ })
+ default:
+ return false, fmt.Errorf("unexpected error type: %T", err)
+ }
+ }
+ return true, nil
+}
+
+func lintTestFiles(memPkg *gnovm.MemPackage) *gno.FileSet {
+ testfiles := &gno.FileSet{}
+ for _, mfile := range memPkg.Files {
+ if !strings.HasSuffix(mfile.Name, ".gno") {
+ continue // Skip non-GNO files
+ }
+
+ n, _ := gno.ParseFile(mfile.Name, mfile.Body)
+ if n == nil {
+ continue // Skip empty files
+ }
+
+ // XXX: package ending with `_test` is not supported yet
+ if strings.HasSuffix(mfile.Name, "_test.gno") && !strings.HasSuffix(string(n.PkgName), "_test") {
+ // Keep only test files
+ testfiles.AddFiles(n)
+ }
+ }
+ return testfiles
+}
+
func guessSourcePath(pkg, source string) string {
if info, err := os.Stat(pkg); !os.IsNotExist(err) && !info.IsDir() {
pkg = filepath.Dir(pkg)
@@ -174,21 +264,21 @@ func catchRuntimeError(pkgPath string, stderr io.WriteCloser, action func()) (ha
switch verr := r.(type) {
case *gno.PreprocessError:
err := verr.Unwrap()
- fmt.Fprint(stderr, issueFromError(pkgPath, err).String()+"\n")
+ fmt.Fprintln(stderr, issueFromError(pkgPath, err).String())
case error:
errors := multierr.Errors(verr)
for _, err := range errors {
errList, ok := err.(scanner.ErrorList)
if ok {
for _, errorInList := range errList {
- fmt.Fprint(stderr, issueFromError(pkgPath, errorInList).String()+"\n")
+ fmt.Fprintln(stderr, issueFromError(pkgPath, errorInList).String())
}
} else {
- fmt.Fprint(stderr, issueFromError(pkgPath, err).String()+"\n")
+ fmt.Fprintln(stderr, issueFromError(pkgPath, err).String())
}
}
case string:
- fmt.Fprint(stderr, issueFromError(pkgPath, errors.New(verr)).String()+"\n")
+ fmt.Fprintln(stderr, issueFromError(pkgPath, errors.New(verr)).String())
default:
panic(r)
}
@@ -198,29 +288,6 @@ func catchRuntimeError(pkgPath string, stderr io.WriteCloser, action func()) (ha
return
}
-type lintCode int
-
-const (
- lintUnknown lintCode = 0
- lintNoGnoMod lintCode = iota
- lintGnoError
-
- // TODO: add new linter codes here.
-)
-
-type lintIssue struct {
- Code lintCode
- Msg string
- Confidence float64 // 1 is 100%
- Location string // file:line, or equivalent
- // TODO: consider writing fix suggestions
-}
-
-func (i lintIssue) String() string {
- // TODO: consider crafting a doc URL based on Code.
- return fmt.Sprintf("%s: %s (code=%d).", i.Location, i.Msg, i.Code)
-}
-
func issueFromError(pkgPath string, err error) lintIssue {
var issue lintIssue
issue.Confidence = 1
diff --git a/gnovm/cmd/gno/lint_test.go b/gnovm/cmd/gno/lint_test.go
index 031c252bc79..4589fc55f92 100644
--- a/gnovm/cmd/gno/lint_test.go
+++ b/gnovm/cmd/gno/lint_test.go
@@ -1,6 +1,9 @@
package main
-import "testing"
+import (
+ "strings"
+ "testing"
+)
func TestLintApp(t *testing.T) {
tc := []testMainCase{
@@ -9,7 +12,7 @@ func TestLintApp(t *testing.T) {
errShouldBe: "flag: help requested",
}, {
args: []string{"lint", "../../tests/integ/run_main/"},
- stderrShouldContain: "./../../tests/integ/run_main: missing 'gno.mod' file (code=1).",
+ stderrShouldContain: "./../../tests/integ/run_main: gno.mod file not found in current or any parent directory (code=1)",
errShouldBe: "exit code: 1",
}, {
args: []string{"lint", "../../tests/integ/undefined_variable_test/undefined_variables_test.gno"},
@@ -17,33 +20,43 @@ func TestLintApp(t *testing.T) {
errShouldBe: "exit code: 1",
}, {
args: []string{"lint", "../../tests/integ/package_not_declared/main.gno"},
- stderrShouldContain: "main.gno:4:2: name fmt not declared (code=2).",
+ stderrShouldContain: "main.gno:4:2: name fmt not declared (code=2)",
errShouldBe: "exit code: 1",
}, {
args: []string{"lint", "../../tests/integ/several-lint-errors/main.gno"},
- stderrShouldContain: "../../tests/integ/several-lint-errors/main.gno:5:5: expected ';', found example (code=2).\n../../tests/integ/several-lint-errors/main.gno:6",
+ stderrShouldContain: "../../tests/integ/several-lint-errors/main.gno:5:5: expected ';', found example (code=2)\n../../tests/integ/several-lint-errors/main.gno:6",
errShouldBe: "exit code: 1",
}, {
- args: []string{"lint", "../../tests/integ/several-files-multiple-errors/main.gno"},
- stderrShouldContain: "../../tests/integ/several-files-multiple-errors/file2.gno:3:5: expected 'IDENT', found '{' (code=2).\n../../tests/integ/several-files-multiple-errors/file2.gno:5:1: expected type, found '}' (code=2).\n../../tests/integ/several-files-multiple-errors/main.gno:5:5: expected ';', found example (code=2).\n../../tests/integ/several-files-multiple-errors/main.gno:6:2: expected '}', found 'EOF' (code=2).\n",
- errShouldBe: "exit code: 1",
- }, {
- args: []string{"lint", "../../tests/integ/run_main/"},
- stderrShouldContain: "./../../tests/integ/run_main: missing 'gno.mod' file (code=1).",
- errShouldBe: "exit code: 1",
+ args: []string{"lint", "../../tests/integ/several-files-multiple-errors/main.gno"},
+ stderrShouldContain: func() string {
+ lines := []string{
+ "../../tests/integ/several-files-multiple-errors/file2.gno:3:5: expected 'IDENT', found '{' (code=2)",
+ "../../tests/integ/several-files-multiple-errors/file2.gno:5:1: expected type, found '}' (code=2)",
+ "../../tests/integ/several-files-multiple-errors/main.gno:5:5: expected ';', found example (code=2)",
+ "../../tests/integ/several-files-multiple-errors/main.gno:6:2: expected '}', found 'EOF' (code=2)",
+ }
+ return strings.Join(lines, "\n") + "\n"
+ }(),
+ errShouldBe: "exit code: 1",
}, {
args: []string{"lint", "../../tests/integ/minimalist_gnomod/"},
// TODO: raise an error because there is a gno.mod, but no .gno files
}, {
args: []string{"lint", "../../tests/integ/invalid_module_name/"},
// TODO: raise an error because gno.mod is invalid
+ }, {
+ args: []string{"lint", "../../tests/integ/invalid_gno_file/"},
+ stderrShouldContain: "../../tests/integ/invalid_gno_file/invalid.gno:1:1: expected 'package', found packag (code=2)",
+ errShouldBe: "exit code: 1",
+ }, {
+ args: []string{"lint", "../../tests/integ/typecheck_missing_return/"},
+ stderrShouldContain: "../../tests/integ/typecheck_missing_return/main.gno:5:1: missing return (code=4)",
+ errShouldBe: "exit code: 1",
},
// TODO: 'gno mod' is valid?
- // TODO: is gno source valid?
// TODO: are dependencies valid?
// TODO: is gno source using unsafe/discouraged features?
- // TODO: consider making `gno transpile; go lint *gen.go`
// TODO: check for imports of native libs from non _test.gno files
}
testMainCaseRun(t, tc)
diff --git a/gnovm/cmd/gno/run_test.go b/gnovm/cmd/gno/run_test.go
index 74f99f7490c..aa7780c149e 100644
--- a/gnovm/cmd/gno/run_test.go
+++ b/gnovm/cmd/gno/run_test.go
@@ -1,6 +1,9 @@
package main
-import "testing"
+import (
+ "strings"
+ "testing"
+)
func TestRunApp(t *testing.T) {
tc := []testMainCase{
@@ -84,9 +87,17 @@ func TestRunApp(t *testing.T) {
stdoutShouldContain: "Context worked",
},
{
- args: []string{"run", "../../tests/integ/several-files-multiple-errors/"},
- stderrShouldContain: "../../tests/integ/several-files-multiple-errors/file2.gno:3:5: expected 'IDENT', found '{' (code=2).\n../../tests/integ/several-files-multiple-errors/file2.gno:5:1: expected type, found '}' (code=2).\n../../tests/integ/several-files-multiple-errors/main.gno:5:5: expected ';', found example (code=2).\n../../tests/integ/several-files-multiple-errors/main.gno:6:2: expected '}', found 'EOF' (code=2).\n",
- errShouldBe: "exit code: 1",
+ args: []string{"run", "../../tests/integ/several-files-multiple-errors/"},
+ stderrShouldContain: func() string {
+ lines := []string{
+ "../../tests/integ/several-files-multiple-errors/file2.gno:3:5: expected 'IDENT', found '{' (code=2)",
+ "../../tests/integ/several-files-multiple-errors/file2.gno:5:1: expected type, found '}' (code=2)",
+ "../../tests/integ/several-files-multiple-errors/main.gno:5:5: expected ';', found example (code=2)",
+ "../../tests/integ/several-files-multiple-errors/main.gno:6:2: expected '}', found 'EOF' (code=2)",
+ }
+ return strings.Join(lines, "\n") + "\n"
+ }(),
+ errShouldBe: "exit code: 1",
},
// TODO: a test file
// TODO: args
diff --git a/gnovm/cmd/gno/testdata/gno_fmt/empty.txtar b/gnovm/cmd/gno/testdata/fmt/empty.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_fmt/empty.txtar
rename to gnovm/cmd/gno/testdata/fmt/empty.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_fmt/import_cleaning.txtar b/gnovm/cmd/gno/testdata/fmt/import_cleaning.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_fmt/import_cleaning.txtar
rename to gnovm/cmd/gno/testdata/fmt/import_cleaning.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_fmt/include.txtar b/gnovm/cmd/gno/testdata/fmt/include.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_fmt/include.txtar
rename to gnovm/cmd/gno/testdata/fmt/include.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_fmt/multi_import.txtar b/gnovm/cmd/gno/testdata/fmt/multi_import.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_fmt/multi_import.txtar
rename to gnovm/cmd/gno/testdata/fmt/multi_import.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_fmt/noimport_format.txtar b/gnovm/cmd/gno/testdata/fmt/noimport_format.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_fmt/noimport_format.txtar
rename to gnovm/cmd/gno/testdata/fmt/noimport_format.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_fmt/parse_error.txtar b/gnovm/cmd/gno/testdata/fmt/parse_error.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_fmt/parse_error.txtar
rename to gnovm/cmd/gno/testdata/fmt/parse_error.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_fmt/shadow_import.txtar b/gnovm/cmd/gno/testdata/fmt/shadow_import.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_fmt/shadow_import.txtar
rename to gnovm/cmd/gno/testdata/fmt/shadow_import.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_lint/file_error_txtar b/gnovm/cmd/gno/testdata/gno_lint/file_error_txtar
deleted file mode 100644
index 9482eeb1f4f..00000000000
--- a/gnovm/cmd/gno/testdata/gno_lint/file_error_txtar
+++ /dev/null
@@ -1,20 +0,0 @@
-# gno lint: test file error
-
-! gno lint ./i_have_error_test.gno
-
-cmp stdout stdout.golden
-cmp stderr stderr.golden
-
--- i_have_error_test.gno --
-package main
-
-import "fmt"
-
-func TestIHaveSomeError() {
- i := undefined_variable
- fmt.Println("Hello", 42)
-}
-
--- stdout.golden --
--- stderr.golden --
-i_have_error_test.gno:6: name undefined_variable not declared (code=2).
diff --git a/gnovm/cmd/gno/testdata/gno_lint/bad_import.txtar b/gnovm/cmd/gno/testdata/lint/bad_import.txtar
similarity index 54%
rename from gnovm/cmd/gno/testdata/gno_lint/bad_import.txtar
rename to gnovm/cmd/gno/testdata/lint/bad_import.txtar
index 52141dff09b..b5edbdd0223 100644
--- a/gnovm/cmd/gno/testdata/gno_lint/bad_import.txtar
+++ b/gnovm/cmd/gno/testdata/lint/bad_import.txtar
@@ -11,9 +11,13 @@ package main
import "python"
func main() {
- fmt.Println("Hello", 42)
+ println("Hello", 42)
}
+-- gno.mod --
+module gno.land/p/test
+
-- stdout.golden --
-- stderr.golden --
-bad_file.gno:3:8: unknown import path python (code=2).
+bad_file.gno:3:8: could not import python (import not found: python) (code=4)
+bad_file.gno:3:8: unknown import path python (code=2)
diff --git a/gnovm/cmd/gno/testdata/gno_lint/file_error.txtar b/gnovm/cmd/gno/testdata/lint/file_error.txtar
similarity index 88%
rename from gnovm/cmd/gno/testdata/gno_lint/file_error.txtar
rename to gnovm/cmd/gno/testdata/lint/file_error.txtar
index 5aa3a3282d5..4fa50c6da81 100644
--- a/gnovm/cmd/gno/testdata/gno_lint/file_error.txtar
+++ b/gnovm/cmd/gno/testdata/lint/file_error.txtar
@@ -15,6 +15,9 @@ func TestIHaveSomeError() {
fmt.Println("Hello", 42)
}
+-- gno.mod --
+module gno.land/p/test
+
-- stdout.golden --
-- stderr.golden --
-i_have_error_test.gno:6:7: name undefined_variable not declared (code=2).
+i_have_error_test.gno:6:7: name undefined_variable not declared (code=2)
diff --git a/gnovm/cmd/gno/testdata/gno_lint/no_error.txtar b/gnovm/cmd/gno/testdata/lint/no_error.txtar
similarity index 68%
rename from gnovm/cmd/gno/testdata/gno_lint/no_error.txtar
rename to gnovm/cmd/gno/testdata/lint/no_error.txtar
index 95356b1ba2b..5dd3b164952 100644
--- a/gnovm/cmd/gno/testdata/gno_lint/no_error.txtar
+++ b/gnovm/cmd/gno/testdata/lint/no_error.txtar
@@ -1,6 +1,6 @@
# testing simple gno lint command with any error
-gno lint ./good_file.gno
+gno lint ./good_file.gno
cmp stdout stdout.golden
cmp stdout stderr.golden
@@ -8,11 +8,12 @@ cmp stdout stderr.golden
-- good_file.gno --
package main
-import "fmt"
-
func main() {
- fmt.Println("Hello", 42)
+ println("Hello", 42)
}
+-- gno.mod --
+module gno.land/p/demo/test
+
-- stdout.golden --
-- stderr.golden --
diff --git a/gnovm/cmd/gno/testdata/gno_lint/no_gnomod.txtar b/gnovm/cmd/gno/testdata/lint/no_gnomod.txtar
similarity index 60%
rename from gnovm/cmd/gno/testdata/gno_lint/no_gnomod.txtar
rename to gnovm/cmd/gno/testdata/lint/no_gnomod.txtar
index 52daa6f0e9b..b5a046a7095 100644
--- a/gnovm/cmd/gno/testdata/gno_lint/no_gnomod.txtar
+++ b/gnovm/cmd/gno/testdata/lint/no_gnomod.txtar
@@ -8,12 +8,10 @@ cmp stderr stderr.golden
-- good_file.gno --
package main
-import "fmt"
-
func main() {
- fmt.Println("Hello", 42)
+ println("Hello", 42)
}
-- stdout.golden --
-- stderr.golden --
-./.: missing 'gno.mod' file (code=1).
+./.: parsing gno.mod at ./.: gno.mod file not found in current or any parent directory (code=1)
diff --git a/gnovm/cmd/gno/testdata/gno_lint/not_declared.txtar b/gnovm/cmd/gno/testdata/lint/not_declared.txtar
similarity index 55%
rename from gnovm/cmd/gno/testdata/gno_lint/not_declared.txtar
rename to gnovm/cmd/gno/testdata/lint/not_declared.txtar
index b63c5c447e1..ac56b27e0df 100644
--- a/gnovm/cmd/gno/testdata/gno_lint/not_declared.txtar
+++ b/gnovm/cmd/gno/testdata/lint/not_declared.txtar
@@ -8,13 +8,15 @@ cmp stderr stderr.golden
-- bad_file.gno --
package main
-import "fmt"
-
func main() {
- hello.Foo()
- fmt.Println("Hello", 42)
+ hello.Foo()
+ println("Hello", 42)
}
+-- gno.mod --
+module gno.land/p/demo/hello
+
-- stdout.golden --
-- stderr.golden --
-bad_file.gno:6:3: name hello not declared (code=2).
+bad_file.gno:4:2: undefined: hello (code=4)
+bad_file.gno:4:2: name hello not declared (code=2)
diff --git a/gnovm/cmd/gno/testdata/gno_test/dir_not_exist.txtar b/gnovm/cmd/gno/testdata/test/dir_not_exist.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/dir_not_exist.txtar
rename to gnovm/cmd/gno/testdata/test/dir_not_exist.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/empty_dir.txtar b/gnovm/cmd/gno/testdata/test/empty_dir.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/empty_dir.txtar
rename to gnovm/cmd/gno/testdata/test/empty_dir.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/empty_gno1.txtar b/gnovm/cmd/gno/testdata/test/empty_gno1.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/empty_gno1.txtar
rename to gnovm/cmd/gno/testdata/test/empty_gno1.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/empty_gno2.txtar b/gnovm/cmd/gno/testdata/test/empty_gno2.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/empty_gno2.txtar
rename to gnovm/cmd/gno/testdata/test/empty_gno2.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/empty_gno3.txtar b/gnovm/cmd/gno/testdata/test/empty_gno3.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/empty_gno3.txtar
rename to gnovm/cmd/gno/testdata/test/empty_gno3.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/error_correct.txtar b/gnovm/cmd/gno/testdata/test/error_correct.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/error_correct.txtar
rename to gnovm/cmd/gno/testdata/test/error_correct.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/error_incorrect.txtar b/gnovm/cmd/gno/testdata/test/error_incorrect.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/error_incorrect.txtar
rename to gnovm/cmd/gno/testdata/test/error_incorrect.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/error_sync.txtar b/gnovm/cmd/gno/testdata/test/error_sync.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/error_sync.txtar
rename to gnovm/cmd/gno/testdata/test/error_sync.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/failing_filetest.txtar b/gnovm/cmd/gno/testdata/test/failing_filetest.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/failing_filetest.txtar
rename to gnovm/cmd/gno/testdata/test/failing_filetest.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/failing_test.txtar b/gnovm/cmd/gno/testdata/test/failing_test.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/failing_test.txtar
rename to gnovm/cmd/gno/testdata/test/failing_test.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/filetest_events.txtar b/gnovm/cmd/gno/testdata/test/filetest_events.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/filetest_events.txtar
rename to gnovm/cmd/gno/testdata/test/filetest_events.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/flag_print-runtime-metrics.txtar b/gnovm/cmd/gno/testdata/test/flag_print-runtime-metrics.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/flag_print-runtime-metrics.txtar
rename to gnovm/cmd/gno/testdata/test/flag_print-runtime-metrics.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/flag_run.txtar b/gnovm/cmd/gno/testdata/test/flag_run.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/flag_run.txtar
rename to gnovm/cmd/gno/testdata/test/flag_run.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/flag_timeout.txtar b/gnovm/cmd/gno/testdata/test/flag_timeout.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/flag_timeout.txtar
rename to gnovm/cmd/gno/testdata/test/flag_timeout.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/fmt_write_import.txtar b/gnovm/cmd/gno/testdata/test/fmt_write_import.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/fmt_write_import.txtar
rename to gnovm/cmd/gno/testdata/test/fmt_write_import.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/minim1.txtar b/gnovm/cmd/gno/testdata/test/minim1.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/minim1.txtar
rename to gnovm/cmd/gno/testdata/test/minim1.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/minim2.txtar b/gnovm/cmd/gno/testdata/test/minim2.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/minim2.txtar
rename to gnovm/cmd/gno/testdata/test/minim2.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/minim3.txtar b/gnovm/cmd/gno/testdata/test/minim3.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/minim3.txtar
rename to gnovm/cmd/gno/testdata/test/minim3.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/multitest_events.txtar b/gnovm/cmd/gno/testdata/test/multitest_events.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/multitest_events.txtar
rename to gnovm/cmd/gno/testdata/test/multitest_events.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/no_args.txtar b/gnovm/cmd/gno/testdata/test/no_args.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/no_args.txtar
rename to gnovm/cmd/gno/testdata/test/no_args.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/output_correct.txtar b/gnovm/cmd/gno/testdata/test/output_correct.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/output_correct.txtar
rename to gnovm/cmd/gno/testdata/test/output_correct.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/output_incorrect.txtar b/gnovm/cmd/gno/testdata/test/output_incorrect.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/output_incorrect.txtar
rename to gnovm/cmd/gno/testdata/test/output_incorrect.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/output_sync.txtar b/gnovm/cmd/gno/testdata/test/output_sync.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/output_sync.txtar
rename to gnovm/cmd/gno/testdata/test/output_sync.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/panic.txtar b/gnovm/cmd/gno/testdata/test/panic.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/panic.txtar
rename to gnovm/cmd/gno/testdata/test/panic.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/pkg_underscore_test.txtar b/gnovm/cmd/gno/testdata/test/pkg_underscore_test.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/pkg_underscore_test.txtar
rename to gnovm/cmd/gno/testdata/test/pkg_underscore_test.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/realm_boundmethod.txtar b/gnovm/cmd/gno/testdata/test/realm_boundmethod.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/realm_boundmethod.txtar
rename to gnovm/cmd/gno/testdata/test/realm_boundmethod.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/realm_correct.txtar b/gnovm/cmd/gno/testdata/test/realm_correct.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/realm_correct.txtar
rename to gnovm/cmd/gno/testdata/test/realm_correct.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/realm_incorrect.txtar b/gnovm/cmd/gno/testdata/test/realm_incorrect.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/realm_incorrect.txtar
rename to gnovm/cmd/gno/testdata/test/realm_incorrect.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/realm_sync.txtar b/gnovm/cmd/gno/testdata/test/realm_sync.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/realm_sync.txtar
rename to gnovm/cmd/gno/testdata/test/realm_sync.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/recover.txtar b/gnovm/cmd/gno/testdata/test/recover.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/recover.txtar
rename to gnovm/cmd/gno/testdata/test/recover.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/skip.txtar b/gnovm/cmd/gno/testdata/test/skip.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/skip.txtar
rename to gnovm/cmd/gno/testdata/test/skip.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/unknown_package.txtar b/gnovm/cmd/gno/testdata/test/unknown_package.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/unknown_package.txtar
rename to gnovm/cmd/gno/testdata/test/unknown_package.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/valid_filetest.txtar b/gnovm/cmd/gno/testdata/test/valid_filetest.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/valid_filetest.txtar
rename to gnovm/cmd/gno/testdata/test/valid_filetest.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/valid_test.txtar b/gnovm/cmd/gno/testdata/test/valid_test.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/valid_test.txtar
rename to gnovm/cmd/gno/testdata/test/valid_test.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/gobuild_flag_build_error.txtar b/gnovm/cmd/gno/testdata/transpile/gobuild_flag_build_error.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/gobuild_flag_build_error.txtar
rename to gnovm/cmd/gno/testdata/transpile/gobuild_flag_build_error.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/gobuild_flag_parse_error.txtar b/gnovm/cmd/gno/testdata/transpile/gobuild_flag_parse_error.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/gobuild_flag_parse_error.txtar
rename to gnovm/cmd/gno/testdata/transpile/gobuild_flag_parse_error.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/invalid_import.txtar b/gnovm/cmd/gno/testdata/transpile/invalid_import.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/invalid_import.txtar
rename to gnovm/cmd/gno/testdata/transpile/invalid_import.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/no_args.txtar b/gnovm/cmd/gno/testdata/transpile/no_args.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/no_args.txtar
rename to gnovm/cmd/gno/testdata/transpile/no_args.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/parse_error.txtar b/gnovm/cmd/gno/testdata/transpile/parse_error.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/parse_error.txtar
rename to gnovm/cmd/gno/testdata/transpile/parse_error.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_empty_dir.txtar b/gnovm/cmd/gno/testdata/transpile/valid_empty_dir.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/valid_empty_dir.txtar
rename to gnovm/cmd/gno/testdata/transpile/valid_empty_dir.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_gobuild_file.txtar b/gnovm/cmd/gno/testdata/transpile/valid_gobuild_file.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/valid_gobuild_file.txtar
rename to gnovm/cmd/gno/testdata/transpile/valid_gobuild_file.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_gobuild_flag.txtar b/gnovm/cmd/gno/testdata/transpile/valid_gobuild_flag.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/valid_gobuild_flag.txtar
rename to gnovm/cmd/gno/testdata/transpile/valid_gobuild_flag.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_output_flag.txtar b/gnovm/cmd/gno/testdata/transpile/valid_output_flag.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/valid_output_flag.txtar
rename to gnovm/cmd/gno/testdata/transpile/valid_output_flag.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_output_gobuild.txtar b/gnovm/cmd/gno/testdata/transpile/valid_output_gobuild.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/valid_output_gobuild.txtar
rename to gnovm/cmd/gno/testdata/transpile/valid_output_gobuild.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_transpile_file.txtar b/gnovm/cmd/gno/testdata/transpile/valid_transpile_file.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/valid_transpile_file.txtar
rename to gnovm/cmd/gno/testdata/transpile/valid_transpile_file.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_transpile_package.txtar b/gnovm/cmd/gno/testdata/transpile/valid_transpile_package.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/valid_transpile_package.txtar
rename to gnovm/cmd/gno/testdata/transpile/valid_transpile_package.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_transpile_tree.txtar b/gnovm/cmd/gno/testdata/transpile/valid_transpile_tree.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/valid_transpile_tree.txtar
rename to gnovm/cmd/gno/testdata/transpile/valid_transpile_tree.txtar
diff --git a/gnovm/cmd/gno/testdata_test.go b/gnovm/cmd/gno/testdata_test.go
index 15bc8d96e26..6b1bbd1d459 100644
--- a/gnovm/cmd/gno/testdata_test.go
+++ b/gnovm/cmd/gno/testdata_test.go
@@ -24,7 +24,6 @@ func Test_Scripts(t *testing.T) {
}
name := dir.Name()
- t.Logf("testing: %s", name)
t.Run(name, func(t *testing.T) {
updateScripts, _ := strconv.ParseBool(os.Getenv("UPDATE_SCRIPTS"))
p := testscript.Params{
diff --git a/gnovm/cmd/gno/transpile_test.go b/gnovm/cmd/gno/transpile_test.go
index 827c09e23f1..5a03ddc7657 100644
--- a/gnovm/cmd/gno/transpile_test.go
+++ b/gnovm/cmd/gno/transpile_test.go
@@ -6,29 +6,9 @@ import (
"strconv"
"testing"
- "github.com/rogpeppe/go-internal/testscript"
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
- "github.com/gnolang/gno/gnovm/pkg/integration"
)
-func Test_ScriptsTranspile(t *testing.T) {
- p := testscript.Params{
- Dir: "testdata/gno_transpile",
- }
-
- if coverdir, ok := integration.ResolveCoverageDir(); ok {
- err := integration.SetupTestscriptsCoverage(&p, coverdir)
- require.NoError(t, err)
- }
-
- err := integration.SetupGno(&p, t.TempDir())
- require.NoError(t, err)
-
- testscript.Run(t, p)
-}
-
func Test_parseGoBuildErrors(t *testing.T) {
t.Parallel()
diff --git a/gnovm/pkg/gnolang/go2gno.go b/gnovm/pkg/gnolang/go2gno.go
index 338efa20fcc..82d5c69b08b 100644
--- a/gnovm/pkg/gnolang/go2gno.go
+++ b/gnovm/pkg/gnolang/go2gno.go
@@ -39,7 +39,9 @@ import (
"go/token"
"go/types"
"os"
+ "path"
"reflect"
+ "slices"
"strconv"
"strings"
@@ -499,6 +501,18 @@ type MemPackageGetter interface {
// If format is true, the code will be automatically updated with the
// formatted source code.
func TypeCheckMemPackage(mempkg *gnovm.MemPackage, getter MemPackageGetter, format bool) error {
+ return typeCheckMemPackage(mempkg, getter, false, format)
+}
+
+// TypeCheckMemPackageTest performs the same type checks as [TypeCheckMemPackage],
+// but allows re-declarations.
+//
+// Note: like TypeCheckMemPackage, this function ignores tests and filetests.
+func TypeCheckMemPackageTest(mempkg *gnovm.MemPackage, getter MemPackageGetter) error {
+ return typeCheckMemPackage(mempkg, getter, true, false)
+}
+
+func typeCheckMemPackage(mempkg *gnovm.MemPackage, getter MemPackageGetter, testing, format bool) error {
var errs error
imp := &gnoImporter{
getter: getter,
@@ -508,6 +522,7 @@ func TypeCheckMemPackage(mempkg *gnovm.MemPackage, getter MemPackageGetter, form
errs = multierr.Append(errs, err)
},
},
+ allowRedefinitions: testing,
}
imp.cfg.Importer = imp
@@ -529,6 +544,9 @@ type gnoImporter struct {
getter MemPackageGetter
cache map[string]gnoImporterResult
cfg *types.Config
+
+ // allow symbol redefinitions? (test standard libraries)
+ allowRedefinitions bool
}
// Unused, but satisfies the Importer interface.
@@ -559,22 +577,39 @@ func (g *gnoImporter) ImportFrom(path, _ string, _ types.ImportMode) (*types.Pac
}
func (g *gnoImporter) parseCheckMemPackage(mpkg *gnovm.MemPackage, fmt bool) (*types.Package, error) {
+ // This map is used to allow for function re-definitions, which are allowed
+ // in Gno (testing context) but not in Go.
+ // This map links each function identifier with a closure to remove its
+ // associated declaration.
+ var delFunc map[string]func()
+ if g.allowRedefinitions {
+ delFunc = make(map[string]func())
+ }
+
fset := token.NewFileSet()
files := make([]*ast.File, 0, len(mpkg.Files))
var errs error
for _, file := range mpkg.Files {
+ // Ignore non-gno files.
+ // TODO: support filetest type checking. (should probably handle as each its
+ // own separate pkg, which should also be typechecked)
if !strings.HasSuffix(file.Name, ".gno") ||
- endsWith(file.Name, []string{"_test.gno", "_filetest.gno"}) {
- continue // skip spurious file.
+ strings.HasSuffix(file.Name, "_test.gno") ||
+ strings.HasSuffix(file.Name, "_filetest.gno") {
+ continue
}
const parseOpts = parser.ParseComments | parser.DeclarationErrors | parser.SkipObjectResolution
- f, err := parser.ParseFile(fset, file.Name, file.Body, parseOpts)
+ f, err := parser.ParseFile(fset, path.Join(mpkg.Path, file.Name), file.Body, parseOpts)
if err != nil {
errs = multierr.Append(errs, err)
continue
}
+ if delFunc != nil {
+ deleteOldIdents(delFunc, f)
+ }
+
// enforce formatting
if fmt {
var buf bytes.Buffer
@@ -595,6 +630,24 @@ func (g *gnoImporter) parseCheckMemPackage(mpkg *gnovm.MemPackage, fmt bool) (*t
return g.cfg.Check(mpkg.Path, fset, files, nil)
}
+func deleteOldIdents(idents map[string]func(), f *ast.File) {
+ for _, decl := range f.Decls {
+ fd, ok := decl.(*ast.FuncDecl)
+ if !ok || fd.Recv != nil { // ignore methods
+ continue
+ }
+ if del := idents[fd.Name.Name]; del != nil {
+ del()
+ }
+ decl := decl
+ idents[fd.Name.Name] = func() {
+ // NOTE: cannot use the index as a file may contain multiple decls to be removed,
+ // so removing one would make all "later" indexes wrong.
+ f.Decls = slices.DeleteFunc(f.Decls, func(d ast.Decl) bool { return decl == d })
+ }
+ }
+}
+
//----------------------------------------
// utility methods
diff --git a/gnovm/tests/integ/typecheck_missing_return/gno.mod b/gnovm/tests/integ/typecheck_missing_return/gno.mod
new file mode 100644
index 00000000000..3eaaa374994
--- /dev/null
+++ b/gnovm/tests/integ/typecheck_missing_return/gno.mod
@@ -0,0 +1 @@
+module gno.land/p/integ/valid
diff --git a/gnovm/tests/integ/typecheck_missing_return/main.gno b/gnovm/tests/integ/typecheck_missing_return/main.gno
new file mode 100644
index 00000000000..5d6e547097c
--- /dev/null
+++ b/gnovm/tests/integ/typecheck_missing_return/main.gno
@@ -0,0 +1,5 @@
+package valid
+
+func Hello() int {
+ // no return
+}
From 1bd64192a170fdf7ca904fb6bf27f10e2acc8ed5 Mon Sep 17 00:00:00 2001
From: Antoine Eddi <5222525+aeddi@users.noreply.github.com>
Date: Mon, 9 Dec 2024 11:16:05 +0100
Subject: [PATCH 10/22] feat(github-bot): add a fork condition and handle PR
reviews (#3303)
This PR improves the bot on two points:
- it now handle `pull_request_review` events to address [this
concern](https://github.com/gnolang/gno/issues/3238#issuecomment-2526206058)
https://github.com/gnolang/gno/commit/4f7b0b80c6e726806a473556b02444fded707254
- a new condition allows to check if a PR was created from a fork to
address [this
concern](https://github.com/gnolang/gno/issues/3238#issuecomment-2524018469)
https://github.com/gnolang/gno/commit/f491d95d68c755d7154cbbee48a9cebc7693fa89
---
.github/workflows/bot.yml | 4 +++
.../github-bot/internal/conditions/fork.go | 27 ++++++++++++++++
.../internal/conditions/fork_test.go | 31 +++++++++++++++++++
contribs/github-bot/internal/config/config.go | 2 +-
contribs/github-bot/internal/matrix/matrix.go | 2 +-
.../github-bot/internal/matrix/matrix_test.go | 9 ++++++
contribs/github-bot/internal/utils/actions.go | 2 +-
.../github-bot/internal/utils/github_const.go | 1 +
8 files changed, 75 insertions(+), 3 deletions(-)
create mode 100644 contribs/github-bot/internal/conditions/fork.go
create mode 100644 contribs/github-bot/internal/conditions/fork_test.go
diff --git a/.github/workflows/bot.yml b/.github/workflows/bot.yml
index 644540c1aaf..300a5928e25 100644
--- a/.github/workflows/bot.yml
+++ b/.github/workflows/bot.yml
@@ -14,6 +14,10 @@ on:
- converted_to_draft
- ready_for_review
+ # Watch for changes on PR reviews
+ pull_request_review:
+ types: [submitted, edited, dismissed]
+
# Watch for changes on PR comment
issue_comment:
types: [created, edited, deleted]
diff --git a/contribs/github-bot/internal/conditions/fork.go b/contribs/github-bot/internal/conditions/fork.go
new file mode 100644
index 00000000000..72cbae12004
--- /dev/null
+++ b/contribs/github-bot/internal/conditions/fork.go
@@ -0,0 +1,27 @@
+package conditions
+
+import (
+ "fmt"
+
+ "github.com/gnolang/gno/contribs/github-bot/internal/utils"
+
+ "github.com/google/go-github/v64/github"
+ "github.com/xlab/treeprint"
+)
+
+// CreatedFromFork Condition.
+type createdFromFork struct{}
+
+var _ Condition = &createdFromFork{}
+
+func (b *createdFromFork) IsMet(pr *github.PullRequest, details treeprint.Tree) bool {
+ return utils.AddStatusNode(
+ pr.GetHead().GetRepo().GetFullName() != pr.GetBase().GetRepo().GetFullName(),
+ fmt.Sprintf("The pull request was created from a fork (head branch repo: %s)", pr.GetHead().GetRepo().GetFullName()),
+ details,
+ )
+}
+
+func CreatedFromFork() Condition {
+ return &createdFromFork{}
+}
diff --git a/contribs/github-bot/internal/conditions/fork_test.go b/contribs/github-bot/internal/conditions/fork_test.go
new file mode 100644
index 00000000000..fe7e9a95bf1
--- /dev/null
+++ b/contribs/github-bot/internal/conditions/fork_test.go
@@ -0,0 +1,31 @@
+package conditions
+
+import (
+ "testing"
+
+ "github.com/gnolang/gno/contribs/github-bot/internal/utils"
+ "github.com/stretchr/testify/assert"
+
+ "github.com/google/go-github/v64/github"
+ "github.com/xlab/treeprint"
+)
+
+func TestCreatedFromFork(t *testing.T) {
+ t.Parallel()
+
+ var (
+ repo = &github.PullRequestBranch{Repo: &github.Repository{Owner: &github.User{Login: github.String("main")}, Name: github.String("repo"), FullName: github.String("main/repo")}}
+ fork = &github.PullRequestBranch{Repo: &github.Repository{Owner: &github.User{Login: github.String("fork")}, Name: github.String("repo"), FullName: github.String("fork/repo")}}
+ )
+
+ prFromMain := &github.PullRequest{Base: repo, Head: repo}
+ prFromFork := &github.PullRequest{Base: repo, Head: fork}
+
+ details := treeprint.New()
+ assert.False(t, CreatedFromFork().IsMet(prFromMain, details))
+ assert.True(t, utils.TestLastNodeStatus(t, false, details), "condition details should have a status: false")
+
+ details = treeprint.New()
+ assert.True(t, CreatedFromFork().IsMet(prFromFork, details))
+ assert.True(t, utils.TestLastNodeStatus(t, true, details), "condition details should have a status: true")
+}
diff --git a/contribs/github-bot/internal/config/config.go b/contribs/github-bot/internal/config/config.go
index c1d89e4cde5..2d595c7ce51 100644
--- a/contribs/github-bot/internal/config/config.go
+++ b/contribs/github-bot/internal/config/config.go
@@ -28,7 +28,7 @@ func Config(gh *client.GitHub) ([]AutomaticCheck, []ManualCheck) {
auto := []AutomaticCheck{
{
Description: "Maintainers must be able to edit this pull request ([more info](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork))",
- If: c.Always(),
+ If: c.CreatedFromFork(),
Then: r.MaintainerCanModify(),
},
{
diff --git a/contribs/github-bot/internal/matrix/matrix.go b/contribs/github-bot/internal/matrix/matrix.go
index 9c8f12e4214..02840721c80 100644
--- a/contribs/github-bot/internal/matrix/matrix.go
+++ b/contribs/github-bot/internal/matrix/matrix.go
@@ -113,7 +113,7 @@ func getPRListFromEvent(gh *client.GitHub, actionCtx *githubactions.GitHubContex
// Event triggered by an issue / PR comment being created / edited / deleted
// or any update on a PR.
- case utils.EventIssueComment, utils.EventPullRequest, utils.EventPullRequestTarget:
+ case utils.EventIssueComment, utils.EventPullRequest, utils.EventPullRequestReview, utils.EventPullRequestTarget:
// For these events, retrieve the number of the associated PR from the context.
prNum, err := utils.GetPRNumFromActionsCtx(actionCtx)
if err != nil {
diff --git a/contribs/github-bot/internal/matrix/matrix_test.go b/contribs/github-bot/internal/matrix/matrix_test.go
index fe5b7452a49..f6b34f16c24 100644
--- a/contribs/github-bot/internal/matrix/matrix_test.go
+++ b/contribs/github-bot/internal/matrix/matrix_test.go
@@ -54,6 +54,15 @@ func TestProcessEvent(t *testing.T) {
prs,
utils.PRList{1},
false,
+ }, {
+ "valid pull_request_review event",
+ &githubactions.GitHubContext{
+ EventName: utils.EventPullRequestReview,
+ Event: map[string]any{"pull_request": map[string]any{"number": 1.}},
+ },
+ prs,
+ utils.PRList{1},
+ false,
}, {
"valid pull_request_target event",
&githubactions.GitHubContext{
diff --git a/contribs/github-bot/internal/utils/actions.go b/contribs/github-bot/internal/utils/actions.go
index 3e08a8e1548..0686e8c29c5 100644
--- a/contribs/github-bot/internal/utils/actions.go
+++ b/contribs/github-bot/internal/utils/actions.go
@@ -30,7 +30,7 @@ func GetPRNumFromActionsCtx(actionCtx *githubactions.GitHubContext) (int, error)
switch actionCtx.EventName {
case EventIssueComment:
firstKey = "issue"
- case EventPullRequest, EventPullRequestTarget:
+ case EventPullRequest, EventPullRequestReview, EventPullRequestTarget:
firstKey = "pull_request"
default:
return 0, fmt.Errorf("unsupported event: %s", actionCtx.EventName)
diff --git a/contribs/github-bot/internal/utils/github_const.go b/contribs/github-bot/internal/utils/github_const.go
index 26d7d54d477..f030d9365f7 100644
--- a/contribs/github-bot/internal/utils/github_const.go
+++ b/contribs/github-bot/internal/utils/github_const.go
@@ -5,6 +5,7 @@ const (
// GitHub Actions Event Names.
EventIssueComment = "issue_comment"
EventPullRequest = "pull_request"
+ EventPullRequestReview = "pull_request_review"
EventPullRequestTarget = "pull_request_target"
EventWorkflowDispatch = "workflow_dispatch"
From 9bd9e47e7354d98cbc2980abbd6ee61096efd92d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Milo=C5=A1=20=C5=BDivkovi=C4=87?=
Date: Mon, 9 Dec 2024 16:39:22 +0100
Subject: [PATCH 11/22] chore: update portal loop archiver version (#3308)
## Description
This PR updates the portal loop tx-archiver version to `v0.4.2`, which
eliminates the millisecond timestamp issue
---
misc/loop/go.mod | 2 +-
misc/loop/go.sum | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/misc/loop/go.mod b/misc/loop/go.mod
index 70e9d21734b..af7783e57bb 100644
--- a/misc/loop/go.mod
+++ b/misc/loop/go.mod
@@ -8,7 +8,7 @@ require (
github.com/docker/docker v24.0.7+incompatible
github.com/docker/go-connections v0.4.0
github.com/gnolang/gno v0.1.0-nightly.20240627
- github.com/gnolang/tx-archive v0.4.0
+ github.com/gnolang/tx-archive v0.4.2
github.com/prometheus/client_golang v1.17.0
github.com/sirupsen/logrus v1.9.3
)
diff --git a/misc/loop/go.sum b/misc/loop/go.sum
index 8e0feb11e4a..0d235f2cfb1 100644
--- a/misc/loop/go.sum
+++ b/misc/loop/go.sum
@@ -68,8 +68,8 @@ github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHqu
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/gnolang/tx-archive v0.4.0 h1:+1Rgo0U0HjLQLq/xqeGdJwtAzo9xWj09t1oZLvrL3bU=
-github.com/gnolang/tx-archive v0.4.0/go.mod h1:seKHGnvxUnDgH/mSsCEdwG0dHY/FrpbUm6Hd0+KMd9w=
+github.com/gnolang/tx-archive v0.4.2 h1:xBBqLLKY9riv9yxpQgVhItCWxIji2rX6xNFmCY1cEOQ=
+github.com/gnolang/tx-archive v0.4.2/go.mod h1:AGUBGO+DCLuKL80a1GJRnpcJ5gxVd9L4jEJXQB9uXp4=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
From bb38fb1942aac60999bed90c904711c46fe7b783 Mon Sep 17 00:00:00 2001
From: 6h057 <15034695+omarsy@users.noreply.github.com>
Date: Tue, 10 Dec 2024 02:15:27 +0100
Subject: [PATCH 12/22] fix(gnovm): improve error message for nil assignment in
variable declaration (#3068)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
…aration
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: Morgan
Co-authored-by: ltzmaxwell
---
gnovm/pkg/gnolang/preprocess.go | 199 ++++++++++++++-------------
gnovm/pkg/gnolang/type_check.go | 53 ++++---
gnovm/pkg/gnolang/type_check_test.go | 2 +-
gnovm/pkg/gnolang/types.go | 38 ++---
gnovm/tests/files/add3.gno | 9 ++
gnovm/tests/files/assign38.gno | 10 ++
gnovm/tests/files/fun28.gno | 10 ++
gnovm/tests/files/slice3.gno | 9 ++
gnovm/tests/files/var35.gno | 8 ++
9 files changed, 200 insertions(+), 138 deletions(-)
create mode 100644 gnovm/tests/files/add3.gno
create mode 100644 gnovm/tests/files/assign38.gno
create mode 100644 gnovm/tests/files/fun28.gno
create mode 100644 gnovm/tests/files/slice3.gno
create mode 100644 gnovm/tests/files/var35.gno
diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go
index 78b11a4ebc5..6e749053d72 100644
--- a/gnovm/pkg/gnolang/preprocess.go
+++ b/gnovm/pkg/gnolang/preprocess.go
@@ -743,7 +743,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
for i, cx := range n.Cases {
cx = Preprocess(
store, last, cx).(Expr)
- checkOrConvertType(store, last, &cx, tt, false) // #nosec G601
+ checkOrConvertType(store, last, n, &cx, tt, false) // #nosec G601
n.Cases[i] = cx
}
}
@@ -882,7 +882,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
// Preprocess and convert tag if const.
if n.X != nil {
n.X = Preprocess(store, last, n.X).(Expr)
- convertIfConst(store, last, n.X)
+ convertIfConst(store, last, n, n.X)
}
}
return n, TRANS_CONTINUE
@@ -1102,10 +1102,10 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
// First, convert untyped as necessary.
if !shouldSwapOnSpecificity(lcx.T, rcx.T) {
// convert n.Left to right type.
- checkOrConvertType(store, last, &n.Left, rcx.T, false)
+ checkOrConvertType(store, last, n, &n.Left, rcx.T, false)
} else {
// convert n.Right to left type.
- checkOrConvertType(store, last, &n.Right, lcx.T, false)
+ checkOrConvertType(store, last, n, &n.Right, lcx.T, false)
}
// Then, evaluate the expression.
cx := evalConst(store, last, n)
@@ -1125,7 +1125,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
rnt.String()))
}
// convert n.Left to pt type,
- checkOrConvertType(store, last, &n.Left, pt, false)
+ checkOrConvertType(store, last, n, &n.Left, pt, false)
// if check pass, convert n.Right to (gno) pt type,
rn := Expr(Call(pt.String(), n.Right))
// and convert result back.
@@ -1154,7 +1154,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
}
if !isUntyped(rt) { // right is typed
- checkOrConvertType(store, last, &n.Left, rt, false)
+ checkOrConvertType(store, last, n, &n.Left, rt, false)
} else {
if shouldSwapOnSpecificity(lt, rt) {
checkUntypedShiftExpr(n.Right)
@@ -1165,10 +1165,10 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
}
} else if lcx.T == nil { // LHS is nil.
// convert n.Left to typed-nil type.
- checkOrConvertType(store, last, &n.Left, rt, false)
+ checkOrConvertType(store, last, n, &n.Left, rt, false)
} else {
if isUntyped(rt) {
- checkOrConvertType(store, last, &n.Right, lt, false)
+ checkOrConvertType(store, last, n, &n.Right, lt, false)
}
}
} else if ric { // right is const, left is not
@@ -1186,7 +1186,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
// convert n.Left to (gno) pt type,
ln := Expr(Call(pt.String(), n.Left))
// convert n.Right to pt type,
- checkOrConvertType(store, last, &n.Right, pt, false)
+ checkOrConvertType(store, last, n, &n.Right, pt, false)
// and convert result back.
tx := constType(n, lnt)
// reset/create n2 to preprocess left child.
@@ -1212,7 +1212,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
}
// both untyped, e.g. 1< float64.
// (const) untyped bigint -> int.
if !constConverted {
- convertConst(store, last, arg0, nil)
+ convertConst(store, last, n, arg0, nil)
}
// evaluate the new expression.
cx := evalConst(store, last, n)
@@ -1397,15 +1397,15 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
if isUntyped(at) {
switch arg0.Op {
case EQL, NEQ, LSS, GTR, LEQ, GEQ:
- assertAssignableTo(at, ct, false)
+ assertAssignableTo(n, at, ct, false)
break
default:
- checkOrConvertType(store, last, &n.Args[0], ct, false)
+ checkOrConvertType(store, last, n, &n.Args[0], ct, false)
}
}
case *UnaryExpr:
if isUntyped(at) {
- checkOrConvertType(store, last, &n.Args[0], ct, false)
+ checkOrConvertType(store, last, n, &n.Args[0], ct, false)
}
default:
// do nothing
@@ -1549,7 +1549,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
panic("should not happen")
}
// Specify function param/result generics.
- sft := ft.Specify(store, argTVs, isVarg)
+ sft := ft.Specify(store, n, argTVs, isVarg)
spts := sft.Params
srts := FieldTypeList(sft.Results).Types()
// If generics were specified, override attr
@@ -1575,12 +1575,12 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
for i, tv := range argTVs {
if hasVarg {
if (len(spts) - 1) <= i {
- assertAssignableTo(tv.T, spts[len(spts)-1].Type.Elem(), true)
+ assertAssignableTo(n, tv.T, spts[len(spts)-1].Type.Elem(), true)
} else {
- assertAssignableTo(tv.T, spts[i].Type, true)
+ assertAssignableTo(n, tv.T, spts[i].Type, true)
}
} else {
- assertAssignableTo(tv.T, spts[i].Type, true)
+ assertAssignableTo(n, tv.T, spts[i].Type, true)
}
}
} else {
@@ -1591,16 +1591,16 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
if len(spts) <= i {
panic("expected final vargs slice but got many")
}
- checkOrConvertType(store, last, &n.Args[i], spts[i].Type, true)
+ checkOrConvertType(store, last, n, &n.Args[i], spts[i].Type, true)
} else {
- checkOrConvertType(store, last, &n.Args[i],
+ checkOrConvertType(store, last, n, &n.Args[i],
spts[len(spts)-1].Type.Elem(), true)
}
} else {
- checkOrConvertType(store, last, &n.Args[i], spts[i].Type, true)
+ checkOrConvertType(store, last, n, &n.Args[i], spts[i].Type, true)
}
} else {
- checkOrConvertType(store, last, &n.Args[i], spts[i].Type, true)
+ checkOrConvertType(store, last, n, &n.Args[i], spts[i].Type, true)
}
}
}
@@ -1621,10 +1621,10 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
case StringKind, ArrayKind, SliceKind:
// Replace const index with int *ConstExpr,
// or if not const, assert integer type..
- checkOrConvertIntegerKind(store, last, n.Index)
+ checkOrConvertIntegerKind(store, last, n, n.Index)
case MapKind:
mt := baseOf(gnoTypeOf(store, dt)).(*MapType)
- checkOrConvertType(store, last, &n.Index, mt.Key, false)
+ checkOrConvertType(store, last, n, &n.Index, mt.Key, false)
default:
panic(fmt.Sprintf(
"unexpected index base kind for type %s",
@@ -1635,15 +1635,15 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
case *SliceExpr:
// Replace const L/H/M with int *ConstExpr,
// or if not const, assert integer type..
- checkOrConvertIntegerKind(store, last, n.Low)
- checkOrConvertIntegerKind(store, last, n.High)
- checkOrConvertIntegerKind(store, last, n.Max)
+ checkOrConvertIntegerKind(store, last, n, n.Low)
+ checkOrConvertIntegerKind(store, last, n, n.High)
+ checkOrConvertIntegerKind(store, last, n, n.Max)
// if n.X is untyped, convert to corresponding type
t := evalStaticTypeOf(store, last, n.X)
if isUntyped(t) {
dt := defaultTypeOf(t)
- checkOrConvertType(store, last, &n.X, dt, false)
+ checkOrConvertType(store, last, n, &n.X, dt, false)
}
// TRANS_LEAVE -----------------------
@@ -1722,28 +1722,28 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
key := n.Elts[i].Key.(*NameExpr).Name
path := cclt.GetPathForName(key)
ft := cclt.GetStaticTypeOfAt(path)
- checkOrConvertType(store, last, &n.Elts[i].Value, ft, false)
+ checkOrConvertType(store, last, n, &n.Elts[i].Value, ft, false)
}
} else {
for i := 0; i < len(n.Elts); i++ {
ft := cclt.Fields[i].Type
- checkOrConvertType(store, last, &n.Elts[i].Value, ft, false)
+ checkOrConvertType(store, last, n, &n.Elts[i].Value, ft, false)
}
}
case *ArrayType:
for i := 0; i < len(n.Elts); i++ {
- convertType(store, last, &n.Elts[i].Key, IntType)
- checkOrConvertType(store, last, &n.Elts[i].Value, cclt.Elt, false)
+ convertType(store, last, n, &n.Elts[i].Key, IntType)
+ checkOrConvertType(store, last, n, &n.Elts[i].Value, cclt.Elt, false)
}
case *SliceType:
for i := 0; i < len(n.Elts); i++ {
- convertType(store, last, &n.Elts[i].Key, IntType)
- checkOrConvertType(store, last, &n.Elts[i].Value, cclt.Elt, false)
+ convertType(store, last, n, &n.Elts[i].Key, IntType)
+ checkOrConvertType(store, last, n, &n.Elts[i].Value, cclt.Elt, false)
}
case *MapType:
for i := 0; i < len(n.Elts); i++ {
- checkOrConvertType(store, last, &n.Elts[i].Key, cclt.Key, false)
- checkOrConvertType(store, last, &n.Elts[i].Value, cclt.Value, false)
+ checkOrConvertType(store, last, n, &n.Elts[i].Key, cclt.Key, false)
+ checkOrConvertType(store, last, n, &n.Elts[i].Value, cclt.Value, false)
}
case *NativeType:
clt = cclt.GnoType(store)
@@ -1943,7 +1943,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
// TRANS_LEAVE -----------------------
case *FieldTypeExpr:
// Replace const Tag with default *ConstExpr.
- convertIfConst(store, last, n.Tag)
+ convertIfConst(store, last, n, n.Tag)
// TRANS_LEAVE -----------------------
case *ArrayTypeExpr:
@@ -1952,7 +1952,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
} else {
// Replace const Len with int *ConstExpr.
cx := evalConst(store, last, n.Len)
- convertConst(store, last, cx, IntType)
+ convertConst(store, last, n, cx, IntType)
n.Len = cx
}
// NOTE: For all TypeExprs, the node is not replaced
@@ -1993,7 +1993,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
// Rhs consts become default *ConstExprs.
for _, rx := range n.Rhs {
// NOTE: does nothing if rx is "nil".
- convertIfConst(store, last, rx)
+ convertIfConst(store, last, n, rx)
}
nameExprs := make(NameExprs, len(n.Lhs))
@@ -2001,7 +2001,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
nameExprs[i] = *n.Lhs[i].(*NameExpr)
}
- defineOrDecl(store, last, false, nameExprs, nil, n.Rhs)
+ defineOrDecl(store, last, n, false, nameExprs, nil, n.Rhs)
} else { // ASSIGN, or assignment operation (+=, -=, <<=, etc.)
// NOTE: Keep in sync with DEFINE above.
if len(n.Lhs) > len(n.Rhs) {
@@ -2090,11 +2090,11 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
} else { // len(Lhs) == len(Rhs)
if n.Op == SHL_ASSIGN || n.Op == SHR_ASSIGN {
// Special case if shift assign <<= or >>=.
- convertType(store, last, &n.Rhs[0], UintType)
+ convertType(store, last, n, &n.Rhs[0], UintType)
} else if n.Op == ADD_ASSIGN || n.Op == SUB_ASSIGN || n.Op == MUL_ASSIGN || n.Op == QUO_ASSIGN || n.Op == REM_ASSIGN {
// e.g. a += b, single value for lhs and rhs,
lt := evalStaticTypeOf(store, last, n.Lhs[0])
- checkOrConvertType(store, last, &n.Rhs[0], lt, true)
+ checkOrConvertType(store, last, n, &n.Rhs[0], lt, true)
} else { // all else, like BAND_ASSIGN, etc
// General case: a, b = x, y.
for i, lx := range n.Lhs {
@@ -2104,7 +2104,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
}
// if lt is interface, nothing will happen
- checkOrConvertType(store, last, &n.Rhs[i], lt, true)
+ checkOrConvertType(store, last, n, &n.Rhs[i], lt, true)
}
}
}
@@ -2181,12 +2181,12 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
// TRANS_LEAVE -----------------------
case *ForStmt:
// Cond consts become bool *ConstExprs.
- checkOrConvertBoolKind(store, last, n.Cond)
+ checkOrConvertBoolKind(store, last, n, n.Cond)
// TRANS_LEAVE -----------------------
case *IfStmt:
// Cond consts become bool *ConstExprs.
- checkOrConvertBoolKind(store, last, n.Cond)
+ checkOrConvertBoolKind(store, last, n, n.Cond)
// TRANS_LEAVE -----------------------
case *RangeStmt:
@@ -2242,7 +2242,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
// XXX how to deal?
panic("not yet implemented")
} else {
- checkOrConvertType(store, last, &n.Results[i], rt, false)
+ checkOrConvertType(store, last, n, &n.Results[i], rt, false)
}
}
}
@@ -2250,7 +2250,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
// TRANS_LEAVE -----------------------
case *SendStmt:
// Value consts become default *ConstExprs.
- checkOrConvertType(store, last, &n.Value, nil, false)
+ checkOrConvertType(store, last, n, &n.Value, nil, false)
// TRANS_LEAVE -----------------------
case *SelectCaseStmt:
@@ -2303,7 +2303,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
// runDeclaration(), as this uses OpStaticTypeOf.
}
- defineOrDecl(store, last, n.Const, n.NameExprs, n.Type, n.Values)
+ defineOrDecl(store, last, n, n.Const, n.NameExprs, n.Type, n.Values)
// TODO make note of constance in static block for
// future use, or consider "const paths". set as
@@ -2383,6 +2383,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
func defineOrDecl(
store Store,
bn BlockNode,
+ n Node,
isConst bool,
nameExprs []NameExpr,
typeExpr Expr,
@@ -2399,9 +2400,9 @@ func defineOrDecl(
tvs := make([]TypedValue, numNames)
if numVals == 1 && numNames > 1 {
- parseMultipleAssignFromOneExpr(sts, tvs, store, bn, nameExprs, typeExpr, valueExprs[0])
+ parseMultipleAssignFromOneExpr(store, bn, n, sts, tvs, nameExprs, typeExpr, valueExprs[0])
} else {
- parseAssignFromExprList(sts, tvs, store, bn, isConst, nameExprs, typeExpr, valueExprs)
+ parseAssignFromExprList(store, bn, n, sts, tvs, isConst, nameExprs, typeExpr, valueExprs)
}
node := skipFile(bn)
@@ -2420,10 +2421,11 @@ func defineOrDecl(
// parseAssignFromExprList parses assignment to multiple variables from a list of expressions.
// This function will alter the value of sts, tvs.
func parseAssignFromExprList(
- sts []Type,
- tvs []TypedValue,
store Store,
bn BlockNode,
+ n Node,
+ sts []Type,
+ tvs []TypedValue,
isConst bool,
nameExprs []NameExpr,
typeExpr Expr,
@@ -2450,7 +2452,7 @@ func parseAssignFromExprList(
}
// Convert if const to nt.
for i := range valueExprs {
- checkOrConvertType(store, bn, &valueExprs[i], nt, false)
+ checkOrConvertType(store, bn, n, &valueExprs[i], nt, false)
}
} else if isConst {
// Derive static type from values.
@@ -2462,10 +2464,10 @@ func parseAssignFromExprList(
// Convert n.Value to default type.
for i, vx := range valueExprs {
if cx, ok := vx.(*ConstExpr); ok {
- convertConst(store, bn, cx, nil)
+ convertConst(store, bn, n, cx, nil)
// convertIfConst(store, last, vx)
} else {
- checkOrConvertType(store, bn, &vx, nil, false)
+ checkOrConvertType(store, bn, n, &vx, nil, false)
}
vt := evalStaticTypeOf(store, bn, vx)
sts[i] = vt
@@ -2506,10 +2508,11 @@ func parseAssignFromExprList(
// - a, b := n.(T)
// - a, b := n[i], where n is a map
func parseMultipleAssignFromOneExpr(
- sts []Type,
- tvs []TypedValue,
store Store,
bn BlockNode,
+ n Node,
+ sts []Type,
+ tvs []TypedValue,
nameExprs []NameExpr,
typeExpr Expr,
valueExpr Expr,
@@ -2567,7 +2570,7 @@ func parseMultipleAssignFromOneExpr(
if st != nil {
tt := tuple.Elts[i]
- if checkAssignableTo(tt, st, false) != nil {
+ if checkAssignableTo(n, tt, st, false) != nil {
panic(
fmt.Sprintf(
"cannot use %v (value of type %s) as %s value in assignment",
@@ -3491,14 +3494,14 @@ func isConstType(x Expr) bool {
}
// check before convert type
-func checkOrConvertType(store Store, last BlockNode, x *Expr, t Type, autoNative bool) {
+func checkOrConvertType(store Store, last BlockNode, n Node, x *Expr, t Type, autoNative bool) {
if debug {
debug.Printf("checkOrConvertType, *x: %v:, t:%v \n", *x, t)
}
if cx, ok := (*x).(*ConstExpr); ok {
if _, ok := t.(*NativeType); !ok { // not native type, refer to time4_native.gno.
// e.g. int(1) == int8(1)
- assertAssignableTo(cx.T, t, autoNative)
+ assertAssignableTo(n, cx.T, t, autoNative)
}
} else if bx, ok := (*x).(*BinaryExpr); ok && (bx.Op == SHL || bx.Op == SHR) {
xt := evalStaticTypeOf(store, last, *x)
@@ -3507,22 +3510,22 @@ func checkOrConvertType(store Store, last BlockNode, x *Expr, t Type, autoNative
}
if isUntyped(xt) {
// check assignable first, see: types/shift_b6.gno
- assertAssignableTo(xt, t, autoNative)
+ assertAssignableTo(n, xt, t, autoNative)
if t == nil || t.Kind() == InterfaceKind {
t = defaultTypeOf(xt)
}
bx.assertShiftExprCompatible2(t)
- checkOrConvertType(store, last, &bx.Left, t, autoNative)
+ checkOrConvertType(store, last, n, &bx.Left, t, autoNative)
} else {
- assertAssignableTo(xt, t, autoNative)
+ assertAssignableTo(n, xt, t, autoNative)
}
return
} else if *x != nil {
xt := evalStaticTypeOf(store, last, *x)
if t != nil {
- assertAssignableTo(xt, t, autoNative)
+ assertAssignableTo(n, xt, t, autoNative)
}
if isUntyped(xt) {
// Push type into expr if qualifying binary expr.
@@ -3534,8 +3537,8 @@ func checkOrConvertType(store Store, last BlockNode, x *Expr, t Type, autoNative
rt := evalStaticTypeOf(store, last, bx.Right)
if t != nil {
// push t into bx.Left and bx.Right
- checkOrConvertType(store, last, &bx.Left, t, autoNative)
- checkOrConvertType(store, last, &bx.Right, t, autoNative)
+ checkOrConvertType(store, last, n, &bx.Left, t, autoNative)
+ checkOrConvertType(store, last, n, &bx.Right, t, autoNative)
return
} else {
if shouldSwapOnSpecificity(lt, rt) {
@@ -3546,11 +3549,11 @@ func checkOrConvertType(store Store, last BlockNode, x *Expr, t Type, autoNative
// without a specific context type, '1.0< + (const (undefined)) (mismatched types int and untyped nil)
diff --git a/gnovm/tests/files/assign38.gno b/gnovm/tests/files/assign38.gno
new file mode 100644
index 00000000000..5ef3549ccf6
--- /dev/null
+++ b/gnovm/tests/files/assign38.gno
@@ -0,0 +1,10 @@
+package main
+
+func main() {
+ a := 1
+ a = nil
+ println(a)
+}
+
+// Error:
+// main/files/assign38.gno:5:2: cannot use nil as int value in assignment
diff --git a/gnovm/tests/files/fun28.gno b/gnovm/tests/files/fun28.gno
new file mode 100644
index 00000000000..cf969f9f34b
--- /dev/null
+++ b/gnovm/tests/files/fun28.gno
@@ -0,0 +1,10 @@
+package main
+
+func f(i int) {}
+
+func main() {
+ f(nil)
+}
+
+// Error:
+// main/files/fun28.gno:6:2: cannot use nil as int value in argument to f
diff --git a/gnovm/tests/files/slice3.gno b/gnovm/tests/files/slice3.gno
new file mode 100644
index 00000000000..1132da01420
--- /dev/null
+++ b/gnovm/tests/files/slice3.gno
@@ -0,0 +1,9 @@
+package main
+
+func main() {
+ i := []string{nil}
+ println(i)
+}
+
+// Error:
+// main/files/slice3.gno:4:7: cannot use nil as string value in array, slice literal or map literal
diff --git a/gnovm/tests/files/var35.gno b/gnovm/tests/files/var35.gno
new file mode 100644
index 00000000000..87b1cc68590
--- /dev/null
+++ b/gnovm/tests/files/var35.gno
@@ -0,0 +1,8 @@
+package main
+
+func main() {
+ var i int = nil
+}
+
+// Error:
+// main/files/var35.gno:4:6: cannot use nil as int value in variable declaration
From ed4ebe826b24189d0fcaa50c57eab03ff178c9fa Mon Sep 17 00:00:00 2001
From: hthieu1110
Date: Tue, 10 Dec 2024 02:36:38 -0800
Subject: [PATCH 13/22] fix(gnovm): do not allow nil as type declaration
(#3309)
Fix https://github.com/gnolang/gno/issues/3307
---------
Co-authored-by: hieu.ha
---
gnovm/pkg/gnolang/preprocess.go | 6 ++++++
gnovm/tests/files/type40.gno | 2 +-
gnovm/tests/files/type41.gno | 9 +++++++++
3 files changed, 16 insertions(+), 1 deletion(-)
create mode 100644 gnovm/tests/files/type41.gno
diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go
index 6e749053d72..a3e498710bb 100644
--- a/gnovm/pkg/gnolang/preprocess.go
+++ b/gnovm/pkg/gnolang/preprocess.go
@@ -4283,6 +4283,12 @@ func tryPredefine(store Store, last BlockNode, d Decl) (un Name) {
if isBlankIdentifier(tx) {
panic("cannot use _ as value or type")
}
+
+ // do not allow nil as type.
+ if tx.Name == "nil" {
+ panic("nil is not a type")
+ }
+
if tv := last.GetValueRef(store, tx.Name, true); tv != nil {
t = tv.GetType()
if dt, ok := t.(*DeclaredType); ok {
diff --git a/gnovm/tests/files/type40.gno b/gnovm/tests/files/type40.gno
index 65210798007..fe312e220e0 100644
--- a/gnovm/tests/files/type40.gno
+++ b/gnovm/tests/files/type40.gno
@@ -43,4 +43,4 @@ func main() {
// 5
// 6
// 7
-// yo
\ No newline at end of file
+// yo
diff --git a/gnovm/tests/files/type41.gno b/gnovm/tests/files/type41.gno
new file mode 100644
index 00000000000..ea1a3b1df24
--- /dev/null
+++ b/gnovm/tests/files/type41.gno
@@ -0,0 +1,9 @@
+package main
+
+type A nil
+
+func main() {
+}
+
+// Error:
+// main/files/type41.gno:3:6: nil is not a type
From 8e7fb503adc7a728ea30fedd0891f27d80a2fe1b Mon Sep 17 00:00:00 2001
From: Antoine Eddi <5222525+aeddi@users.noreply.github.com>
Date: Tue, 10 Dec 2024 12:02:55 +0100
Subject: [PATCH 14/22] feat(github-bot): refactor comment + add force skip
(#3311)
This PR significantly modifies the github-bot's comment and adds a
button to force the success of its CI check, even it the requirements
provided in the config are not met.
Related to
https://github.com/gnolang/gno/issues/3238#issuecomment-2526174402
**Edit**: I updated [the comment
below](https://github.com/gnolang/gno/pull/3311#issuecomment-2528477336)
by running the bot on my laptop if you want to see the result (so the
skip button is not working yet).
---
contribs/github-bot/internal/check/check.go | 32 +++++++++++++++----
contribs/github-bot/internal/check/comment.go | 30 +++++++++--------
.../github-bot/internal/check/comment.tmpl | 32 ++++++++++++++-----
.../github-bot/internal/check/comment_test.go | 19 +++++++++--
contribs/github-bot/internal/config/config.go | 9 ++++++
5 files changed, 91 insertions(+), 31 deletions(-)
diff --git a/contribs/github-bot/internal/check/check.go b/contribs/github-bot/internal/check/check.go
index 5ca2235e823..cb1848b757c 100644
--- a/contribs/github-bot/internal/check/check.go
+++ b/contribs/github-bot/internal/check/check.go
@@ -101,7 +101,8 @@ func processPRList(gh *client.GitHub, prs []*github.PullRequest) error {
go func(pr *github.PullRequest) {
defer wg.Done()
commentContent := CommentContent{}
- commentContent.allSatisfied = true
+ commentContent.AutoAllSatisfied = true
+ commentContent.ManualAllSatisfied = true
// Iterate over all automatic rules in config.
for _, autoRule := range autoRules {
@@ -120,7 +121,7 @@ func processPRList(gh *client.GitHub, prs []*github.PullRequest) error {
thenDetails.SetValue(fmt.Sprintf("%s Requirement satisfied", utils.Success))
c.Satisfied = true
} else {
- commentContent.allSatisfied = false
+ commentContent.AutoAllSatisfied = false
}
c.ConditionDetails = ifDetails.String()
@@ -160,8 +161,14 @@ func processPRList(gh *client.GitHub, prs []*github.PullRequest) error {
},
)
- if checkedBy == "" {
- commentContent.allSatisfied = false
+ // If this check is the special one, store its state in the dedicated var.
+ if manualRule.Description == config.ForceSkipDescription {
+ if checkedBy != "" {
+ commentContent.ForceSkip = true
+ }
+ } else if checkedBy == "" {
+ // Or if its a normal check, just verify if it was checked by someone.
+ commentContent.ManualAllSatisfied = false
}
}
@@ -224,9 +231,20 @@ func logResults(logger logger.Logger, prNum int, commentContent CommentContent)
}
logger.Infof("Conclusion:")
- if commentContent.allSatisfied {
- logger.Infof("%s All requirements are satisfied\n", utils.Success)
+
+ if commentContent.AutoAllSatisfied {
+ logger.Infof("%s All automated checks are satisfied", utils.Success)
+ } else {
+ logger.Infof("%s Some automated checks are not satisfied", utils.Fail)
+ }
+
+ if commentContent.ManualAllSatisfied {
+ logger.Infof("%s All manual checks are satisfied\n", utils.Success)
} else {
- logger.Infof("%s Not all requirements are satisfied\n", utils.Fail)
+ logger.Infof("%s Some manual checks are not satisfied\n", utils.Fail)
+ }
+
+ if commentContent.ForceSkip {
+ logger.Infof("%s Bot checks are force skipped\n", utils.Success)
}
}
diff --git a/contribs/github-bot/internal/check/comment.go b/contribs/github-bot/internal/check/comment.go
index 297395ffe4b..d2b386cfa2e 100644
--- a/contribs/github-bot/internal/check/comment.go
+++ b/contribs/github-bot/internal/check/comment.go
@@ -24,9 +24,9 @@ var errTriggeredByBot = errors.New("event triggered by bot")
// Compile regex only once.
var (
// Regex for capturing the entire line of a manual check.
- manualCheckLine = regexp.MustCompile(`(?m:^-\s\[([ xX])\]\s+(.+?)\s*(\(checked by @(\w+)\))?$)`)
+ manualCheckLine = regexp.MustCompile(`(?m:^- \[([ xX])\] (.+?)(?: \(checked by @([A-Za-z0-9-]+)\))?$)`)
// Regex for capturing only the checkboxes.
- checkboxes = regexp.MustCompile(`(?m:^- \[[ x]\])`)
+ checkboxes = regexp.MustCompile(`(?m:^- \[[ xX]\])`)
// Regex used to capture markdown links.
markdownLink = regexp.MustCompile(`\[(.*)\]\([^)]*\)`)
)
@@ -46,9 +46,11 @@ type ManualContent struct {
Teams []string
}
type CommentContent struct {
- AutoRules []AutoContent
- ManualRules []ManualContent
- allSatisfied bool
+ AutoRules []AutoContent
+ ManualRules []ManualContent
+ AutoAllSatisfied bool
+ ManualAllSatisfied bool
+ ForceSkip bool
}
type manualCheckDetails struct {
@@ -64,10 +66,10 @@ func getCommentManualChecks(commentBody string) map[string]manualCheckDetails {
// For each line that matches the "Manual check" regex.
for _, match := range manualCheckLine.FindAllStringSubmatch(commentBody, -1) {
description := match[2]
- status := match[1]
+ status := strings.ToLower(match[1]) // if X captured, convert it to x.
checkedBy := ""
- if len(match) > 4 {
- checkedBy = strings.ToLower(match[4]) // if X captured, convert it to x.
+ if len(match) > 3 {
+ checkedBy = match[3]
}
checks[description] = manualCheckDetails{status: status, checkedBy: checkedBy}
@@ -261,13 +263,15 @@ func updatePullRequest(gh *client.GitHub, pr *github.PullRequest, content Commen
var (
context = "Merge Requirements"
targetURL = comment.GetHTMLURL()
- state = "failure"
- description = "Some requirements are not satisfied yet. See bot comment."
+ state = "success"
+ description = "All requirements are satisfied."
)
- if content.allSatisfied {
- state = "success"
- description = "All requirements are satisfied."
+ if content.ForceSkip {
+ description = "Bot checks are skipped for this PR."
+ } else if !content.AutoAllSatisfied || !content.ManualAllSatisfied {
+ state = "failure"
+ description = "Some requirements are not satisfied yet. See bot comment."
}
// Update or create commit status.
diff --git a/contribs/github-bot/internal/check/comment.tmpl b/contribs/github-bot/internal/check/comment.tmpl
index 4312019dd2e..d9b633a69d5 100644
--- a/contribs/github-bot/internal/check/comment.tmpl
+++ b/contribs/github-bot/internal/check/comment.tmpl
@@ -1,19 +1,34 @@
-I'm a bot that assists the Gno Core team in maintaining this repository. My role is to ensure that contributors understand and follow our guidelines, helping to streamline the development process.
+#### 🛠 PR Checks Summary
+{{ if and .AutoRules (not .AutoAllSatisfied) }}{{ range .AutoRules }}{{ if not .Satisfied }} 🔴 {{ .Description }}
+{{end}}{{end}}{{ else }}All **Automated Checks** passed. ✅{{end}}
-The following requirements must be fulfilled before a pull request can be merged.
-Some requirement checks are automated and can be verified by the CI, while others need manual verification by a staff member.
+##### Manual Checks (for Reviewers):
+{{ if .ManualRules }}{{ range .ManualRules }}- [{{ if .CheckedBy }}x{{ else }} {{ end }}] {{ .Description }}{{ if .CheckedBy }} (checked by @{{ .CheckedBy }}){{ end }}
+{{ end }}{{ else }}*No manual checks match this pull request.*{{ end }}
-These requirements are defined in this [configuration file](https://github.com/gnolang/gno/tree/master/contribs/github-bot/internal/config/config.go).
+Read More
-## Automated Checks
+🤖 This bot helps streamline PR reviews by verifying automated checks and providing guidance for contributors and reviewers.
+##### ✅ Automated Checks (for Contributors):
{{ if .AutoRules }}{{ range .AutoRules }} {{ if .Satisfied }}🟢{{ else }}🔴{{ end }} {{ .Description }}
{{ end }}{{ else }}*No automated checks match this pull request.*{{ end }}
-## Manual Checks
+##### ☑️ Contributor Actions:
+1. Fix any issues flagged by automated checks.
+2. Follow the Contributor Checklist to ensure your PR is ready for review.
+ - Add new tests, or document why they are unnecessary.
+ - Provide clear examples/screenshots, if necessary.
+ - Update documentation, if required.
+ - Ensure no breaking changes, or include `BREAKING CHANGE` notes.
+ - Link related issues/PRs, where applicable.
-{{ if .ManualRules }}{{ range .ManualRules }}- [{{ if .CheckedBy }}x{{ else }} {{ end }}] {{ .Description }}{{ if .CheckedBy }} (checked by @{{ .CheckedBy }}){{ end }}
-{{ end }}{{ else }}*No manual checks match this pull request.*{{ end }}
+##### ☑️ Reviewer Actions:
+1. Complete manual checks for the PR, including the guidelines and additional checks if applicable.
+
+##### 📚 Resources:
+- [Report a bug with the bot](https://github.com/gnolang/gno/issues/3238).
+- [View the bot’s configuration file](https://github.com/gnolang/gno/tree/master/contribs/github-bot/internal/config/config.go).
{{ if or .AutoRules .ManualRules }}Debug
{{ if .AutoRules }}Automated Checks
@@ -52,3 +67,4 @@ These requirements are defined in this [configuration file](https://github.com/g
{{ end }}
{{ end }}
+
diff --git a/contribs/github-bot/internal/check/comment_test.go b/contribs/github-bot/internal/check/comment_test.go
index 0334b76f95c..29886f80f43 100644
--- a/contribs/github-bot/internal/check/comment_test.go
+++ b/contribs/github-bot/internal/check/comment_test.go
@@ -31,31 +31,44 @@ func TestGeneratedComment(t *testing.T) {
{Description: "Test automatic 5", Satisfied: false},
}
manualRules := []ManualContent{
- {Description: "Test manual 1", CheckedBy: "user_1"},
+ {Description: "Test manual 1", CheckedBy: "user-1"},
{Description: "Test manual 2", CheckedBy: ""},
{Description: "Test manual 3", CheckedBy: ""},
- {Description: "Test manual 4", CheckedBy: "user_4"},
- {Description: "Test manual 5", CheckedBy: "user_5"},
+ {Description: "Test manual 4", CheckedBy: "user-4"},
+ {Description: "Test manual 5", CheckedBy: "user-5"},
}
commentText, err := generateComment(content)
assert.Nil(t, err, fmt.Sprintf("error is not nil: %v", err))
assert.True(t, strings.Contains(commentText, "*No automated checks match this pull request.*"), "should contains automated check placeholder")
assert.True(t, strings.Contains(commentText, "*No manual checks match this pull request.*"), "should contains manual check placeholder")
+ assert.True(t, strings.Contains(commentText, "All **Automated Checks** passed. ✅"), "should contains automated checks passed placeholder")
content.AutoRules = autoRules
+ content.AutoAllSatisfied = true
commentText, err = generateComment(content)
assert.Nil(t, err, fmt.Sprintf("error is not nil: %v", err))
assert.False(t, strings.Contains(commentText, "*No automated checks match this pull request.*"), "should not contains automated check placeholder")
assert.True(t, strings.Contains(commentText, "*No manual checks match this pull request.*"), "should contains manual check placeholder")
+ assert.True(t, strings.Contains(commentText, "All **Automated Checks** passed. ✅"), "should contains automated checks passed placeholder")
assert.Equal(t, 2, len(autoCheckSuccessLine.FindAllStringSubmatch(commentText, -1)), "wrong number of succeeded automatic check")
assert.Equal(t, 3, len(autoCheckFailLine.FindAllStringSubmatch(commentText, -1)), "wrong number of failed automatic check")
+ content.AutoAllSatisfied = false
+ commentText, err = generateComment(content)
+ assert.Nil(t, err, fmt.Sprintf("error is not nil: %v", err))
+ assert.False(t, strings.Contains(commentText, "*No automated checks match this pull request.*"), "should not contains automated check placeholder")
+ assert.True(t, strings.Contains(commentText, "*No manual checks match this pull request.*"), "should contains manual check placeholder")
+ assert.False(t, strings.Contains(commentText, "All **Automated Checks** passed. ✅"), "should contains automated checks passed placeholder")
+ assert.Equal(t, 2, len(autoCheckSuccessLine.FindAllStringSubmatch(commentText, -1)), "wrong number of succeeded automatic check")
+ assert.Equal(t, 3+3, len(autoCheckFailLine.FindAllStringSubmatch(commentText, -1)), "wrong number of failed automatic check")
+
content.ManualRules = manualRules
commentText, err = generateComment(content)
assert.Nil(t, err, fmt.Sprintf("error is not nil: %v", err))
assert.False(t, strings.Contains(commentText, "*No automated checks match this pull request.*"), "should not contains automated check placeholder")
assert.False(t, strings.Contains(commentText, "*No manual checks match this pull request.*"), "should not contains manual check placeholder")
+ assert.False(t, strings.Contains(commentText, "All **Automated Checks** passed. ✅"), "should contains automated checks passed placeholder")
manualChecks := getCommentManualChecks(commentText)
assert.Equal(t, len(manualChecks), len(manualRules), "wrong number of manual checks found")
diff --git a/contribs/github-bot/internal/config/config.go b/contribs/github-bot/internal/config/config.go
index 2d595c7ce51..fd29f5e5f57 100644
--- a/contribs/github-bot/internal/config/config.go
+++ b/contribs/github-bot/internal/config/config.go
@@ -22,6 +22,10 @@ type ManualCheck struct {
Teams Teams // Members of these teams can check the checkbox to make the check pass.
}
+// This is the description for a persistent rule with a non-standard behavior
+// that allow maintainer to force the "success" state of the CI check
+const ForceSkipDescription = "**SKIP**: Do not block the CI for this PR"
+
// This function returns the configuration of the bot consisting of automatic and manual checks
// in which the GitHub client is injected.
func Config(gh *client.GitHub) ([]AutomaticCheck, []ManualCheck) {
@@ -53,6 +57,11 @@ func Config(gh *client.GitHub) ([]AutomaticCheck, []ManualCheck) {
}
manual := []ManualCheck{
+ {
+ // WARN: Do not edit this special rule which must remain persistent.
+ Description: ForceSkipDescription,
+ If: c.Always(),
+ },
{
Description: "The pull request description provides enough details",
If: c.Not(c.AuthorInTeam(gh, "core-contributors")),
From 4e7305b67ba23f079e33e73e31e0abd90c33d4b9 Mon Sep 17 00:00:00 2001
From: matijamarjanovic <93043005+matijamarjanovic@users.noreply.github.com>
Date: Tue, 10 Dec 2024 12:15:51 +0100
Subject: [PATCH 15/22] feat: add Matija's Homepage realm to examples (#2916)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
### Summary
This pull request adds a new realm example to the Gno `examples`
repository—Matija's Homepage. It showcases a personal homepage built on
the Gno chain where users can interact by voting with GNOT tokens to
change the page's color. The more tokens users send, the greater
influence they have on the color scheme, providing an interactive and
dynamic experience.
### Key Features
- **Profile Section**: Displays a personal profile with an image and
description.
- **Color Voting**: Users can vote for the page's color (red, green,
blue) by sending GNOT tokens. RGB values are adjusted based on the
amount sent.
- **Dynamic Updates**: The homepage dynamically updates the color based
on votes, showcasing real-time interaction on the Gno blockchain.
- **Links to GitHub and LinkedIn**: Includes buttons for GitHub and
LinkedIn, making it easy for users to connect.
### Tools & Technologies
- Utilizes Gno's native functions to handle voting and token transfers.
- Provides a simple, yet effective example of how personal realms can be
interactive and engaging on the Gno platform.
### Why this is valuable
This example highlights the possibilities of personal realms on Gno,
showing how users can create unique and interactive profiles. It’s a fun
and approachable entry point for anyone new to Gno development, while
also demonstrating the platform's flexibility and potential for creative
expression.
---------
Co-authored-by: Morgan
Co-authored-by: Leon Hudak <33522493+leohhhn@users.noreply.github.com>
---
.../r/matijamarjanovic/home/config.gno | 64 +++++
.../gno.land/r/matijamarjanovic/home/gno.mod | 1 +
.../gno.land/r/matijamarjanovic/home/home.gno | 238 ++++++++++++++++++
.../r/matijamarjanovic/home/home_test.gno | 134 ++++++++++
4 files changed, 437 insertions(+)
create mode 100644 examples/gno.land/r/matijamarjanovic/home/config.gno
create mode 100644 examples/gno.land/r/matijamarjanovic/home/gno.mod
create mode 100644 examples/gno.land/r/matijamarjanovic/home/home.gno
create mode 100644 examples/gno.land/r/matijamarjanovic/home/home_test.gno
diff --git a/examples/gno.land/r/matijamarjanovic/home/config.gno b/examples/gno.land/r/matijamarjanovic/home/config.gno
new file mode 100644
index 00000000000..2a9669c0b58
--- /dev/null
+++ b/examples/gno.land/r/matijamarjanovic/home/config.gno
@@ -0,0 +1,64 @@
+package home
+
+import (
+ "errors"
+ "std"
+)
+
+var (
+ mainAddr = std.Address("g1ej0qca5ptsw9kfr64ey8jvfy9eacga6mpj2z0y") // matija's main address
+ backupAddr std.Address // backup address
+
+ errorInvalidAddr = errors.New("config: invalid address")
+ errorUnauthorized = errors.New("config: unauthorized")
+)
+
+func Address() std.Address {
+ return mainAddr
+}
+
+func Backup() std.Address {
+ return backupAddr
+}
+
+func SetAddress(newAddress std.Address) error {
+ if !newAddress.IsValid() {
+ return errorInvalidAddr
+ }
+
+ if err := checkAuthorized(); err != nil {
+ return err
+ }
+
+ mainAddr = newAddress
+ return nil
+}
+
+func SetBackup(newAddress std.Address) error {
+ if !newAddress.IsValid() {
+ return errorInvalidAddr
+ }
+
+ if err := checkAuthorized(); err != nil {
+ return err
+ }
+
+ backupAddr = newAddress
+ return nil
+}
+
+func checkAuthorized() error {
+ caller := std.GetOrigCaller()
+ if caller != mainAddr && caller != backupAddr {
+ return errorUnauthorized
+ }
+
+ return nil
+}
+
+func AssertAuthorized() {
+ caller := std.GetOrigCaller()
+ if caller != mainAddr && caller != backupAddr {
+ panic(errorUnauthorized)
+ }
+}
diff --git a/examples/gno.land/r/matijamarjanovic/home/gno.mod b/examples/gno.land/r/matijamarjanovic/home/gno.mod
new file mode 100644
index 00000000000..0457c947c01
--- /dev/null
+++ b/examples/gno.land/r/matijamarjanovic/home/gno.mod
@@ -0,0 +1 @@
+module gno.land/r/matijamarjanovic/home
diff --git a/examples/gno.land/r/matijamarjanovic/home/home.gno b/examples/gno.land/r/matijamarjanovic/home/home.gno
new file mode 100644
index 00000000000..3757324108a
--- /dev/null
+++ b/examples/gno.land/r/matijamarjanovic/home/home.gno
@@ -0,0 +1,238 @@
+package home
+
+import (
+ "std"
+ "strings"
+
+ "gno.land/p/demo/ufmt"
+ "gno.land/p/moul/md"
+ "gno.land/r/leon/hof"
+)
+
+var (
+ pfp string // link to profile picture
+ pfpCaption string // profile picture caption
+ abtMe string
+
+ modernVotes int64
+ classicVotes int64
+ minimalVotes int64
+ currentTheme string
+
+ modernLink string
+ classicLink string
+ minimalLink string
+)
+
+func init() {
+ pfp = "https://static.artzone.ai/media/38734/conversions/IPF9dR7ro7n05CmMLLrXIojycr1qdLFxgutaaanG-w768.webp"
+ pfpCaption = "My profile picture - Tarantula Nebula"
+ abtMe = `Motivated Computer Science student with strong
+ analytical and problem-solving skills. Proficient in
+ programming and version control, with a high level of
+ focus and attention to detail. Eager to apply academic
+ knowledge to real-world projects and contribute to
+ innovative technology solutions.
+ In addition to my academic pursuits,
+ I enjoy traveling and staying active through weightlifting.
+ I have a keen interest in electronic music and often explore various genres.
+ I believe in maintaining a balanced lifestyle that complements my professional development.`
+
+ modernVotes = 0
+ classicVotes = 0
+ minimalVotes = 0
+ currentTheme = "classic"
+ modernLink = "https://www.google.com"
+ classicLink = "https://www.google.com"
+ minimalLink = "https://www.google.com"
+ hof.Register()
+}
+
+func UpdatePFP(url, caption string) {
+ AssertAuthorized()
+ pfp = url
+ pfpCaption = caption
+}
+
+func UpdateAboutMe(col1 string) {
+ AssertAuthorized()
+ abtMe = col1
+}
+
+func maxOfThree(a, b, c int64) int64 {
+ max := a
+ if b > max {
+ max = b
+ }
+ if c > max {
+ max = c
+ }
+ return max
+}
+
+func VoteModern() {
+ ugnotAmount := std.GetOrigSend().AmountOf("ugnot")
+ votes := ugnotAmount
+ modernVotes += votes
+ updateCurrentTheme()
+}
+
+func VoteClassic() {
+ ugnotAmount := std.GetOrigSend().AmountOf("ugnot")
+ votes := ugnotAmount
+ classicVotes += votes
+ updateCurrentTheme()
+}
+
+func VoteMinimal() {
+ ugnotAmount := std.GetOrigSend().AmountOf("ugnot")
+ votes := ugnotAmount
+ minimalVotes += votes
+ updateCurrentTheme()
+}
+
+func updateCurrentTheme() {
+ maxVotes := maxOfThree(modernVotes, classicVotes, minimalVotes)
+
+ if maxVotes == modernVotes {
+ currentTheme = "modern"
+ } else if maxVotes == classicVotes {
+ currentTheme = "classic"
+ } else {
+ currentTheme = "minimal"
+ }
+}
+
+func CollectBalance() {
+ AssertAuthorized()
+
+ banker := std.GetBanker(std.BankerTypeRealmSend)
+ ownerAddr := Address()
+
+ banker.SendCoins(std.CurrentRealm().Addr(), ownerAddr, banker.GetCoins(std.CurrentRealm().Addr()))
+}
+
+func Render(path string) string {
+ var sb strings.Builder
+
+ // Theme-specific header styling
+ switch currentTheme {
+ case "modern":
+ // Modern theme - Clean and minimalist with emojis
+ sb.WriteString(md.H1("🚀 Matija's Space"))
+ sb.WriteString(md.Image(pfpCaption, pfp))
+ sb.WriteString("\n")
+ sb.WriteString(md.Italic(pfpCaption))
+ sb.WriteString("\n")
+ sb.WriteString(md.HorizontalRule())
+ sb.WriteString(abtMe)
+ sb.WriteString("\n")
+
+ case "minimal":
+ // Minimal theme - No emojis, minimal formatting
+ sb.WriteString(md.H1("Matija Marjanovic"))
+ sb.WriteString("\n")
+ sb.WriteString(abtMe)
+ sb.WriteString("\n")
+ sb.WriteString(md.Image(pfpCaption, pfp))
+ sb.WriteString("\n")
+ sb.WriteString(pfpCaption)
+ sb.WriteString("\n")
+
+ default: // classic
+ // Classic theme - Traditional blog style with decorative elements
+ sb.WriteString(md.H1("✨ Welcome to Matija's Homepage ✨"))
+ sb.WriteString("\n")
+ sb.WriteString(md.Image(pfpCaption, pfp))
+ sb.WriteString("\n")
+ sb.WriteString(pfpCaption)
+ sb.WriteString("\n")
+ sb.WriteString(md.HorizontalRule())
+ sb.WriteString(md.H2("About me"))
+ sb.WriteString("\n")
+ sb.WriteString(abtMe)
+ sb.WriteString("\n")
+ }
+
+ // Theme-specific voting section
+ switch currentTheme {
+ case "modern":
+ sb.WriteString(md.HorizontalRule())
+ sb.WriteString(md.H2("🎨 Theme Selector"))
+ sb.WriteString("Choose your preferred viewing experience:\n")
+ items := []string{
+ md.Link(ufmt.Sprintf("Modern Design (%d votes)", modernVotes), modernLink),
+ md.Link(ufmt.Sprintf("Classic Style (%d votes)", classicVotes), classicLink),
+ md.Link(ufmt.Sprintf("Minimal Look (%d votes)", minimalVotes), minimalLink),
+ }
+ sb.WriteString(md.BulletList(items))
+
+ case "minimal":
+ sb.WriteString("\n")
+ sb.WriteString(md.H3("Theme Selection"))
+ sb.WriteString(ufmt.Sprintf("Current theme: %s\n", currentTheme))
+ sb.WriteString(ufmt.Sprintf("Votes - Modern: %d | Classic: %d | Minimal: %d\n",
+ modernVotes, classicVotes, minimalVotes))
+ sb.WriteString(md.Link("Modern", modernLink))
+ sb.WriteString(" | ")
+ sb.WriteString(md.Link("Classic", classicLink))
+ sb.WriteString(" | ")
+ sb.WriteString(md.Link("Minimal", minimalLink))
+ sb.WriteString("\n")
+
+ default: // classic
+ sb.WriteString(md.HorizontalRule())
+ sb.WriteString(md.H2("✨ Theme Customization ✨"))
+ sb.WriteString(md.Bold("Choose Your Preferred Theme:"))
+ sb.WriteString("\n\n")
+ items := []string{
+ ufmt.Sprintf("Modern 🚀 (%d votes) - %s", modernVotes, md.Link("Vote", modernLink)),
+ ufmt.Sprintf("Classic ✨ (%d votes) - %s", classicVotes, md.Link("Vote", classicLink)),
+ ufmt.Sprintf("Minimal ⚡ (%d votes) - %s", minimalVotes, md.Link("Vote", minimalLink)),
+ }
+ sb.WriteString(md.BulletList(items))
+ }
+
+ // Theme-specific footer/links section
+ switch currentTheme {
+ case "modern":
+ sb.WriteString(md.HorizontalRule())
+ sb.WriteString(md.Link("GitHub", "https://github.com/matijamarjanovic"))
+ sb.WriteString(" | ")
+ sb.WriteString(md.Link("LinkedIn", "https://www.linkedin.com/in/matijamarjanovic"))
+ sb.WriteString("\n")
+
+ case "minimal":
+ sb.WriteString("\n")
+ sb.WriteString(md.Link("GitHub", "https://github.com/matijamarjanovic"))
+ sb.WriteString(" | ")
+ sb.WriteString(md.Link("LinkedIn", "https://www.linkedin.com/in/matijamarjanovic"))
+ sb.WriteString("\n")
+
+ default: // classic
+ sb.WriteString(md.HorizontalRule())
+ sb.WriteString(md.H3("✨ Connect With Me"))
+ items := []string{
+ md.Link("🌟 GitHub", "https://github.com/matijamarjanovic"),
+ md.Link("💼 LinkedIn", "https://www.linkedin.com/in/matijamarjanovic"),
+ }
+ sb.WriteString(md.BulletList(items))
+ }
+
+ return sb.String()
+}
+
+func UpdateModernLink(link string) {
+ AssertAuthorized()
+ modernLink = link
+}
+
+func UpdateClassicLink(link string) {
+ AssertAuthorized()
+ classicLink = link
+}
+
+func UpdateMinimalLink(link string) {
+ AssertAuthorized()
+ minimalLink = link
+}
diff --git a/examples/gno.land/r/matijamarjanovic/home/home_test.gno b/examples/gno.land/r/matijamarjanovic/home/home_test.gno
new file mode 100644
index 00000000000..8cc6e6e5608
--- /dev/null
+++ b/examples/gno.land/r/matijamarjanovic/home/home_test.gno
@@ -0,0 +1,134 @@
+package home
+
+import (
+ "std"
+ "strings"
+ "testing"
+
+ "gno.land/p/demo/uassert"
+ "gno.land/p/demo/urequire"
+)
+
+// Helper function to set up test environment
+func setupTest() {
+ std.TestSetOrigCaller(std.Address("g1ej0qca5ptsw9kfr64ey8jvfy9eacga6mpj2z0y"))
+}
+
+func TestUpdatePFP(t *testing.T) {
+ setupTest()
+ pfp = ""
+ pfpCaption = ""
+
+ UpdatePFP("https://example.com/pic.png", "New Caption")
+
+ urequire.Equal(t, pfp, "https://example.com/pic.png", "Profile picture URL should be updated")
+ urequire.Equal(t, pfpCaption, "New Caption", "Profile picture caption should be updated")
+}
+
+func TestUpdateAboutMe(t *testing.T) {
+ setupTest()
+ abtMe = ""
+
+ UpdateAboutMe("This is my new bio.")
+
+ urequire.Equal(t, abtMe, "This is my new bio.", "About Me should be updated")
+}
+
+func TestVoteModern(t *testing.T) {
+ setupTest()
+ modernVotes, classicVotes, minimalVotes = 0, 0, 0
+
+ coinsSent := std.NewCoins(std.NewCoin("ugnot", 75000000))
+ coinsSpent := std.NewCoins(std.NewCoin("ugnot", 1))
+
+ std.TestSetOrigSend(coinsSent, coinsSpent)
+ VoteModern()
+
+ uassert.Equal(t, int64(75000000), modernVotes, "Modern votes should be calculated correctly")
+ uassert.Equal(t, "modern", currentTheme, "Theme should be updated to modern")
+}
+
+func TestVoteClassic(t *testing.T) {
+ setupTest()
+ modernVotes, classicVotes, minimalVotes = 0, 0, 0
+
+ coinsSent := std.NewCoins(std.NewCoin("ugnot", 75000000))
+ coinsSpent := std.NewCoins(std.NewCoin("ugnot", 1))
+
+ std.TestSetOrigSend(coinsSent, coinsSpent)
+ VoteClassic()
+
+ uassert.Equal(t, int64(75000000), classicVotes, "Classic votes should be calculated correctly")
+ uassert.Equal(t, "classic", currentTheme, "Theme should be updated to classic")
+}
+
+func TestVoteMinimal(t *testing.T) {
+ setupTest()
+ modernVotes, classicVotes, minimalVotes = 0, 0, 0
+
+ coinsSent := std.NewCoins(std.NewCoin("ugnot", 75000000))
+ coinsSpent := std.NewCoins(std.NewCoin("ugnot", 1))
+
+ std.TestSetOrigSend(coinsSent, coinsSpent)
+ VoteMinimal()
+
+ uassert.Equal(t, int64(75000000), minimalVotes, "Minimal votes should be calculated correctly")
+ uassert.Equal(t, "minimal", currentTheme, "Theme should be updated to minimal")
+}
+
+func TestRender(t *testing.T) {
+ setupTest()
+ // Reset the state to known values
+ modernVotes, classicVotes, minimalVotes = 0, 0, 0
+ currentTheme = "classic"
+ pfp = "https://example.com/pic.png"
+ pfpCaption = "Test Caption"
+ abtMe = "Test About Me"
+
+ out := Render("")
+ urequire.NotEqual(t, out, "", "Render output should not be empty")
+
+ // Test classic theme specific content
+ uassert.True(t, strings.Contains(out, "✨ Welcome to Matija's Homepage ✨"), "Classic theme should have correct header")
+ uassert.True(t, strings.Contains(out, pfp), "Should contain profile picture URL")
+ uassert.True(t, strings.Contains(out, pfpCaption), "Should contain profile picture caption")
+ uassert.True(t, strings.Contains(out, "About me"), "Should contain About me section")
+ uassert.True(t, strings.Contains(out, abtMe), "Should contain about me content")
+ uassert.True(t, strings.Contains(out, "Theme Customization"), "Should contain theme customization section")
+ uassert.True(t, strings.Contains(out, "Connect With Me"), "Should contain connect section")
+}
+
+func TestRenderModernTheme(t *testing.T) {
+ setupTest()
+ modernVotes, classicVotes, minimalVotes = 100, 0, 0
+ currentTheme = "modern"
+ updateCurrentTheme()
+
+ out := Render("")
+ uassert.True(t, strings.Contains(out, "🚀 Matija's Space"), "Modern theme should have correct header")
+}
+
+func TestRenderMinimalTheme(t *testing.T) {
+ setupTest()
+ modernVotes, classicVotes, minimalVotes = 0, 0, 100
+ currentTheme = "minimal"
+ updateCurrentTheme()
+
+ out := Render("")
+ uassert.True(t, strings.Contains(out, "Matija Marjanovic"), "Minimal theme should have correct header")
+}
+
+func TestUpdateLinks(t *testing.T) {
+ setupTest()
+
+ newLink := "https://example.com/vote"
+
+ UpdateModernLink(newLink)
+ urequire.Equal(t, modernLink, newLink, "Modern link should be updated")
+
+ UpdateClassicLink(newLink)
+ urequire.Equal(t, classicLink, newLink, "Classic link should be updated")
+
+ UpdateMinimalLink(newLink)
+ urequire.Equal(t, minimalLink, newLink, "Minimal link should be updated")
+}
From 5c31552b05c78575f45876ab07abd73c1d96bf27 Mon Sep 17 00:00:00 2001
From: ltzmaxwell
Date: Tue, 10 Dec 2024 21:50:53 +0800
Subject: [PATCH 16/22] fix(gnovm): make static-analysis handle block stmt
(#3313)
as the title says.
it give incorrect error before fix:
```
unexpected panic: main/files/block0.gno:3:1: [function "foo" does not terminate]
```
---
gnovm/pkg/gnolang/static_analysis.go | 2 ++
gnovm/tests/files/block0.gno | 14 ++++++++++++++
2 files changed, 16 insertions(+)
create mode 100644 gnovm/tests/files/block0.gno
diff --git a/gnovm/pkg/gnolang/static_analysis.go b/gnovm/pkg/gnolang/static_analysis.go
index 311a0d42feb..7094ccbb4c8 100644
--- a/gnovm/pkg/gnolang/static_analysis.go
+++ b/gnovm/pkg/gnolang/static_analysis.go
@@ -108,6 +108,8 @@ func (s *staticAnalysis) staticAnalysisExpr(expr Expr) bool {
// indicating whether a statement is terminating or not
func (s *staticAnalysis) staticAnalysisStmt(stmt Stmt) bool {
switch n := stmt.(type) {
+ case *BlockStmt:
+ return s.staticAnalysisBlockStmt(n.Body)
case *BranchStmt:
switch n.Op {
case BREAK:
diff --git a/gnovm/tests/files/block0.gno b/gnovm/tests/files/block0.gno
new file mode 100644
index 00000000000..b6d554ce500
--- /dev/null
+++ b/gnovm/tests/files/block0.gno
@@ -0,0 +1,14 @@
+package main
+
+func foo() int {
+ {
+ return 1
+ }
+}
+
+func main() {
+ println(foo())
+}
+
+// Output:
+// 1
From c33cf676daed03a29ca85f7386daf98e35d2b38f Mon Sep 17 00:00:00 2001
From: piux2 <90544084+piux2@users.noreply.github.com>
Date: Tue, 10 Dec 2024 08:20:52 -0800
Subject: [PATCH 17/22] fix: catch the out of gas exception in preprocess
(#2638)
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).
---------
Co-authored-by: Morgan
---
.../gnoland/testdata/addpkg_outofgas.txtar | 57 +++++++++++++++++++
gnovm/pkg/gnolang/preprocess.go | 7 +++
2 files changed, 64 insertions(+)
create mode 100644 gno.land/cmd/gnoland/testdata/addpkg_outofgas.txtar
diff --git a/gno.land/cmd/gnoland/testdata/addpkg_outofgas.txtar b/gno.land/cmd/gnoland/testdata/addpkg_outofgas.txtar
new file mode 100644
index 00000000000..56050f4733b
--- /dev/null
+++ b/gno.land/cmd/gnoland/testdata/addpkg_outofgas.txtar
@@ -0,0 +1,57 @@
+# ensure users get proper out of gas errors when they add packages
+
+# start a new node
+gnoland start
+
+# add foo package
+gnokey maketx addpkg -pkgdir $WORK/foo -pkgpath gno.land/r/foo -gas-fee 1000000ugnot -gas-wanted 220000 -broadcast -chainid=tendermint_test test1
+
+
+# add bar package
+# out of gas at store.GetPackage() with gas 60000
+
+! gnokey maketx addpkg -pkgdir $WORK/bar -pkgpath gno.land/r/bar -gas-fee 1000000ugnot -gas-wanted 60000 -broadcast -chainid=tendermint_test test1
+
+# Out of gas error
+
+stderr '--= Error =--'
+stderr 'Data: out of gas error'
+stderr 'Msg Traces:'
+stderr 'out of gas.*?in preprocess'
+stderr '--= /Error =--'
+
+
+
+# out of gas at store.store.GetTypeSafe() with gas 63000
+
+! gnokey maketx addpkg -pkgdir $WORK/bar -pkgpath gno.land/r/bar -gas-fee 1000000ugnot -gas-wanted 63000 -broadcast -chainid=tendermint_test test1
+
+stderr '--= Error =--'
+stderr 'Data: out of gas error'
+stderr 'Msg Traces:'
+stderr 'out of gas.*?in preprocess'
+stderr '--= /Error =--'
+
+
+-- foo/foo.gno --
+package foo
+
+type Counter int
+
+func Inc(i Counter) Counter{
+ i = i+1
+ return i
+}
+
+-- bar/bar.gno --
+package bar
+
+import "gno.land/r/foo"
+
+type NewCounter foo.Counter
+
+func Add2(i NewCounter) NewCounter{
+ i=i+2
+
+ return i
+}
diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go
index a3e498710bb..15f268f6321 100644
--- a/gnovm/pkg/gnolang/preprocess.go
+++ b/gnovm/pkg/gnolang/preprocess.go
@@ -10,6 +10,7 @@ import (
"sync/atomic"
"github.com/gnolang/gno/tm2/pkg/errors"
+ tmstore "github.com/gnolang/gno/tm2/pkg/store"
)
const (
@@ -365,6 +366,12 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) {
func doRecover(stack []BlockNode, n Node) {
if r := recover(); r != nil {
+ // Catch the out-of-gas exception and throw it
+ if exp, ok := r.(tmstore.OutOfGasException); ok {
+ exp.Descriptor = fmt.Sprintf("in preprocess: %v", r)
+ panic(exp)
+ }
+
if _, ok := r.(*PreprocessError); ok {
// re-panic directly if this is a PreprocessError already.
panic(r)
From 7185cefe2e091cb1795aef1d3d4c4d938a81778c Mon Sep 17 00:00:00 2001
From: Morgan
Date: Wed, 11 Dec 2024 09:05:18 +0100
Subject: [PATCH 18/22] fix(gnovm): in op_binary, return typed booleans where
appropriate (#3298)
bool8.gno was failing, because the result of the `==` expression is an
untyped boolean, while the first value is a typed boolean. This PR
ensures that if either of the values in a binary expression is typed, we
return a typed bool instead of an untyped bool.
---------
Co-authored-by: ltzmaxwell
---
gnovm/pkg/gnolang/preprocess.go | 4 ++++
gnovm/tests/files/bool8.gno | 17 +++++++++++++++++
2 files changed, 21 insertions(+)
create mode 100644 gnovm/tests/files/bool8.gno
diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go
index 15f268f6321..4ff182670cd 100644
--- a/gnovm/pkg/gnolang/preprocess.go
+++ b/gnovm/pkg/gnolang/preprocess.go
@@ -3574,6 +3574,10 @@ func checkOrConvertType(store Store, last BlockNode, n Node, x *Expr, t Type, au
checkOrConvertType(store, last, n, &bx.Left, rt, autoNative)
checkOrConvertType(store, last, n, &bx.Right, rt, autoNative)
}
+ // this is not a constant expression; the result here should
+ // always be a BoolType. (in this scenario, we may have some
+ // UntypedBoolTypes)
+ t = BoolType
default:
// do nothing
}
diff --git a/gnovm/tests/files/bool8.gno b/gnovm/tests/files/bool8.gno
new file mode 100644
index 00000000000..9efbbbe6da2
--- /dev/null
+++ b/gnovm/tests/files/bool8.gno
@@ -0,0 +1,17 @@
+package main
+
+// results from comparisons should not be untyped bools
+
+var a interface{} = true
+
+func main() {
+ buf := "hello="
+ isEqual(a, (buf[len(buf)-1] == '='))
+}
+
+func isEqual(v1, v2 interface{}) {
+ println("v1 == v2", v1 == v2)
+}
+
+// Output:
+// v1 == v2 true
From 6f48a5b6eb26e6dcb7cd2797c57f4545cb02f744 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jer=C3=B3nimo=20Albi?=
Date: Wed, 11 Dec 2024 11:42:06 +0100
Subject: [PATCH 19/22] feat: generic datasource package (#3318)
The new package is a generic implementation for datasources. It aims to
be one possible solution to integrate/aggregate data from different
realms.
---
.../p/jeronimoalbi/datasource/datasource.gno | 103 +++++++++++
.../datasource/datasource_test.gno | 171 ++++++++++++++++++
.../p/jeronimoalbi/datasource/gno.mod | 1 +
.../p/jeronimoalbi/datasource/query.gno | 70 +++++++
.../p/jeronimoalbi/datasource/query_test.gno | 104 +++++++++++
5 files changed, 449 insertions(+)
create mode 100644 examples/gno.land/p/jeronimoalbi/datasource/datasource.gno
create mode 100644 examples/gno.land/p/jeronimoalbi/datasource/datasource_test.gno
create mode 100644 examples/gno.land/p/jeronimoalbi/datasource/gno.mod
create mode 100644 examples/gno.land/p/jeronimoalbi/datasource/query.gno
create mode 100644 examples/gno.land/p/jeronimoalbi/datasource/query_test.gno
diff --git a/examples/gno.land/p/jeronimoalbi/datasource/datasource.gno b/examples/gno.land/p/jeronimoalbi/datasource/datasource.gno
new file mode 100644
index 00000000000..bf80964a9a0
--- /dev/null
+++ b/examples/gno.land/p/jeronimoalbi/datasource/datasource.gno
@@ -0,0 +1,103 @@
+// Package datasource defines generic interfaces for datasources.
+//
+// Datasources contain a set of records which can optionally be
+// taggable. Tags can optionally be used to filter records by taxonomy.
+//
+// Datasources can help in cases where the data sent during
+// communication between different realms needs to be generic
+// to avoid direct dependencies.
+package datasource
+
+import "errors"
+
+// ErrInvalidRecord indicates that a datasource contains invalid records.
+var ErrInvalidRecord = errors.New("datasource records is not valid")
+
+type (
+ // Fields defines an interface for read-only fields.
+ Fields interface {
+ // Has checks whether a field exists.
+ Has(name string) bool
+
+ // Get retrieves the value associated with the given field.
+ Get(name string) (value interface{}, found bool)
+ }
+
+ // Record defines a datasource record.
+ Record interface {
+ // ID returns the unique record's identifier.
+ ID() string
+
+ // String returns a string representation of the record.
+ String() string
+
+ // Fields returns record fields and values.
+ Fields() (Fields, error)
+ }
+
+ // TaggableRecord defines a datasource record that supports tags.
+ // Tags can be used to build a taxonomy to filter records by category.
+ TaggableRecord interface {
+ // Tags returns a list of tags for the record.
+ Tags() []string
+ }
+
+ // ContentRecord defines a datasource record that can return content.
+ ContentRecord interface {
+ // Content returns the record content.
+ Content() (string, error)
+ }
+
+ // Iterator defines an iterator of datasource records.
+ Iterator interface {
+ // Next returns true when a new record is available.
+ Next() bool
+
+ // Err returns any error raised when reading records.
+ Err() error
+
+ // Record returns the current record.
+ Record() Record
+ }
+
+ // Datasource defines a generic datasource.
+ Datasource interface {
+ // Records returns a new datasource records iterator.
+ Records(Query) Iterator
+
+ // Size returns the total number of records in the datasource.
+ // When -1 is returned it means datasource doesn't support size.
+ Size() int
+
+ // Record returns a single datasource record.
+ Record(id string) (Record, error)
+ }
+)
+
+// NewIterator returns a new record iterator for a datasource query.
+func NewIterator(ds Datasource, options ...QueryOption) Iterator {
+ return ds.Records(NewQuery(options...))
+}
+
+// QueryRecords return a slice of records for a datasource query.
+func QueryRecords(ds Datasource, options ...QueryOption) ([]Record, error) {
+ var (
+ records []Record
+ query = NewQuery(options...)
+ iter = ds.Records(query)
+ )
+
+ for i := 0; i < query.Count && iter.Next(); i++ {
+ r := iter.Record()
+ if r == nil {
+ return nil, ErrInvalidRecord
+ }
+
+ records = append(records, r)
+ }
+
+ if err := iter.Err(); err != nil {
+ return nil, err
+ }
+ return records, nil
+}
diff --git a/examples/gno.land/p/jeronimoalbi/datasource/datasource_test.gno b/examples/gno.land/p/jeronimoalbi/datasource/datasource_test.gno
new file mode 100644
index 00000000000..304a311ced7
--- /dev/null
+++ b/examples/gno.land/p/jeronimoalbi/datasource/datasource_test.gno
@@ -0,0 +1,171 @@
+package datasource
+
+import (
+ "errors"
+ "testing"
+
+ "gno.land/p/demo/uassert"
+ "gno.land/p/demo/urequire"
+)
+
+func TestNewIterator(t *testing.T) {
+ cases := []struct {
+ name string
+ records []Record
+ err error
+ }{
+ {
+ name: "ok",
+ records: []Record{
+ testRecord{id: "1"},
+ testRecord{id: "2"},
+ testRecord{id: "3"},
+ },
+ },
+ {
+ name: "error",
+ err: errors.New("test"),
+ },
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.name, func(t *testing.T) {
+ // Arrange
+ ds := testDatasource{
+ records: tc.records,
+ err: tc.err,
+ }
+
+ // Act
+ iter := NewIterator(ds)
+
+ // Assert
+ if tc.err != nil {
+ uassert.ErrorIs(t, tc.err, iter.Err())
+ return
+ }
+
+ uassert.NoError(t, iter.Err())
+
+ for i := 0; iter.Next(); i++ {
+ r := iter.Record()
+ urequire.NotEqual(t, nil, r, "valid record")
+ urequire.True(t, i < len(tc.records), "iteration count")
+ uassert.Equal(t, tc.records[i].ID(), r.ID())
+ }
+ })
+ }
+}
+
+func TestQueryRecords(t *testing.T) {
+ cases := []struct {
+ name string
+ records []Record
+ recordCount int
+ options []QueryOption
+ err error
+ }{
+ {
+ name: "ok",
+ records: []Record{
+ testRecord{id: "1"},
+ testRecord{id: "2"},
+ testRecord{id: "3"},
+ },
+ recordCount: 3,
+ },
+ {
+ name: "with count",
+ options: []QueryOption{WithCount(2)},
+ records: []Record{
+ testRecord{id: "1"},
+ testRecord{id: "2"},
+ testRecord{id: "3"},
+ },
+ recordCount: 2,
+ },
+ {
+ name: "invalid record",
+ records: []Record{
+ testRecord{id: "1"},
+ nil,
+ testRecord{id: "3"},
+ },
+ err: ErrInvalidRecord,
+ },
+ {
+ name: "iterator error",
+ records: []Record{
+ testRecord{id: "1"},
+ testRecord{id: "3"},
+ },
+ err: errors.New("test"),
+ },
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.name, func(t *testing.T) {
+ // Arrange
+ ds := testDatasource{
+ records: tc.records,
+ err: tc.err,
+ }
+
+ // Act
+ records, err := QueryRecords(ds, tc.options...)
+
+ // Assert
+ if tc.err != nil {
+ uassert.ErrorIs(t, tc.err, err)
+ return
+ }
+
+ uassert.NoError(t, err)
+
+ urequire.Equal(t, tc.recordCount, len(records), "record count")
+ for i, r := range records {
+ urequire.NotEqual(t, nil, r, "valid record")
+ uassert.Equal(t, tc.records[i].ID(), r.ID())
+ }
+ })
+ }
+}
+
+type testDatasource struct {
+ records []Record
+ err error
+}
+
+func (testDatasource) Size() int { return -1 }
+func (testDatasource) Record(string) (Record, error) { return nil, nil }
+func (ds testDatasource) Records(Query) Iterator { return &testIter{records: ds.records, err: ds.err} }
+
+type testRecord struct {
+ id string
+ fields Fields
+ err error
+}
+
+func (r testRecord) ID() string { return r.id }
+func (r testRecord) String() string { return "str" + r.id }
+func (r testRecord) Fields() (Fields, error) { return r.fields, r.err }
+
+type testIter struct {
+ index int
+ records []Record
+ current Record
+ err error
+}
+
+func (it testIter) Err() error { return it.err }
+func (it testIter) Record() Record { return it.current }
+
+func (it *testIter) Next() bool {
+ count := len(it.records)
+ if it.err != nil || count == 0 || it.index >= count {
+ return false
+ }
+ it.current = it.records[it.index]
+ it.index++
+ return true
+}
diff --git a/examples/gno.land/p/jeronimoalbi/datasource/gno.mod b/examples/gno.land/p/jeronimoalbi/datasource/gno.mod
new file mode 100644
index 00000000000..3b398971b41
--- /dev/null
+++ b/examples/gno.land/p/jeronimoalbi/datasource/gno.mod
@@ -0,0 +1 @@
+module gno.land/p/jeronimoalbi/datasource
diff --git a/examples/gno.land/p/jeronimoalbi/datasource/query.gno b/examples/gno.land/p/jeronimoalbi/datasource/query.gno
new file mode 100644
index 00000000000..f971f9c64db
--- /dev/null
+++ b/examples/gno.land/p/jeronimoalbi/datasource/query.gno
@@ -0,0 +1,70 @@
+package datasource
+
+import "gno.land/p/demo/avl"
+
+// DefaultQueryRecords defines the default number of records returned by queries.
+const DefaultQueryRecords = 50
+
+var defaultQuery = Query{Count: DefaultQueryRecords}
+
+type (
+ // QueryOption configures datasource queries.
+ QueryOption func(*Query)
+
+ // Query contains datasource query options.
+ Query struct {
+ // Offset of the first record to return during iteration.
+ Offset int
+
+ // Count contains the number to records that query should return.
+ Count int
+
+ // Tag contains a tag to use as filter for the records.
+ Tag string
+
+ // Filters contains optional query filters by field value.
+ Filters avl.Tree
+ }
+)
+
+// WithOffset configures query to return records starting from an offset.
+func WithOffset(offset int) QueryOption {
+ return func(q *Query) {
+ q.Offset = offset
+ }
+}
+
+// WithCount configures the number of records that query returns.
+func WithCount(count int) QueryOption {
+ return func(q *Query) {
+ if count < 1 {
+ count = DefaultQueryRecords
+ }
+ q.Count = count
+ }
+}
+
+// ByTag configures query to filter by tag.
+func ByTag(tag string) QueryOption {
+ return func(q *Query) {
+ q.Tag = tag
+ }
+}
+
+// WithFilter assigns a new filter argument to a query.
+// This option can be used multiple times if more than one
+// filter has to be given to the query.
+func WithFilter(field string, value interface{}) QueryOption {
+ return func(q *Query) {
+ q.Filters.Set(field, value)
+ }
+}
+
+// NewQuery creates a new datasource query.
+func NewQuery(options ...QueryOption) Query {
+ q := defaultQuery
+ for _, apply := range options {
+ apply(&q)
+ }
+ return q
+}
diff --git a/examples/gno.land/p/jeronimoalbi/datasource/query_test.gno b/examples/gno.land/p/jeronimoalbi/datasource/query_test.gno
new file mode 100644
index 00000000000..6f78d41bb35
--- /dev/null
+++ b/examples/gno.land/p/jeronimoalbi/datasource/query_test.gno
@@ -0,0 +1,104 @@
+package datasource
+
+import (
+ "fmt"
+ "testing"
+
+ "gno.land/p/demo/uassert"
+)
+
+func TestNewQuery(t *testing.T) {
+ cases := []struct {
+ name string
+ options []QueryOption
+ setup func() Query
+ }{
+ {
+ name: "default",
+ setup: func() Query {
+ return Query{Count: DefaultQueryRecords}
+ },
+ },
+ {
+ name: "with offset",
+ options: []QueryOption{WithOffset(100)},
+ setup: func() Query {
+ return Query{
+ Offset: 100,
+ Count: DefaultQueryRecords,
+ }
+ },
+ },
+ {
+ name: "with count",
+ options: []QueryOption{WithCount(10)},
+ setup: func() Query {
+ return Query{Count: 10}
+ },
+ },
+ {
+ name: "with invalid count",
+ options: []QueryOption{WithCount(0)},
+ setup: func() Query {
+ return Query{Count: DefaultQueryRecords}
+ },
+ },
+ {
+ name: "by tag",
+ options: []QueryOption{ByTag("foo")},
+ setup: func() Query {
+ return Query{
+ Tag: "foo",
+ Count: DefaultQueryRecords,
+ }
+ },
+ },
+ {
+ name: "with filter",
+ options: []QueryOption{WithFilter("foo", 42)},
+ setup: func() Query {
+ q := Query{Count: DefaultQueryRecords}
+ q.Filters.Set("foo", 42)
+ return q
+ },
+ },
+ {
+ name: "with multiple filters",
+ options: []QueryOption{
+ WithFilter("foo", 42),
+ WithFilter("bar", "baz"),
+ },
+ setup: func() Query {
+ q := Query{Count: DefaultQueryRecords}
+ q.Filters.Set("foo", 42)
+ q.Filters.Set("bar", "baz")
+ return q
+ },
+ },
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.name, func(t *testing.T) {
+ // Arrange
+ want := tc.setup()
+
+ // Act
+ q := NewQuery(tc.options...)
+
+ // Assert
+ uassert.Equal(t, want.Offset, q.Offset)
+ uassert.Equal(t, want.Count, q.Count)
+ uassert.Equal(t, want.Tag, q.Tag)
+ uassert.Equal(t, want.Filters.Size(), q.Filters.Size())
+
+ want.Filters.Iterate("", "", func(k string, v interface{}) bool {
+ got, exists := q.Filters.Get(k)
+ uassert.True(t, exists)
+ if exists {
+ uassert.Equal(t, fmt.Sprint(v), fmt.Sprint(got))
+ }
+ return false
+ })
+ })
+ }
+}
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 20/22] 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 21/22] 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,
From c48219a1f3782d318e7753e1df7f1da0c5fd10c6 Mon Sep 17 00:00:00 2001
From: Nemanja Aleksic
Date: Fri, 13 Dec 2024 07:12:06 +0100
Subject: [PATCH 22/22] docs: add bad contribution section (#3329)
Co-authored-by: Nathan Toups <612924+n2p5@users.noreply.github.com>
Co-authored-by: Morgan
---
CONTRIBUTING.md | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index bc125a6da73..b58d63c6c75 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -469,6 +469,18 @@ Resources for idiomatic Go docs:
- [godoc](https://go.dev/blog/godoc)
- [Go Doc Comments](https://tip.golang.org/doc/comment)
+## Avoding Unhelpful Contributions
+
+While we welcome all contributions to the Gno project, it's important to ensure that your changes provide meaningful value or improve the quality of the codebase. Contributions that fail to meet these criteria may not be accepted. Examples of unhelpful contributions include (but not limited to):
+
+- Airdrop farming & karma farming: Making minimal, superficial changes, with the goal of becoming eligible for airdrops and GovDAO participation.
+- Incomplete submissions: Changes that lack adequate context, link to a related issue, documentation, or test coverage.
+
+Before submitting a pull request, ask yourself:
+- Does this change solve a specific problem or add clear value?
+- Is the implementation aligned with the gno.land's goals and style guide?
+- Have I tested my changes and included relevant documentation?
+
## Additional Notes
### Issue and Pull Request Labels
@@ -502,3 +514,4 @@ automatic label management.
| info needed | Issue is lacking information needed for resolving |
| investigating | Issue is still being investigated by the team |
| question | Issue starts a discussion or raises a question |
+