diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3870ff30539..bafaa70301b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -51,7 +51,7 @@ /gno.land/cmd/genesis/ @zivkovicmilos /gno.land/cmd/gnokey/ @jaekwon @moul @gfanton /gno.land/cmd/gnoland/ @zivkovicmilos @gnolang/devops -/gno.land/cmd/gnoweb/ @gfanton @thehowl +/gno.land/cmd/gnoweb/ @gfanton @thehowl @alexiscolin /gno.land/pkg/gnoclient/ @zivkovicmilos @leohhhn @gfanton /gno.land/pkg/gnoland/ @zivkovicmilos @gfanton /gno.land/pkg/keyscli/ @jaekwon @moul @gfanton @@ -64,9 +64,9 @@ # GnoVM/Gnolang. /gnovm/ @jaekwon @moul @piux2 @thehowl /gnovm/stdlibs/ @thehowl -/gnovm/tests/ @jaekwon @deelawn @thehowl @mvertes +/gnovm/tests/ @jaekwon @thehowl @mvertes /gnovm/cmd/gno/ @moul @thehowl -/gnovm/pkg/gnolang/ @jaekwon @moul @piux2 @deelawn +/gnovm/pkg/gnolang/ @jaekwon @moul @piux2 /gnovm/pkg/doc/ @thehowl /gnovm/pkg/repl/ @mvertes @ajnavarro /gnovm/pkg/gnomod/ @thehowl diff --git a/.github/codecov.yml b/.github/codecov.yml index ea1c701d946..d1ecba7ade3 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -39,3 +39,8 @@ flag_management: - type: patch target: auto # Let's decrease this later. threshold: 10 + +ignore: + - "gnovm/stdlibs/generated.go" + - "gnovm/tests/stdlibs/generated.go" + - "**/*.pb.go" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index d76f68cba5d..12e07a9cde6 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -8,5 +8,4 @@ - [ ] 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). diff --git a/.github/workflows/benchmark-master-push.yml b/.github/workflows/benchmark-master-push.yml index a219a49305a..09978a0ae5c 100644 --- a/.github/workflows/benchmark-master-push.yml +++ b/.github/workflows/benchmark-master-push.yml @@ -4,6 +4,11 @@ on: push: branches: - master + paths: + - contribs/** + - gno.land/** + - gnovm/** + - tm2/** permissions: # deployments permission to deploy GitHub pages website diff --git a/.github/workflows/misc.yml b/.github/workflows/misc.yml index b824235ca2b..859e1429983 100644 --- a/.github/workflows/misc.yml +++ b/.github/workflows/misc.yml @@ -22,7 +22,6 @@ jobs: - genproto - genstd - goscan - - logos - loop name: Run Main uses: ./.github/workflows/main_template.yml diff --git a/.github/workflows/nightlies.yml b/.github/workflows/nightlies.yml index e8f3fe4ca5c..99f9a406ed1 100644 --- a/.github/workflows/nightlies.yml +++ b/.github/workflows/nightlies.yml @@ -23,8 +23,8 @@ jobs: go-version: "1.22.x" cache: true - - uses: sigstore/cosign-installer@v3.6.0 - - uses: anchore/sbom-action/download-syft@v0.17.2 + - uses: sigstore/cosign-installer@v3.7.0 + - uses: anchore/sbom-action/download-syft@v0.17.5 - uses: docker/login-action@v3 with: diff --git a/.github/workflows/releaser-master.yml b/.github/workflows/releaser-master.yml index 7eda0536532..59dc2ec8705 100644 --- a/.github/workflows/releaser-master.yml +++ b/.github/workflows/releaser-master.yml @@ -24,8 +24,8 @@ jobs: go-version: "1.22.x" cache: true - - uses: sigstore/cosign-installer@v3.6.0 - - uses: anchore/sbom-action/download-syft@v0.17.2 + - uses: sigstore/cosign-installer@v3.7.0 + - uses: anchore/sbom-action/download-syft@v0.17.5 - uses: docker/login-action@v3 with: diff --git a/.github/workflows/releaser.yml b/.github/workflows/releaser.yml index 5433582cace..4fb3c279b1f 100644 --- a/.github/workflows/releaser.yml +++ b/.github/workflows/releaser.yml @@ -23,8 +23,8 @@ jobs: go-version: "1.22.x" cache: true - - uses: sigstore/cosign-installer@v3.6.0 - - uses: anchore/sbom-action/download-syft@v0.17.2 + - uses: sigstore/cosign-installer@v3.7.0 + - uses: anchore/sbom-action/download-syft@v0.17.5 - uses: docker/login-action@v3 with: diff --git a/contribs/gnodev/go.mod b/contribs/gnodev/go.mod index f4859889a16..c419f968d4a 100644 --- a/contribs/gnodev/go.mod +++ b/contribs/gnodev/go.mod @@ -1,6 +1,6 @@ module github.com/gnolang/gno/contribs/gnodev -go 1.22 +go 1.22.0 replace github.com/gnolang/gno => ../.. diff --git a/contribs/gnodev/pkg/dev/node.go b/contribs/gnodev/pkg/dev/node.go index c3e70366fb2..0e1099eef88 100644 --- a/contribs/gnodev/pkg/dev/node.go +++ b/contribs/gnodev/pkg/dev/node.go @@ -565,9 +565,8 @@ func newNodeConfig(tmc *tmcfg.Config, chainid string, appstate gnoland.GnoGenesi } return &gnoland.InMemoryNodeConfig{ - PrivValidator: pv, - TMConfig: tmc, - Genesis: genesis, - GenesisMaxVMCycles: 100_000_000, + PrivValidator: pv, + TMConfig: tmc, + Genesis: genesis, } } diff --git a/docs/gno-tooling/cli/gnokey/working-with-key-pairs.md b/docs/gno-tooling/cli/gnokey/working-with-key-pairs.md index ba03ca569b4..9bc29da6a18 100644 --- a/docs/gno-tooling/cli/gnokey/working-with-key-pairs.md +++ b/docs/gno-tooling/cli/gnokey/working-with-key-pairs.md @@ -38,6 +38,7 @@ gno.land keychain & client SUBCOMMANDS add adds key to the keybase delete deletes a key from the keybase + rotate rotate the password of a key in the keybase to a new password generate generates a bip39 mnemonic export exports private key armor import imports encrypted private key armor @@ -161,6 +162,18 @@ you can recover it using the key's mnemonic, or by importing it if it was export at a previous point in time. ::: + +## Rotating the password of a private key to a new password +To rotate the password of a private key from the `gnokey` keystore to a new password, we need to know the name or +address of the key to remove. +After we have this information, we can run the following command: + +```bash +gnokey rotate MyKey +``` + +After entering the current key decryption password and the new password, the password of the key will be updated in the keystore. + ## Exporting a private key Private keys stored in the `gnokey` keystore can be exported to a desired place on the user's filesystem. diff --git a/docs/reference/go-gno-compatibility.md b/docs/reference/go-gno-compatibility.md index bad19860655..9f9d611e4fd 100644 --- a/docs/reference/go-gno-compatibility.md +++ b/docs/reference/go-gno-compatibility.md @@ -248,7 +248,7 @@ Legend: | runtime/trace | `gospec` | | slices | `gnics` | | sort | `part`[^6] | -| strconv | `part` | +| strconv | `full`[^10] | | strings | `full` | | sync | `tbd` | | sync/atomic | `tbd` | @@ -292,6 +292,8 @@ Legend: [^8]: `crypto/ed25519` is currently only implemented for `Verify`, which should still cover a majority of use cases. A full implementation is welcome. [^9]: `math/rand` in Gno ports over Go's `math/rand/v2`. +[^10]: `strconv` does not have the methods relating to types `complex64` and + `complex128`. ## Tooling (`gno` binary) @@ -301,9 +303,9 @@ Legend: | go build | gno transpile -gobuild | same intention, limited compatibility | | go clean | gno clean | same intention, limited compatibility | | go doc | gno doc | limited compatibility; see https://github.com/gnolang/gno/issues/522 | -| go env | | | +| go env | gno env | | | go fix | | | -| go fmt | | gofmt (& similar tools, like gofumpt) works on gno code. | +| go fmt | gno fmt | gofmt (& similar tools, like gofumpt) works on gno code. | | go generate | | | | go get | | see `gno mod download`. | | go help | gno $cmd --help | ie. `gno doc --help` | diff --git a/examples/gno.land/p/demo/gnorkle/gnorkle/instance.gno b/examples/gno.land/p/demo/gnorkle/gnorkle/instance.gno index 22746d569a8..eea4782909e 100644 --- a/examples/gno.land/p/demo/gnorkle/gnorkle/instance.gno +++ b/examples/gno.land/p/demo/gnorkle/gnorkle/instance.gno @@ -227,7 +227,7 @@ func (i *Instance) GetFeedDefinitions(forAddress string) (string, error) { first = false buf.Write(taskBytes) - return true + return false }) if err != nil { diff --git a/examples/gno.land/p/demo/uassert/uassert.gno b/examples/gno.land/p/demo/uassert/uassert.gno index 2776e93dca9..f9c0ab3efc8 100644 --- a/examples/gno.land/p/demo/uassert/uassert.gno +++ b/examples/gno.land/p/demo/uassert/uassert.gno @@ -266,7 +266,7 @@ func NotEqual(t TestingT, expected, actual interface{}, msgs ...string) bool { if av, ok := actual.(string); ok { notEqual = ev != av ok_ = true - es, as = ev, as + es, as = ev, av } case std.Address: if av, ok := actual.(std.Address); ok { diff --git a/examples/gno.land/p/demo/uint256/arithmetic_test.gno b/examples/gno.land/p/demo/uint256/arithmetic_test.gno index 9f45a507754..079d89fa794 100644 --- a/examples/gno.land/p/demo/uint256/arithmetic_test.gno +++ b/examples/gno.land/p/demo/uint256/arithmetic_test.gno @@ -1,6 +1,8 @@ package uint256 -import "testing" +import ( + "testing" +) type binOp2Test struct { x, y, want string @@ -16,30 +18,45 @@ func TestAdd(t *testing.T) { {"18446744073709551615", "18446744073709551615", "36893488147419103230"}, // uint64 overflow } - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } + for _, tt := range tests { + x := MustFromDecimal(tt.x) + y := MustFromDecimal(tt.y) - y, err := FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } + want := MustFromDecimal(tt.want) + got := new(Uint).Add(x, y) - want, err := FromDecimal(tc.want) - if err != nil { - t.Error(err) - continue + if got.Neq(want) { + t.Errorf("Add(%s, %s) = %v, want %v", tt.x, tt.y, got.ToString(), want.ToString()) } + } +} - got := &Uint{} - got.Add(x, y) +func TestAddOverflow(t *testing.T) { + tests := []struct { + x, y string + want string + overflow bool + }{ + {"0", "1", "1", false}, + {"1", "0", "1", false}, + {"1", "1", "2", false}, + {"10", "10", "20", false}, + {"18446744073709551615", "18446744073709551615", "36893488147419103230", false}, // uint64 overflow, but not Uint256 overflow + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "1", "0", true}, // 2^256 - 1 + 1, should overflow + {"57896044618658097711785492504343953926634992332820282019728792003956564819967", "57896044618658097711785492504343953926634992332820282019728792003956564819968", "115792089237316195423570985008687907853269984665640564039457584007913129639935", false}, // (2^255 - 1) + 2^255, no overflow + {"57896044618658097711785492504343953926634992332820282019728792003956564819967", "57896044618658097711785492504343953926634992332820282019728792003956564819969", "0", true}, // (2^255 - 1) + (2^255 + 1), should overflow + } - if got.Neq(want) { - t.Errorf("Add(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + for _, tt := range tests { + x := MustFromDecimal(tt.x) + y := MustFromDecimal(tt.y) + want, _ := FromDecimal(tt.want) + + got, overflow := new(Uint).AddOverflow(x, y) + + if got.Cmp(want) != 0 || overflow != tt.overflow { + t.Errorf("AddOverflow(%s, %s) = (%s, %v), want (%s, %v)", + tt.x, tt.y, got.ToString(), overflow, tt.want, tt.overflow) } } } @@ -50,33 +67,53 @@ func TestSub(t *testing.T) { {"1", "1", "0"}, {"10", "10", "0"}, {"31337", "1337", "30000"}, - {"2", "3", "115792089237316195423570985008687907853269984665640564039457584007913129639935"}, // underflow + {"2", "3", twoPow256Sub1}, // underflow } for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } + x := MustFromDecimal(tc.x) + y := MustFromDecimal(tc.y) - y, err := FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } + want := MustFromDecimal(tc.want) - want, err := FromDecimal(tc.want) - if err != nil { - t.Error(err) - continue + got := new(Uint).Sub(x, y) + + if got.Neq(want) { + t.Errorf( + "Sub(%s, %s) = %v, want %v", + tc.x, tc.y, got.ToString(), want.ToString(), + ) } + } +} - got := &Uint{} - got.Sub(x, y) +func TestSubOverflow(t *testing.T) { + tests := []struct { + x, y string + want string + overflow bool + }{ + {"1", "0", "1", false}, + {"1", "1", "0", false}, + {"10", "10", "0", false}, + {"31337", "1337", "30000", false}, + {"0", "1", "115792089237316195423570985008687907853269984665640564039457584007913129639935", true}, // 0 - 1, should underflow + {"57896044618658097711785492504343953926634992332820282019728792003956564819968", "1", "57896044618658097711785492504343953926634992332820282019728792003956564819967", false}, // 2^255 - 1, no underflow + {"57896044618658097711785492504343953926634992332820282019728792003956564819968", "57896044618658097711785492504343953926634992332820282019728792003956564819969", "115792089237316195423570985008687907853269984665640564039457584007913129639935", true}, // 2^255 - (2^255 + 1), should underflow + } - if got.Neq(want) { - t.Errorf("Sub(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + for _, tc := range tests { + x := MustFromDecimal(tc.x) + y := MustFromDecimal(tc.y) + want := MustFromDecimal(tc.want) + + got, overflow := new(Uint).SubOverflow(x, y) + + if got.Cmp(want) != 0 || overflow != tc.overflow { + t.Errorf( + "SubOverflow(%s, %s) = (%s, %v), want (%s, %v)", + tc.x, tc.y, got.ToString(), overflow, tc.want, tc.overflow, + ) } } } @@ -89,30 +126,50 @@ func TestMul(t *testing.T) { {"18446744073709551615", "2", "36893488147419103230"}, // uint64 overflow } - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } + for _, tt := range tests { + x := MustFromDecimal(tt.x) + y := MustFromDecimal(tt.y) + want := MustFromDecimal(tt.want) + got := new(Uint).Mul(x, y) - y, err := FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue + if got.Neq(want) { + t.Errorf("Mul(%s, %s) = %v, want %v", tt.x, tt.y, got.ToString(), want.ToString()) } + } +} - want, err := FromDecimal(tc.want) - if err != nil { - t.Error(err) - continue - } +func TestMulOverflow(t *testing.T) { + tests := []struct { + x string + y string + wantZ string + wantOver bool + }{ + {"0x1", "0x1", "0x1", false}, + {"0x0", "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0x0", false}, + {"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0x2", "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", true}, + {"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0x1", true}, + {"0x8000000000000000000000000000000000000000000000000000000000000000", "0x2", "0x0", true}, + {"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0x2", "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", false}, + {"0x100000000000000000", "0x100000000000000000", "0x10000000000000000000000000000000000", false}, + {"0x10000000000000000000000000000000", "0x10000000000000000000000000000000", "0x100000000000000000000000000000000000000000000000000000000000000", false}, + } - got := &Uint{} - got.Mul(x, y) + for _, tt := range tests { + x := MustFromHex(tt.x) + y := MustFromHex(tt.y) + wantZ := MustFromHex(tt.wantZ) - if got.Neq(want) { - t.Errorf("Mul(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + gotZ, gotOver := new(Uint).MulOverflow(x, y) + + if gotZ.Neq(wantZ) { + t.Errorf( + "MulOverflow(%s, %s) = %s, want %s", + tt.x, tt.y, gotZ.ToString(), wantZ.ToString(), + ) + } + if gotOver != tt.wantOver { + t.Errorf("MulOverflow(%s, %s) = %v, want %v", tt.x, tt.y, gotOver, tt.wantOver) } } } @@ -123,32 +180,19 @@ func TestDiv(t *testing.T) { {"31337", "0", "0"}, {"0", "31337", "0"}, {"1", "1", "1"}, + {"1000000000000000000", "3", "333333333333333333"}, + {twoPow256Sub1, "2", "57896044618658097711785492504343953926634992332820282019728792003956564819967"}, } - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } - - want, err := FromDecimal(tc.want) - if err != nil { - t.Error(err) - continue - } + for _, tt := range tests { + x := MustFromDecimal(tt.x) + y := MustFromDecimal(tt.y) + want := MustFromDecimal(tt.want) - got := &Uint{} - got.Div(x, y) + got := new(Uint).Div(x, y) if got.Neq(want) { - t.Errorf("Div(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + t.Errorf("Div(%s, %s) = %v, want %v", tt.x, tt.y, got.ToString(), want.ToString()) } } } @@ -160,32 +204,56 @@ func TestMod(t *testing.T) { {"0", "31337", "0"}, {"2", "31337", "2"}, {"1", "1", "0"}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "2", "1"}, // 2^256 - 1 mod 2 + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "3", "0"}, // 2^256 - 1 mod 3 + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "57896044618658097711785492504343953926634992332820282019728792003956564819968", "57896044618658097711785492504343953926634992332820282019728792003956564819967"}, // 2^256 - 1 mod 2^255 } - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } + for _, tt := range tests { + x := MustFromDecimal(tt.x) + y := MustFromDecimal(tt.y) + want := MustFromDecimal(tt.want) - y, err := FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } + got := new(Uint).Mod(x, y) - want, err := FromDecimal(tc.want) - if err != nil { - t.Error(err) - continue + if got.Neq(want) { + t.Errorf("Mod(%s, %s) = %v, want %v", tt.x, tt.y, got.ToString(), want.ToString()) } + } +} - got := &Uint{} - got.Mod(x, y) +func TestMulMod(t *testing.T) { + tests := []struct { + x string + y string + m string + want string + }{ + {"0x1", "0x1", "0x2", "0x1"}, + {"0x10", "0x10", "0x7", "0x4"}, + {"0x100", "0x100", "0x17", "0x9"}, + {"0x31337", "0x31337", "0x31338", "0x1"}, + {"0x0", "0x31337", "0x31338", "0x0"}, + {"0x31337", "0x0", "0x31338", "0x0"}, + {"0x2", "0x3", "0x5", "0x1"}, + {"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0x0"}, + {"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", "0x1"}, + {"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0xffffffffffffffffffffffffffffffff", "0x0"}, + } + + for _, tt := range tests { + x := MustFromHex(tt.x) + y := MustFromHex(tt.y) + m := MustFromHex(tt.m) + want := MustFromHex(tt.want) + + got := new(Uint).MulMod(x, y, m) if got.Neq(want) { - t.Errorf("Mod(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + t.Errorf( + "MulMod(%s, %s, %s) = %s, want %s", + tt.x, tt.y, tt.m, got.ToString(), want.ToString(), + ) } } } @@ -206,30 +274,11 @@ func TestDivMod(t *testing.T) { {"2", "31337", "0", "2"}, } - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } - - wantDiv, err := FromDecimal(tc.wantDiv) - if err != nil { - t.Error(err) - continue - } - - wantMod, err := FromDecimal(tc.wantMod) - if err != nil { - t.Error(err) - continue - } + for _, tt := range tests { + x := MustFromDecimal(tt.x) + y := MustFromDecimal(tt.y) + wantDiv := MustFromDecimal(tt.wantDiv) + wantMod := MustFromDecimal(tt.wantMod) gotDiv := new(Uint) gotMod := new(Uint) @@ -237,13 +286,13 @@ func TestDivMod(t *testing.T) { for i := range gotDiv.arr { if gotDiv.arr[i] != wantDiv.arr[i] { - t.Errorf("DivMod(%s, %s) got Div %v, want Div %v", tc.x, tc.y, gotDiv, wantDiv) + t.Errorf("DivMod(%s, %s) got Div %v, want Div %v", tt.x, tt.y, gotDiv, wantDiv) break } } for i := range gotMod.arr { if gotMod.arr[i] != wantMod.arr[i] { - t.Errorf("DivMod(%s, %s) got Mod %v, want Mod %v", tc.x, tc.y, gotMod, wantMod) + t.Errorf("DivMod(%s, %s) got Mod %v, want Mod %v", tt.x, tt.y, gotMod, wantMod) break } } @@ -259,27 +308,17 @@ func TestNeg(t *testing.T) { {"115792089237316195423570985008687907853269984665640564039457584007913129608599", "31337"}, {"0", "0"}, {"2", "115792089237316195423570985008687907853269984665640564039457584007913129639934"}, - {"1", "115792089237316195423570985008687907853269984665640564039457584007913129639935"}, + {"1", twoPow256Sub1}, } - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - want, err := FromDecimal(tc.want) - if err != nil { - t.Error(err) - continue - } + for _, tt := range tests { + x := MustFromDecimal(tt.x) + want := MustFromDecimal(tt.want) - got := &Uint{} - got.Neg(x) + got := new(Uint).Neg(x) if got.Neq(want) { - t.Errorf("Neg(%s) = %v, want %v", tc.x, got.ToString(), want.ToString()) + t.Errorf("Neg(%s) = %v, want %v", tt.x, got.ToString(), want.ToString()) } } } @@ -297,30 +336,57 @@ func TestExp(t *testing.T) { {"2", "256", "0"}, // overflow } - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } + for _, tt := range tests { + x := MustFromDecimal(tt.x) + y := MustFromDecimal(tt.y) + want := MustFromDecimal(tt.want) - y, err := FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } + got := new(Uint).Exp(x, y) - want, err := FromDecimal(tc.want) - if err != nil { - t.Error(err) - continue + if got.Neq(want) { + t.Errorf( + "Exp(%s, %s) = %v, want %v", + tt.x, tt.y, got.ToString(), want.ToString(), + ) } + } +} + +func TestExp_LargeExponent(t *testing.T) { + tests := []struct { + name string + base string + exponent string + expected string + }{ + { + name: "2^129", + base: "2", + exponent: "680564733841876926926749214863536422912", + expected: "0", + }, + { + name: "2^193", + base: "2", + exponent: "12379400392853802746563808384000000000000000000", + expected: "0", + }, + } - got := &Uint{} - got.Exp(x, y) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + base := MustFromDecimal(tt.base) + exponent := MustFromDecimal(tt.exponent) + expected := MustFromDecimal(tt.expected) - if got.Neq(want) { - t.Errorf("Exp(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) - } + result := new(Uint).Exp(base, exponent) + + if result.Neq(expected) { + t.Errorf( + "Test %s failed. Expected %s, got %s", + tt.name, expected.ToString(), result.ToString(), + ) + } + }) } } diff --git a/examples/gno.land/p/demo/uint256/bitwise_test.gno b/examples/gno.land/p/demo/uint256/bitwise_test.gno index aba89edfabf..3561629fd94 100644 --- a/examples/gno.land/p/demo/uint256/bitwise_test.gno +++ b/examples/gno.land/p/demo/uint256/bitwise_test.gno @@ -37,11 +37,14 @@ func TestOr(t *testing.T) { }, } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - res := new(Uint).Or(&tc.x, &tc.y) - if *res != tc.want { - t.Errorf("Or(%s, %s) = %s, want %s", tc.x.ToString(), tc.y.ToString(), res.ToString(), (tc.want).ToString()) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res := new(Uint).Or(&tt.x, &tt.y) + if *res != tt.want { + t.Errorf( + "Or(%s, %s) = %s, want %s", + tt.x.ToString(), tt.y.ToString(), res.ToString(), (tt.want).ToString(), + ) } }) } @@ -93,11 +96,14 @@ func TestAnd(t *testing.T) { }, } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - res := new(Uint).And(&tc.x, &tc.y) - if *res != tc.want { - t.Errorf("And(%s, %s) = %s, want %s", tc.x.ToString(), tc.y.ToString(), res.ToString(), (tc.want).ToString()) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res := new(Uint).And(&tt.x, &tt.y) + if *res != tt.want { + t.Errorf( + "And(%s, %s) = %s, want %s", + tt.x.ToString(), tt.y.ToString(), res.ToString(), (tt.want).ToString(), + ) } }) } @@ -126,11 +132,14 @@ func TestNot(t *testing.T) { }, } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - res := new(Uint).Not(&tc.x) - if *res != tc.want { - t.Errorf("Not(%s) = %s, want %s", tc.x.ToString(), res.ToString(), (tc.want).ToString()) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res := new(Uint).Not(&tt.x) + if *res != tt.want { + t.Errorf( + "Not(%s) = %s, want %s", + tt.x.ToString(), res.ToString(), (tt.want).ToString(), + ) } }) } @@ -182,11 +191,14 @@ func TestAndNot(t *testing.T) { }, } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - res := new(Uint).AndNot(&tc.x, &tc.y) - if *res != tc.want { - t.Errorf("AndNot(%s, %s) = %s, want %s", tc.x.ToString(), tc.y.ToString(), res.ToString(), (tc.want).ToString()) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res := new(Uint).AndNot(&tt.x, &tt.y) + if *res != tt.want { + t.Errorf( + "AndNot(%s, %s) = %s, want %s", + tt.x.ToString(), tt.y.ToString(), res.ToString(), (tt.want).ToString(), + ) } }) } @@ -238,11 +250,14 @@ func TestXor(t *testing.T) { }, } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - res := new(Uint).Xor(&tc.x, &tc.y) - if *res != tc.want { - t.Errorf("Xor(%s, %s) = %s, want %s", tc.x.ToString(), tc.y.ToString(), res.ToString(), (tc.want).ToString()) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res := new(Uint).Xor(&tt.x, &tt.y) + if *res != tt.want { + t.Errorf( + "Xor(%s, %s) = %s, want %s", + tt.x.ToString(), tt.y.ToString(), res.ToString(), (tt.want).ToString(), + ) } }) } @@ -272,26 +287,31 @@ func TestLsh(t *testing.T) { {"31337", 193, "393411074163624830192644266310117284962799025126338899061243904"}, {"31337", 255, "57896044618658097711785492504343953926634992332820282019728792003956564819968"}, {"31337", 256, "0"}, - } + // 64 < n < 128 + {"1", 65, "36893488147419103232"}, + {"31337", 100, "39724366859352024754702188346867712"}, - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } + // 128 < n < 192 + {"1", 129, "680564733841876926926749214863536422912"}, + {"31337", 150, "44725660946326664792723507424638829088826130956288"}, - want, err := FromDecimal(tc.want) - if err != nil { - t.Error(err) - continue - } + // 192 < n < 256 + {"1", 193, "12554203470773361527671578846415332832204710888928069025792"}, + {"31337", 200, "50356617492943978264658466087695012475238275216171379079839219712"}, - got := &Uint{} - got.Lsh(x, tc.y) + // n > 256 + {"1", 257, "0"}, + {"31337", 300, "0"}, + } + + for _, tt := range tests { + x := MustFromDecimal(tt.x) + want := MustFromDecimal(tt.want) + + got := new(Uint).Lsh(x, tt.y) if got.Neq(want) { - t.Errorf("Lsh(%s, %d) = %s, want %s", tc.x, tc.y, got.ToString(), want.ToString()) + t.Errorf("Lsh(%s, %d) = %s, want %s", tt.x, tt.y, got.ToString(), want.ToString()) } } } @@ -319,26 +339,85 @@ func TestRsh(t *testing.T) { {"196705537081812415096322133155058642481399512563169449530621952", 192, "31337"}, {"10663428532201448629551770073089320442396672", 128, "31337"}, {"578065619037836218990592", 64, "31337"}, + {twoPow256Sub1, 256, "0"}, + // outliers + {"340282366920938463463374607431768211455", 129, "0"}, + {"18446744073709551615", 65, "0"}, + {twoPow256Sub1, 1, "57896044618658097711785492504343953926634992332820282019728792003956564819967"}, + + // n > 256 + {"1", 257, "0"}, + {"31337", 300, "0"}, } - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } + for _, tt := range tests { + x := MustFromDecimal(tt.x) + + want := MustFromDecimal(tt.want) + got := new(Uint).Rsh(x, tt.y) - want, err := FromDecimal(tc.want) - if err != nil { - t.Error(err) - continue + if got.Neq(want) { + t.Errorf("Rsh(%s, %d) = %s, want %s", tt.x, tt.y, got.ToString(), want.ToString()) } + } +} + +func TestSRsh(t *testing.T) { + tests := []struct { + x string + y uint + want string + }{ + // Positive numbers (behaves like Rsh) + {"0x0", 0, "0x0"}, + {"0x0", 1, "0x0"}, + {"0x1", 0, "0x1"}, + {"0x1", 1, "0x0"}, + {"0x31337", 0, "0x31337"}, + {"0x31337", 4, "0x3133"}, + {"0x31337", 8, "0x313"}, + {"0x31337", 16, "0x3"}, + {"0x10000000000000000", 64, "0x1"}, // 2^64 >> 64 - got := &Uint{} - got.Rsh(x, tc.y) + // // Numbers with MSB set (negative numbers in two's complement) + {"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 0, "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1, "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 4, "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 64, "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 128, "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 192, "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 255, "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, - if got.Neq(want) { - t.Errorf("Rsh(%s, %d) = %s, want %s", tc.x, tc.y, got.ToString(), want.ToString()) + // Large positive number close to max value + {"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1, "0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 2, "0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 64, "0x7fffffffffffffffffffffffffffffffffffffffffffffff"}, + {"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 128, "0x7fffffffffffffffffffffffffffffff"}, + {"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 192, "0x7fffffffffffffff"}, + {"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 255, "0x0"}, + + // Specific cases + {"0x8000000000000000000000000000000000000000000000000000000000000000", 1, "0xc000000000000000000000000000000000000000000000000000000000000000"}, + {"0x8000000000000000000000000000000000000000000000000000000000000001", 1, "0xc000000000000000000000000000000000000000000000000000000000000000"}, + + {"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 65, "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 127, "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 129, "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 193, "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + + // n > 256 + {"0x1", 257, "0x0"}, + {"0x31337", 300, "0x0"}, + } + + for _, tt := range tests { + x := MustFromHex(tt.x) + want := MustFromHex(tt.want) + + got := new(Uint).SRsh(x, tt.y) + + if !got.Eq(want) { + t.Errorf("SRsh(%s, %d) = %s, want %s", tt.x, tt.y, got.ToString(), want.ToString()) } } } diff --git a/examples/gno.land/p/demo/uint256/cmp_test.gno b/examples/gno.land/p/demo/uint256/cmp_test.gno index 930079f70f0..51c9e70d9a7 100644 --- a/examples/gno.land/p/demo/uint256/cmp_test.gno +++ b/examples/gno.land/p/demo/uint256/cmp_test.gno @@ -5,6 +5,39 @@ import ( "testing" ) +func TestSign(t *testing.T) { + tests := []struct { + input *Uint + expected int + }{ + { + input: NewUint(0), + expected: 0, + }, + { + input: NewUint(1), + expected: 1, + }, + { + input: NewUint(0x7fffffffffffffff), + expected: 1, + }, + { + input: NewUint(0x8000000000000000), + expected: 1, + }, + } + + for _, tt := range tests { + t.Run(tt.input.ToString(), func(t *testing.T) { + result := tt.input.Sign() + if result != tt.expected { + t.Errorf("Sign() = %d; want %d", result, tt.expected) + } + }) + } +} + func TestCmp(t *testing.T) { tests := []struct { x, y string @@ -20,17 +53,8 @@ func TestCmp(t *testing.T) { } for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } + x := MustFromDecimal(tc.x) + y := MustFromDecimal(tc.y) got := x.Cmp(y) if got != tc.want { @@ -49,16 +73,12 @@ func TestIsZero(t *testing.T) { {"10", false}, } - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } + for _, tt := range tests { + x := MustFromDecimal(tt.x) got := x.IsZero() - if got != tc.want { - t.Errorf("IsZero(%s) = %v, want %v", tc.x, got, tc.want) + if got != tt.want { + t.Errorf("IsZero(%s) = %v, want %v", tt.x, got, tt.want) } } } @@ -77,31 +97,53 @@ func TestLtUint64(t *testing.T) { } for _, tc := range tests { - var x *Uint - var err error - - if strings.HasPrefix(tc.x, "0x") { - x, err = FromHex(tc.x) - if err != nil { - t.Error(err) - continue - } - } else { - x, err = FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - } + x := parseTestString(t, tc.x) got := x.LtUint64(tc.y) - if got != tc.want { t.Errorf("LtUint64(%s, %d) = %v, want %v", tc.x, tc.y, got, tc.want) } } } +func TestUint_GtUint64(t *testing.T) { + tests := []struct { + name string + z string + n uint64 + want bool + }{ + { + name: "z > n", + z: "1", + n: 0, + want: true, + }, + { + name: "z < n", + z: "18446744073709551615", + n: 0xFFFFFFFFFFFFFFFF, + want: false, + }, + { + name: "z == n", + z: "18446744073709551615", + n: 0xFFFFFFFFFFFFFFFF, + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + z := MustFromDecimal(tt.z) + + if got := z.GtUint64(tt.n); got != tt.want { + t.Errorf("Uint.GtUint64() = %v, want %v", got, tt.want) + } + }) + } +} + func TestSGT(t *testing.T) { x := MustFromHex("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe") y := MustFromHex("0x0") @@ -127,37 +169,83 @@ func TestEq(t *testing.T) { {"0xffffffffffffffff", "18446744073709551615", true}, {"0x10000000000000000", "18446744073709551616", true}, {"0", "0", true}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "115792089237316195423570985008687907853269984665640564039457584007913129639935", true}, + {twoPow256Sub1, twoPow256Sub1, true}, } - for i, tc := range tests { - var x *Uint - var err error + for _, tt := range tests { + x := parseTestString(t, tt.x) - if strings.HasPrefix(tc.x, "0x") { - x, err = FromHex(tc.x) - if err != nil { - t.Error(err) - continue - } - } else { - x, err = FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } + y, err := FromDecimal(tt.y) + if err != nil { + t.Error(err) + continue } - y, err := FromDecimal(tc.y) + got := x.Eq(y) + + if got != tt.want { + t.Errorf("Eq(%s, %s) = %v, want %v", tt.x, tt.y, got, tt.want) + } + } +} + +func TestUint_Lte(t *testing.T) { + tests := []struct { + z, x string + want bool + }{ + {"10", "20", true}, + {"20", "10", false}, + {"10", "10", true}, + {"0", "0", true}, + } + + for _, tt := range tests { + z, err := FromDecimal(tt.z) if err != nil { t.Error(err) continue } + x, err := FromDecimal(tt.x) + if err != nil { + t.Error(err) + continue + } + if got := z.Lte(x); got != tt.want { + t.Errorf("Uint.Lte(%v, %v) = %v, want %v", tt.z, tt.x, got, tt.want) + } + } +} - got := x.Eq(y) +func TestUint_Gte(t *testing.T) { + tests := []struct { + z, x string + want bool + }{ + {"20", "10", true}, + {"10", "20", false}, + {"10", "10", true}, + {"0", "0", true}, + } - if got != tc.want { - t.Errorf("Eq(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) + for _, tt := range tests { + z := parseTestString(t, tt.z) + x := parseTestString(t, tt.x) + + if got := z.Gte(x); got != tt.want { + t.Errorf("Uint.Gte(%v, %v) = %v, want %v", tt.z, tt.x, got, tt.want) } } } + +func parseTestString(_ *testing.T, s string) *Uint { + var x *Uint + + if strings.HasPrefix(s, "0x") { + x = MustFromHex(s) + } else { + x = MustFromDecimal(s) + } + + return x +} diff --git a/examples/gno.land/p/demo/uint256/conversion_test.gno b/examples/gno.land/p/demo/uint256/conversion_test.gno index ee3aad0f819..0ea20158be4 100644 --- a/examples/gno.land/p/demo/uint256/conversion_test.gno +++ b/examples/gno.land/p/demo/uint256/conversion_test.gno @@ -14,18 +14,18 @@ func TestIsUint64(t *testing.T) { {"0x10000000000000000", false}, } - for _, tc := range tests { - x := MustFromHex(tc.x) + for _, tt := range tests { + x := MustFromHex(tt.x) got := x.IsUint64() - if got != tc.want { - t.Errorf("IsUint64(%s) = %v, want %v", tc.x, got, tc.want) + if got != tt.want { + t.Errorf("IsUint64(%s) = %v, want %v", tt.x, got, tt.want) } } } func TestDec(t *testing.T) { - testCases := []struct { + tests := []struct { name string z Uint want string @@ -43,16 +43,133 @@ func TestDec(t *testing.T) { { name: "max possible value", z: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, - want: "115792089237316195423570985008687907853269984665640564039457584007913129639935", + want: twoPow256Sub1, }, } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - result := tc.z.Dec() - if result != tc.want { - t.Errorf("Dec(%v) = %s, want %s", tc.z, result, tc.want) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := tt.z.Dec() + if result != tt.want { + t.Errorf("Dec(%v) = %s, want %s", tt.z, result, tt.want) } }) } } + +func TestUint_Scan(t *testing.T) { + tests := []struct { + name string + input interface{} + want *Uint + wantErr bool + }{ + { + name: "nil", + input: nil, + want: NewUint(0), + }, + { + name: "valid scientific notation", + input: "1e4", + want: NewUint(10000), + }, + { + name: "valid decimal string", + input: "12345", + want: NewUint(12345), + }, + { + name: "valid byte slice", + input: []byte("12345"), + want: NewUint(12345), + }, + { + name: "invalid string", + input: "invalid", + wantErr: true, + }, + { + name: "out of range", + input: "115792089237316195423570985008687907853269984665640564039457584007913129639936", // 2^256 + wantErr: true, + }, + { + name: "unsupported type", + input: 123, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + z := new(Uint) + err := z.Scan(tt.input) + + if tt.wantErr { + if err == nil { + t.Errorf("Scan() error = %v, wantErr %v", err, tt.wantErr) + } + } else { + if err != nil { + t.Errorf("Scan() error = %v, wantErr %v", err, tt.wantErr) + } + if !z.Eq(tt.want) { + t.Errorf("Scan() = %v, want %v", z, tt.want) + } + } + }) + } +} + +func TestSetBytes(t *testing.T) { + tests := []struct { + input []byte + expected string + }{ + {[]byte{}, "0"}, + {[]byte{0x01}, "1"}, + {[]byte{0x12, 0x34}, "4660"}, + {[]byte{0x12, 0x34, 0x56}, "1193046"}, + {[]byte{0x12, 0x34, 0x56, 0x78}, "305419896"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a}, "78187493530"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc}, "20015998343868"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde}, "5124095576030430"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}, "1311768467463790320"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12}, "335812727670730321938"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34}, "85968058283706962416180"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56}, "22007822920628982378542166"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78}, "5634002667681019488906794616"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a}, "1442304682926340989160139421850"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc}, "369229998829143293224995691993788"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde}, "94522879700260683065598897150409950"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}, "24197857203266734864793317670504947440"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12}, "6194651444036284125387089323649266544658"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34}, "1585830769673288736099094866854212235432500"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56}, "405972677036361916441368285914678332270720086"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78}, "103929005321308650608990281194157653061304342136"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a}, "26605825362255014555901511985704359183693911586970"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc}, "6811091292737283726310787068340315951025641366264508"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde}, "1743639370940744633935561489495120883462564189763714270"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}, "446371678960830626287503741310750946166416432579510853360"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12}, "114271149813972640329600957775552242218602606740354778460178"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34}, "29253414352376995924377845190541374007962267325530823285805620"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56}, "7488874074208510956640728368778591746038340435335890761166238806"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78}, "1917151762997378804900026462407319486985815151445988034858557134456"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a}, "490790851327328974054406774376273788668368678770172936923790626420890"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc}, "125642457939796217357928134240326089899102381765164271852490400363748028"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde}, "32164469232587831643629602365523479014170209731882053594237542493119495390"}, + {[]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}, "8234104123542484900769178205574010627627573691361805720124810878238590820080"}, + // over 32 bytes (last 32 bytes are used) + {append([]byte{0xff}, []byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}...), "8234104123542484900769178205574010627627573691361805720124810878238590820080"}, + } + + for _, test := range tests { + z := new(Uint) + z.SetBytes(test.input) + expected := MustFromDecimal(test.expected) + if z.Cmp(expected) != 0 { + t.Errorf("SetBytes(%x) = %s, expected %s", test.input, z.ToString(), test.expected) + } + } +} diff --git a/examples/gno.land/p/demo/uint256/uint256.gno b/examples/gno.land/p/demo/uint256/uint256.gno index 80da0ba882b..3d183362992 100644 --- a/examples/gno.land/p/demo/uint256/uint256.gno +++ b/examples/gno.land/p/demo/uint256/uint256.gno @@ -5,6 +5,7 @@ package uint256 import ( "errors" "math/bits" + "strconv" ) const ( @@ -143,10 +144,10 @@ func (z *Uint) fromDecimal(bs string) error { if remaining <= 0 { return nil // Done } else if remaining > 19 { - num, err = parseUint(bs[remaining-19:remaining], 10, 64) + num, err = strconv.ParseUint(bs[remaining-19:remaining], 10, 64) } else { // Final round - num, err = parseUint(bs, 10, 64) + num, err = strconv.ParseUint(bs, 10, 64) } if err != nil { return err diff --git a/examples/gno.land/p/demo/uint256/uint256_test.gno b/examples/gno.land/p/demo/uint256/uint256_test.gno new file mode 100644 index 00000000000..0089af15c66 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/uint256_test.gno @@ -0,0 +1,127 @@ +package uint256 + +import ( + "testing" +) + +func TestSetAllOne(t *testing.T) { + z := Zero() + z.SetAllOne() + if z.ToString() != twoPow256Sub1 { + t.Errorf("Expected all ones, got %s", z.ToString()) + } +} + +func TestByte(t *testing.T) { + tests := []struct { + input string + position uint64 + expected byte + }{ + {"0x1000000000000000000000000000000000000000000000000000000000000000", 0, 16}, + {"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 0, 255}, + {"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 31, 255}, + } + + for i, tt := range tests { + z, _ := FromHex(tt.input) + n := NewUint(tt.position) + result := z.Byte(n) + + if result.arr[0] != uint64(tt.expected) { + t.Errorf("Test case %d failed. Input: %s, Position: %d, Expected: %d, Got: %d", + i, tt.input, tt.position, tt.expected, result.arr[0]) + } + + // check other array elements are 0 + if result.arr[1] != 0 || result.arr[2] != 0 || result.arr[3] != 0 { + t.Errorf("Test case %d failed. Non-zero values in upper bytes", i) + } + } + + // overflow + z, _ := FromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + n := NewUint(32) + result := z.Byte(n) + + if !result.IsZero() { + t.Errorf("Expected zero for position >= 32, got %v", result) + } +} + +func TestBitLen(t *testing.T) { + tests := []struct { + input string + expected int + }{ + {"0x0", 0}, + {"0x1", 1}, + {"0xff", 8}, + {"0x100", 9}, + {"0xffff", 16}, + {"0x10000", 17}, + {"0xffffffffffffffff", 64}, + {"0x10000000000000000", 65}, + {"0xffffffffffffffffffffffffffffffff", 128}, + {"0x100000000000000000000000000000000", 129}, + {"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 256}, + } + + for i, tt := range tests { + z, _ := FromHex(tt.input) + result := z.BitLen() + + if result != tt.expected { + t.Errorf("Test case %d failed. Input: %s, Expected: %d, Got: %d", + i, tt.input, tt.expected, result) + } + } +} + +func TestByteLen(t *testing.T) { + tests := []struct { + input string + expected int + }{ + {"0x0", 0}, + {"0x1", 1}, + {"0xff", 1}, + {"0x100", 2}, + {"0xffff", 2}, + {"0x10000", 3}, + {"0xffffffffffffffff", 8}, + {"0x10000000000000000", 9}, + {"0xffffffffffffffffffffffffffffffff", 16}, + {"0x100000000000000000000000000000000", 17}, + {"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 32}, + } + + for i, tt := range tests { + z, _ := FromHex(tt.input) + result := z.ByteLen() + + if result != tt.expected { + t.Errorf("Test case %d failed. Input: %s, Expected: %d, Got: %d", + i, tt.input, tt.expected, result) + } + } +} + +func TestClone(t *testing.T) { + tests := []struct { + input string + expected string + }{ + {"0x1", "1"}, + {"0x100", "256"}, + {"0x10000000000000000", "18446744073709551616"}, + } + + for _, tt := range tests { + z, _ := FromHex(tt.input) + result := z.Clone() + if result.ToString() != tt.expected { + t.Errorf("Test %s failed. Expected %s, got %s", tt.input, tt.expected, result.ToString()) + } + } +} diff --git a/examples/gno.land/p/demo/uint256/utils.gno b/examples/gno.land/p/demo/uint256/utils.gno index 969728f3369..bcc7bb283e0 100644 --- a/examples/gno.land/p/demo/uint256/utils.gno +++ b/examples/gno.land/p/demo/uint256/utils.gno @@ -1,63 +1,5 @@ package uint256 -// lower(c) is a lower-case letter if and only if -// c is either that lower-case letter or the equivalent upper-case letter. -// Instead of writing c == 'x' || c == 'X' one can write lower(c) == 'x'. -// Note that lower of non-letters can produce other non-letters. -func lower(c byte) byte { - return c | ('x' - 'X') -} - -// underscoreOK reports whether the underscores in s are allowed. -// Checking them in this one function lets all the parsers skip over them simply. -// Underscore must appear only between digits or between a base prefix and a digit. -func underscoreOK(s string) bool { - // saw tracks the last character (class) we saw: - // ^ for beginning of number, - // 0 for a digit or base prefix, - // _ for an underscore, - // ! for none of the above. - saw := '^' - i := 0 - - // Optional sign. - if len(s) >= 1 && (s[0] == '-' || s[0] == '+') { - s = s[1:] - } - - // Optional base prefix. - hex := false - if len(s) >= 2 && s[0] == '0' && (lower(s[1]) == 'b' || lower(s[1]) == 'o' || lower(s[1]) == 'x') { - i = 2 - saw = '0' // base prefix counts as a digit for "underscore as digit separator" - hex = lower(s[1]) == 'x' - } - - // Number proper. - for ; i < len(s); i++ { - // Digits are always okay. - if '0' <= s[i] && s[i] <= '9' || hex && 'a' <= lower(s[i]) && lower(s[i]) <= 'f' { - saw = '0' - continue - } - // Underscore must follow digit. - if s[i] == '_' { - if saw != '0' { - return false - } - saw = '_' - continue - } - // Underscore must also be followed by digit. - if saw == '_' { - return false - } - // Saw non-digit, non-underscore. - saw = '!' - } - return saw != '_' -} - func checkNumberS(input string) error { const fn = "UnmarshalText" l := len(input) @@ -76,105 +18,3 @@ func checkNumberS(input string) error { } return nil } - -// ParseUint is like ParseUint but for unsigned numbers. -// -// A sign prefix is not permitted. -func parseUint(s string, base int, bitSize int) (uint64, error) { - const fnParseUint = "ParseUint" - - if s == "" { - return 0, errSyntax(fnParseUint, s) - } - - base0 := base == 0 - - s0 := s - switch { - case 2 <= base && base <= 36: - // valid base; nothing to do - - case base == 0: - // Look for octal, hex prefix. - base = 10 - if s[0] == '0' { - switch { - case len(s) >= 3 && lower(s[1]) == 'b': - base = 2 - s = s[2:] - case len(s) >= 3 && lower(s[1]) == 'o': - base = 8 - s = s[2:] - case len(s) >= 3 && lower(s[1]) == 'x': - base = 16 - s = s[2:] - default: - base = 8 - s = s[1:] - } - } - - default: - return 0, errInvalidBase(fnParseUint, base) - } - - if bitSize == 0 { - bitSize = uintSize - } else if bitSize < 0 || bitSize > 64 { - return 0, errInvalidBitSize(fnParseUint, bitSize) - } - - // Cutoff is the smallest number such that cutoff*base > maxUint64. - // Use compile-time constants for common cases. - var cutoff uint64 - switch base { - case 10: - cutoff = MaxUint64/10 + 1 - case 16: - cutoff = MaxUint64/16 + 1 - default: - cutoff = MaxUint64/uint64(base) + 1 - } - - maxVal := uint64(1)<= byte(base) { - return 0, errSyntax(fnParseUint, s0) - } - - if n >= cutoff { - // n*base overflows - return maxVal, errRange(fnParseUint, s0) - } - n *= uint64(base) - - n1 := n + uint64(d) - if n1 < n || n1 > maxVal { - // n+d overflows - return maxVal, errRange(fnParseUint, s0) - } - n = n1 - } - - if underscores && !underscoreOK(s0) { - return 0, errSyntax(fnParseUint, s0) - } - - return n, nil -} diff --git a/examples/gno.land/r/gnoland/ghverify/contract.gno b/examples/gno.land/r/gnoland/ghverify/contract.gno index b40c9ef1448..3b8f7fcbbe1 100644 --- a/examples/gno.land/r/gnoland/ghverify/contract.gno +++ b/examples/gno.land/r/gnoland/ghverify/contract.gno @@ -83,6 +83,11 @@ func RequestVerification(githubHandle string) { ); err != nil { panic(err) } + std.Emit( + "verification_requested", + "from", gnoAddress, + "handle", githubHandle, + ) } // GnorkleEntrypoint is the entrypoint to the gnorkle oracle handler. @@ -139,7 +144,7 @@ func Render(_ string) string { result += `"` + handle + `": "` + address.(string) + `"` appendComma = true - return true + return false }) return result + "}" diff --git a/examples/gno.land/r/gnoland/ghverify/contract_test.gno b/examples/gno.land/r/gnoland/ghverify/contract_test.gno index d9c399942ae..5c0be0afcb1 100644 --- a/examples/gno.land/r/gnoland/ghverify/contract_test.gno +++ b/examples/gno.land/r/gnoland/ghverify/contract_test.gno @@ -9,7 +9,8 @@ import ( func TestVerificationLifecycle(t *testing.T) { defaultAddress := std.GetOrigCaller() - userAddress := std.Address(testutils.TestAddress("user")) + user1Address := std.Address(testutils.TestAddress("user 1")) + user2Address := std.Address(testutils.TestAddress("user 2")) // Verify request returns no feeds. result := GnorkleEntrypoint("request") @@ -18,7 +19,7 @@ func TestVerificationLifecycle(t *testing.T) { } // Make a verification request with the created user. - std.TestSetOrigCaller(userAddress) + std.TestSetOrigCaller(user1Address) RequestVerification("deelawn") // A subsequent request from the same address should panic because there is @@ -42,26 +43,32 @@ func TestVerificationLifecycle(t *testing.T) { t.Fatalf("expected empty request result, got %s", result) } + // Make a verification request with the created user. + std.TestSetOrigCaller(user2Address) + RequestVerification("omarsy") + // Set the caller back to the whitelisted user and verify that the feed data // returned matches what should have been created by the `RequestVerification` // invocation. std.TestSetOrigCaller(defaultAddress) result = GnorkleEntrypoint("request") - expResult := `[{"id":"` + string(userAddress) + `","type":"0","value_type":"string","tasks":[{"gno_address":"` + - string(userAddress) + `","github_handle":"deelawn"}]}]` + expResult := `[{"id":"` + string(user1Address) + `","type":"0","value_type":"string","tasks":[{"gno_address":"` + + string(user1Address) + `","github_handle":"deelawn"}]},` + + `{"id":"` + string(user2Address) + `","type":"0","value_type":"string","tasks":[{"gno_address":"` + + string(user2Address) + `","github_handle":"omarsy"}]}]` if result != expResult { t.Fatalf("expected request result %s, got %s", expResult, result) } // Try to trigger feed ingestion from the non-authorized user. - std.TestSetOrigCaller(userAddress) + std.TestSetOrigCaller(user1Address) func() { defer func() { if r := recover(); r != nil { errMsg = r.(error).Error() } }() - GnorkleEntrypoint("ingest," + string(userAddress) + ",OK") + GnorkleEntrypoint("ingest," + string(user1Address) + ",OK") }() if errMsg != "caller not whitelisted" { t.Fatalf("expected caller not whitelisted, got %s", errMsg) @@ -69,15 +76,15 @@ func TestVerificationLifecycle(t *testing.T) { // Set the caller back to the whitelisted user and transfer contract ownership. std.TestSetOrigCaller(defaultAddress) - SetOwner(userAddress) + SetOwner(defaultAddress) // Now trigger the feed ingestion from the user and new owner and only whitelisted address. - std.TestSetOrigCaller(userAddress) - GnorkleEntrypoint("ingest," + string(userAddress) + ",OK") + GnorkleEntrypoint("ingest," + string(user1Address) + ",OK") + GnorkleEntrypoint("ingest," + string(user2Address) + ",OK") // Verify the ingestion autocommitted the value and triggered the post handler. data := Render("") - expResult = `{"deelawn": "` + string(userAddress) + `"}` + expResult = `{"deelawn": "` + string(user1Address) + `","omarsy": "` + string(user2Address) + `"}` if data != expResult { t.Fatalf("expected render data %s, got %s", expResult, data) } @@ -89,10 +96,10 @@ func TestVerificationLifecycle(t *testing.T) { } // Check that the accessor functions are working as expected. - if handle := GetHandleByAddress(string(userAddress)); handle != "deelawn" { + if handle := GetHandleByAddress(string(user1Address)); handle != "deelawn" { t.Fatalf("expected deelawn, got %s", handle) } - if address := GetAddressByHandle("deelawn"); address != string(userAddress) { - t.Fatalf("expected %s, got %s", string(userAddress), address) + if address := GetAddressByHandle("deelawn"); address != string(user1Address) { + t.Fatalf("expected %s, got %s", string(user1Address), address) } } diff --git a/examples/gno.land/r/gnoland/pages/page_contribute.gno b/examples/gno.land/r/gnoland/pages/page_contribute.gno index a4bdfabb6ef..0855dc327cd 100644 --- a/examples/gno.land/r/gnoland/pages/page_contribute.gno +++ b/examples/gno.land/r/gnoland/pages/page_contribute.gno @@ -45,7 +45,7 @@ Don't fear your work being "stolen": if a submission is the result of multiple p - If you, for instance, cannot complete the entirety of the task or, as a non-developer, can only contribute a part of the specification/implementation, you may still be awarded a bounty for your input in the contribution. - If Alice makes a PR that aside from implementing what's required, also undertakes creating useful tools among the way, she may qualify for an "outstanding contribution"; and may be awarded up to 25% more of the original bounty's value. Or she may also ask if the team would be willing to offer a different bounty for the implementation of the tools. -Participants in the gno.land Bounty Program must meet the legal Terms and Conditions referenced [here](https://docs.google.com/document/d/1aXrZ6japdAykB5FLmHCCeBZTo-2tbZQHSQi79ITaTK0). +Participants in the gno.land Bounty Program must meet the legal Terms and Conditions referenced [here](https://docs.google.com/document/d/e/2PACX-1vSUF-JwIXGscrNsc5QBD7Pa6i83mXUGogAEIf1wkeb_w42UgL3Lj6jFKMlNTdwEMUnhsLkjRlhe25K4/pub). ### Bounty sizes diff --git a/examples/gno.land/r/leon/config/config.gno b/examples/gno.land/r/leon/config/config.gno index cbc1e537e3f..bc800ec8263 100644 --- a/examples/gno.land/r/leon/config/config.gno +++ b/examples/gno.land/r/leon/config/config.gno @@ -8,6 +8,9 @@ import ( var ( main std.Address // leon's main address backup std.Address // backup address + + ErrInvalidAddr = errors.New("leon's config: invalid address") + ErrUnauthorized = errors.New("leon's config: unauthorized") ) func init() { @@ -24,7 +27,7 @@ func Backup() std.Address { func SetAddress(a std.Address) error { if !a.IsValid() { - return errors.New("config: invalid address") + return ErrInvalidAddr } if err := checkAuthorized(); err != nil { @@ -37,7 +40,7 @@ func SetAddress(a std.Address) error { func SetBackup(a std.Address) error { if !a.IsValid() { - return errors.New("config: invalid address") + return ErrInvalidAddr } if err := checkAuthorized(); err != nil { @@ -50,16 +53,11 @@ func SetBackup(a std.Address) error { func checkAuthorized() error { caller := std.PrevRealm().Addr() - if caller != main || caller != backup { - return errors.New("config: unauthorized") + isAuthorized := caller == main || caller == backup + + if !isAuthorized { + return ErrUnauthorized } return nil } - -func AssertAuthorized() { - caller := std.PrevRealm().Addr() - if caller != main || caller != backup { - panic("config: unauthorized") - } -} diff --git a/examples/gno.land/r/leon/home/home.gno b/examples/gno.land/r/leon/home/home.gno index 1f6a07e8959..ba688792a4c 100644 --- a/examples/gno.land/r/leon/home/home.gno +++ b/examples/gno.land/r/leon/home/home.gno @@ -34,13 +34,19 @@ TODO import r/gh } func UpdatePFP(url, caption string) { - config.AssertAuthorized() + if !isAuthorized(std.PrevRealm().Addr()) { + panic(config.ErrUnauthorized) + } + pfp = url pfpCaption = caption } func UpdateAboutMe(col1, col2 string) { - config.AssertAuthorized() + if !isAuthorized(std.PrevRealm().Addr()) { + panic(config.ErrUnauthorized) + } + abtMe[0] = col1 abtMe[1] = col2 } @@ -119,3 +125,7 @@ func renderMillipede() string { return out } + +func isAuthorized(addr std.Address) bool { + return addr == config.Address() || addr == config.Backup() +} diff --git a/examples/gno.land/r/stefann/home/gno.mod b/examples/gno.land/r/stefann/home/gno.mod new file mode 100644 index 00000000000..dd556e7f817 --- /dev/null +++ b/examples/gno.land/r/stefann/home/gno.mod @@ -0,0 +1,9 @@ +module gno.land/r/stefann/home + +require ( + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/ownable v0.0.0-latest + gno.land/p/demo/testutils v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest + gno.land/r/stefann/registry v0.0.0-latest +) diff --git a/examples/gno.land/r/stefann/home/home.gno b/examples/gno.land/r/stefann/home/home.gno new file mode 100644 index 00000000000..f40329ebf7e --- /dev/null +++ b/examples/gno.land/r/stefann/home/home.gno @@ -0,0 +1,303 @@ +package home + +import ( + "sort" + "std" + "strings" + + "gno.land/p/demo/avl" + "gno.land/p/demo/ownable" + "gno.land/p/demo/ufmt" + + "gno.land/r/stefann/registry" +) + +type City struct { + Name string + URL string +} + +type Sponsor struct { + Address std.Address + Amount std.Coins +} + +type Profile struct { + pfp string + aboutMe []string +} + +type Travel struct { + cities []City + currentCityIndex int + jarLink string +} + +type Sponsorship struct { + maxSponsors int + sponsors *avl.Tree + DonationsCount int + sponsorsCount int +} + +var ( + profile Profile + travel Travel + sponsorship Sponsorship + owner *ownable.Ownable +) + +func init() { + owner = ownable.NewWithAddress(registry.MainAddr()) + + profile = Profile{ + pfp: "https://i.ibb.co/Bc5YNCx/DSC-0095a.jpg", + aboutMe: []string{ + `### About Me`, + `Hey there! I’m Stefan, a student of Computer Science. I’m all about exploring and adventure — whether it’s diving into the latest tech or discovering a new city, I’m always up for the challenge!`, + + `### Contributions`, + `I'm just getting started, but you can follow my journey through Gno.land right [here](https://github.com/gnolang/hackerspace/issues/94) 🔗`, + }, + } + + travel = Travel{ + cities: []City{ + {Name: "Venice", URL: "https://i.ibb.co/1mcZ7b1/venice.jpg"}, + {Name: "Tokyo", URL: "https://i.ibb.co/wNDJv3H/tokyo.jpg"}, + {Name: "São Paulo", URL: "https://i.ibb.co/yWMq2Sn/sao-paulo.jpg"}, + {Name: "Toronto", URL: "https://i.ibb.co/pb95HJB/toronto.jpg"}, + {Name: "Bangkok", URL: "https://i.ibb.co/pQy3w2g/bangkok.jpg"}, + {Name: "New York", URL: "https://i.ibb.co/6JWLm0h/new-york.jpg"}, + {Name: "Paris", URL: "https://i.ibb.co/q9vf6Hs/paris.jpg"}, + {Name: "Kandersteg", URL: "https://i.ibb.co/60DzywD/kandersteg.jpg"}, + {Name: "Rothenburg", URL: "https://i.ibb.co/cr8d2rQ/rothenburg.jpg"}, + {Name: "Capetown", URL: "https://i.ibb.co/bPGn0v3/capetown.jpg"}, + {Name: "Sydney", URL: "https://i.ibb.co/TBNzqfy/sydney.jpg"}, + {Name: "Oeschinen Lake", URL: "https://i.ibb.co/QJQwp2y/oeschinen-lake.jpg"}, + {Name: "Barra Grande", URL: "https://i.ibb.co/z4RXKc1/barra-grande.jpg"}, + {Name: "London", URL: "https://i.ibb.co/CPGtvgr/london.jpg"}, + }, + currentCityIndex: 0, + jarLink: "https://TODO", // This value should be injected through UpdateJarLink after deployment. + } + + sponsorship = Sponsorship{ + maxSponsors: 5, + sponsors: avl.NewTree(), + DonationsCount: 0, + sponsorsCount: 0, + } +} + +func UpdateCities(newCities []City) { + owner.AssertCallerIsOwner() + travel.cities = newCities +} + +func AddCities(newCities ...City) { + owner.AssertCallerIsOwner() + + travel.cities = append(travel.cities, newCities...) +} + +func UpdateJarLink(newLink string) { + owner.AssertCallerIsOwner() + travel.jarLink = newLink +} + +func UpdatePFP(url string) { + owner.AssertCallerIsOwner() + profile.pfp = url +} + +func UpdateAboutMe(aboutMeStr string) { + owner.AssertCallerIsOwner() + profile.aboutMe = strings.Split(aboutMeStr, "|") +} + +func AddAboutMeRows(newRows ...string) { + owner.AssertCallerIsOwner() + + profile.aboutMe = append(profile.aboutMe, newRows...) +} + +func UpdateMaxSponsors(newMax int) { + owner.AssertCallerIsOwner() + if newMax <= 0 { + panic("maxSponsors must be greater than zero") + } + sponsorship.maxSponsors = newMax +} + +func Donate() { + address := std.GetOrigCaller() + amount := std.GetOrigSend() + + if amount.AmountOf("ugnot") == 0 { + panic("Donation must include GNOT") + } + + existingAmount, exists := sponsorship.sponsors.Get(address.String()) + if exists { + updatedAmount := existingAmount.(std.Coins).Add(amount) + sponsorship.sponsors.Set(address.String(), updatedAmount) + } else { + sponsorship.sponsors.Set(address.String(), amount) + sponsorship.sponsorsCount++ + } + + travel.currentCityIndex++ + sponsorship.DonationsCount++ + + banker := std.GetBanker(std.BankerTypeRealmSend) + ownerAddr := registry.MainAddr() + banker.SendCoins(std.CurrentRealm().Addr(), ownerAddr, banker.GetCoins(std.CurrentRealm().Addr())) +} + +type SponsorSlice []Sponsor + +func (s SponsorSlice) Len() int { + return len(s) +} + +func (s SponsorSlice) Less(i, j int) bool { + return s[i].Amount.AmountOf("ugnot") > s[j].Amount.AmountOf("ugnot") +} + +func (s SponsorSlice) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func GetTopSponsors() []Sponsor { + var sponsorSlice SponsorSlice + + sponsorship.sponsors.Iterate("", "", func(key string, value interface{}) bool { + addr := std.Address(key) + amount := value.(std.Coins) + sponsorSlice = append(sponsorSlice, Sponsor{Address: addr, Amount: amount}) + return false + }) + + sort.Sort(sponsorSlice) + return sponsorSlice +} + +func GetTotalDonations() int { + total := 0 + sponsorship.sponsors.Iterate("", "", func(key string, value interface{}) bool { + total += int(value.(std.Coins).AmountOf("ugnot")) + return false + }) + return total +} + +func Render(path string) string { + out := ufmt.Sprintf("# Exploring %s!\n\n", travel.cities[travel.currentCityIndex].Name) + + out += renderAboutMe() + out += "\n\n" + out += renderTips() + + return out +} + +func renderAboutMe() string { + out := "
" + + out += "
\n\n" + + out += ufmt.Sprintf("
\n\n", travel.cities[travel.currentCityIndex%len(travel.cities)].URL) + + out += ufmt.Sprintf("my profile pic\n\n", profile.pfp) + + out += "
\n\n" + + for _, rows := range profile.aboutMe { + out += "
\n\n" + out += rows + "\n\n" + out += "
\n\n" + } + + out += "
\n\n" + + return out +} + +func renderTips() string { + out := `
` + "\n\n" + + out += `
` + "\n" + + out += `

Help Me Travel The World

` + "\n\n" + + out += renderTipsJar() + "\n" + + out += ufmt.Sprintf(`I am currently in %s,
tip the jar to send me somewhere else!
`, travel.cities[travel.currentCityIndex].Name) + + out += `
Click the jar, tip in GNOT coins, and watch my background change as I head to a new adventure!

` + "\n\n" + + out += renderSponsors() + + out += `
` + "\n\n" + + out += `
` + "\n" + + return out +} + +func formatAddress(address string) string { + if len(address) <= 8 { + return address + } + return address[:4] + "..." + address[len(address)-4:] +} + +func renderSponsors() string { + out := `

Sponsor Leaderboard

` + "\n" + + if sponsorship.sponsorsCount == 0 { + return out + `

No sponsors yet. Be the first to tip the jar!

` + "\n" + } + + topSponsors := GetTopSponsors() + numSponsors := len(topSponsors) + if numSponsors > sponsorship.maxSponsors { + numSponsors = sponsorship.maxSponsors + } + + out += `