From 0aa26e2b2d553cba49f9988d3aa205ba23f6efdb Mon Sep 17 00:00:00 2001 From: linhpn99 Date: Thu, 16 May 2024 14:31:40 +0700 Subject: [PATCH 01/25] logic + test cases --- examples/gno.land/p/demo/nonces/gno.mod | 7 ++ examples/gno.land/p/demo/nonces/nonces.gno | 64 ++++++++++++ .../gno.land/p/demo/nonces/nonces_test.gno | 98 +++++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 examples/gno.land/p/demo/nonces/gno.mod create mode 100644 examples/gno.land/p/demo/nonces/nonces.gno create mode 100644 examples/gno.land/p/demo/nonces/nonces_test.gno diff --git a/examples/gno.land/p/demo/nonces/gno.mod b/examples/gno.land/p/demo/nonces/gno.mod new file mode 100644 index 00000000000..9f9d7824e08 --- /dev/null +++ b/examples/gno.land/p/demo/nonces/gno.mod @@ -0,0 +1,7 @@ +module gno.land/p/demo/nonces + +require ( + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/testutils v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest +) \ No newline at end of file diff --git a/examples/gno.land/p/demo/nonces/nonces.gno b/examples/gno.land/p/demo/nonces/nonces.gno new file mode 100644 index 00000000000..48ddc1dd4d1 --- /dev/null +++ b/examples/gno.land/p/demo/nonces/nonces.gno @@ -0,0 +1,64 @@ +package nonces + +import ( + "std" + + "gno.land/p/demo/avl" + "gno.land/p/demo/ufmt" +) + +type NonceManager struct { + nonces *avl.Tree +} + +// New creates a new instance of NonceManager +func New() *NonceManager { + return &NonceManager{ + nonces: avl.NewTree(), + } +} + +// SetNonce sets the initial nonce for an address if it hasn't been set before +func (nm *NonceManager) SetNonce(owner std.Address, nonce uint64) { + found := nm.nonces.Has(owner.String()) + if !found { + nm.nonces.Set(owner.String(), nonce) + } +} + +// GetNonce returns the next unused nonce for an address +func (nm *NonceManager) GetNonce(owner std.Address) uint64 { + nonce, found := nm.nonces.Get(owner.String()) + if !found { + panic("invalid address") + } + + return nonce.(uint64) +} + +// UseNonce consumes a nonce and returns the current value, then increments the nonce +func (nm *NonceManager) UseNonce(owner std.Address) uint64 { + key := owner.String() + + val, found := nm.nonces.Get(key) + if !found { + panic("invalid address") + } + + currentNonce := val.(uint64) + + nm.nonces.Set(key, currentNonce+1) + + return currentNonce +} + +// UseCheckedNonce consumes a nonce if it matches the expected value, otherwise returns an error +func (nm *NonceManager) UseCheckedNonce(owner std.Address, nonce uint64) error { + currentNonce := nm.UseNonce(owner) + + if nonce != currentNonce { + return ufmt.Errorf("InvalidAccountNonce: expected %d, got %d for account %s", currentNonce, nonce, owner) + } + + return nil +} diff --git a/examples/gno.land/p/demo/nonces/nonces_test.gno b/examples/gno.land/p/demo/nonces/nonces_test.gno new file mode 100644 index 00000000000..a4d0830d6ef --- /dev/null +++ b/examples/gno.land/p/demo/nonces/nonces_test.gno @@ -0,0 +1,98 @@ +package nonces + +import ( + "testing" + + "gno.land/p/demo/testutils" + "gno.land/p/demo/ufmt" +) + +// TestNonceManager tests the NonceManager functions +func TestNonceManager(t *testing.T) { + nm := New() + + // Use a dummy address for testing + address := testutils.TestAddress("dummy") + + // Initialize the address in the AVL tree + nm.SetNonce(address, 0) + + // Test GetNonce method + if nonce := nm.GetNonce(address); nonce != 0 { + t.Errorf("expected nonce to be 0, got %d", nonce) + } + + // Test UseNonce method + if nonce := nm.UseNonce(address); nonce != 0 { + t.Errorf("expected used nonce to be 0, got %d", nonce) + } + + if newNonce := nm.GetNonce(address); newNonce != 1 { + t.Errorf("expected new nonce to be 1, got %d", newNonce) + } + + // Test UseCheckedNonce method with correct nonce + if err := nm.UseCheckedNonce(address, 1); err != nil { + t.Errorf("expected no error, got %v", err) + } + + if finalNonce := nm.GetNonce(address); finalNonce != 2 { + t.Errorf("expected final nonce to be 2, got %d", finalNonce) + } + + // Test UseCheckedNonce method with incorrect nonce + err := nm.UseCheckedNonce(address, 1) + if err == nil { + t.Errorf("expected error, got nil") + } + + expectedErr := ufmt.Sprintf("InvalidAccountNonce: expected 2, got 1 for account %s", address) + if err.Error() != expectedErr { + t.Errorf("expected error message '%s', got '%s'", expectedErr, err.Error()) + } +} + +func TestMultipleAddresses(t *testing.T) { + nm := New() + + // Use dummy addresses for testing + address1 := testutils.TestAddress("bob") + address2 := testutils.TestAddress("alice") + + // Initialize the addresses in the AVL tree + nm.SetNonce(address1, 0) + nm.SetNonce(address2, 0) + + // Test UseNonce with multiple addresses + if nonce := nm.UseNonce(address1); nonce != 0 { + t.Errorf("expected used nonce to be 0 for address1, got %d", nonce) + } + + if nonce := nm.UseNonce(address2); nonce != 0 { + t.Errorf("expected used nonce to be 0 for address2, got %d", nonce) + } + + if nonce := nm.UseNonce(address1); nonce != 1 { + t.Errorf("expected used nonce to be 1 for address1, got %d", nonce) + } + + if nonce := nm.UseNonce(address2); nonce != 1 { + t.Errorf("expected used nonce to be 1 for address2, got %d", nonce) + } +} + +func TestConcurrentUse(t *testing.T) { + nm := New() + address := testutils.TestAddress("dummy") + + // Initialize the address in the AVL tree + nm.SetNonce(address, 0) + + for i := 0; i < 10; i++ { + t.Parallel() + + if nonce := nm.UseNonce(address); nonce != uint64(i) { + t.Errorf("expected used nonce to be %d, got %d", i, nonce) + } + } +} From 412249758f7afaaafcce16da9d7e3e5f7acc0b69 Mon Sep 17 00:00:00 2001 From: linhpn99 Date: Thu, 16 May 2024 14:41:59 +0700 Subject: [PATCH 02/25] add new line --- examples/gno.land/p/demo/nonces/gno.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/p/demo/nonces/gno.mod b/examples/gno.land/p/demo/nonces/gno.mod index 9f9d7824e08..84ff6af9382 100644 --- a/examples/gno.land/p/demo/nonces/gno.mod +++ b/examples/gno.land/p/demo/nonces/gno.mod @@ -4,4 +4,4 @@ require ( gno.land/p/demo/avl v0.0.0-latest gno.land/p/demo/testutils v0.0.0-latest gno.land/p/demo/ufmt v0.0.0-latest -) \ No newline at end of file +) From 9f4a0651f3f7585387ebfba433956e6a54ae7da2 Mon Sep 17 00:00:00 2001 From: sunspirit99 <167175638+linhpn99@users.noreply.github.com> Date: Thu, 23 May 2024 09:06:43 +0700 Subject: [PATCH 03/25] Update examples/gno.land/p/demo/nonces/nonces.gno Co-authored-by: Manfred Touron <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/nonces/nonces.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/p/demo/nonces/nonces.gno b/examples/gno.land/p/demo/nonces/nonces.gno index 48ddc1dd4d1..e2ec31c0648 100644 --- a/examples/gno.land/p/demo/nonces/nonces.gno +++ b/examples/gno.land/p/demo/nonces/nonces.gno @@ -1,4 +1,4 @@ -package nonces +package nonce import ( "std" From a808269cbbf8138eb4f7c88e3554cf1fcb9d9ca6 Mon Sep 17 00:00:00 2001 From: sunspirit99 <167175638+linhpn99@users.noreply.github.com> Date: Thu, 23 May 2024 09:06:49 +0700 Subject: [PATCH 04/25] Update examples/gno.land/p/demo/nonces/nonces.gno Co-authored-by: Manfred Touron <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/nonces/nonces.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/p/demo/nonces/nonces.gno b/examples/gno.land/p/demo/nonces/nonces.gno index e2ec31c0648..45723e44be6 100644 --- a/examples/gno.land/p/demo/nonces/nonces.gno +++ b/examples/gno.land/p/demo/nonces/nonces.gno @@ -37,7 +37,7 @@ func (nm *NonceManager) GetNonce(owner std.Address) uint64 { } // UseNonce consumes a nonce and returns the current value, then increments the nonce -func (nm *NonceManager) UseNonce(owner std.Address) uint64 { +func (nm *NonceManager) UseNonce(addr std.Address) uint64 { key := owner.String() val, found := nm.nonces.Get(key) From 1e120b3a5d67df89d366f23d2ee5af55aa0a338a Mon Sep 17 00:00:00 2001 From: sunspirit99 <167175638+linhpn99@users.noreply.github.com> Date: Thu, 23 May 2024 09:06:54 +0700 Subject: [PATCH 05/25] Update examples/gno.land/p/demo/nonces/nonces.gno Co-authored-by: Manfred Touron <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/nonces/nonces.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/p/demo/nonces/nonces.gno b/examples/gno.land/p/demo/nonces/nonces.gno index 45723e44be6..5d74dc54638 100644 --- a/examples/gno.land/p/demo/nonces/nonces.gno +++ b/examples/gno.land/p/demo/nonces/nonces.gno @@ -19,7 +19,7 @@ func New() *NonceManager { } // SetNonce sets the initial nonce for an address if it hasn't been set before -func (nm *NonceManager) SetNonce(owner std.Address, nonce uint64) { +func (nm *NonceManager) SetNonce(addr std.Address, nonce uint64) { found := nm.nonces.Has(owner.String()) if !found { nm.nonces.Set(owner.String(), nonce) From 9c871cea477cc18a5d7ead520261de7d36d122bb Mon Sep 17 00:00:00 2001 From: sunspirit99 <167175638+linhpn99@users.noreply.github.com> Date: Thu, 23 May 2024 09:07:03 +0700 Subject: [PATCH 06/25] Update examples/gno.land/p/demo/nonces/gno.mod Co-authored-by: Manfred Touron <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/nonces/gno.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/p/demo/nonces/gno.mod b/examples/gno.land/p/demo/nonces/gno.mod index 84ff6af9382..36c71058f84 100644 --- a/examples/gno.land/p/demo/nonces/gno.mod +++ b/examples/gno.land/p/demo/nonces/gno.mod @@ -1,4 +1,4 @@ -module gno.land/p/demo/nonces +module gno.land/p/demo/nonce require ( gno.land/p/demo/avl v0.0.0-latest From 5aa74b5f8029c38f74a56f9fc1f424977040eb4f Mon Sep 17 00:00:00 2001 From: sunspirit99 <167175638+linhpn99@users.noreply.github.com> Date: Thu, 23 May 2024 09:07:10 +0700 Subject: [PATCH 07/25] Update examples/gno.land/p/demo/nonces/nonces.gno Co-authored-by: Manfred Touron <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/nonces/nonces.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/p/demo/nonces/nonces.gno b/examples/gno.land/p/demo/nonces/nonces.gno index 5d74dc54638..e239b5427f8 100644 --- a/examples/gno.land/p/demo/nonces/nonces.gno +++ b/examples/gno.land/p/demo/nonces/nonces.gno @@ -7,7 +7,7 @@ import ( "gno.land/p/demo/ufmt" ) -type NonceManager struct { +type Manager struct { nonces *avl.Tree } From 12f5fea460d7324bf426718cea016ca970089762 Mon Sep 17 00:00:00 2001 From: linhpn99 Date: Thu, 23 May 2024 09:38:59 +0700 Subject: [PATCH 08/25] refactored code after reviewing of Moul --- .../gno.land/p/demo/{nonces => nonce}/gno.mod | 0 examples/gno.land/p/demo/nonce/nonce.gno | 46 +++++++++++++ .../nonces_test.gno => nonce/nonce_test.gno} | 50 ++++----------- examples/gno.land/p/demo/nonces/nonces.gno | 64 ------------------- 4 files changed, 59 insertions(+), 101 deletions(-) rename examples/gno.land/p/demo/{nonces => nonce}/gno.mod (100%) create mode 100644 examples/gno.land/p/demo/nonce/nonce.gno rename examples/gno.land/p/demo/{nonces/nonces_test.gno => nonce/nonce_test.gno} (51%) delete mode 100644 examples/gno.land/p/demo/nonces/nonces.gno diff --git a/examples/gno.land/p/demo/nonces/gno.mod b/examples/gno.land/p/demo/nonce/gno.mod similarity index 100% rename from examples/gno.land/p/demo/nonces/gno.mod rename to examples/gno.land/p/demo/nonce/gno.mod diff --git a/examples/gno.land/p/demo/nonce/nonce.gno b/examples/gno.land/p/demo/nonce/nonce.gno new file mode 100644 index 00000000000..b5451fa30ff --- /dev/null +++ b/examples/gno.land/p/demo/nonce/nonce.gno @@ -0,0 +1,46 @@ +package nonce + +import ( + "std" + + "gno.land/p/demo/avl" + "gno.land/p/demo/ufmt" +) + +type Manager struct { + nonces *avl.Tree +} + +// New creates a new instance of NonceManager +func New() *Manager { + return &Manager{ + nonces: avl.NewTree(), + } +} + +// GetNonce returns the next unused nonce for an address +func (m *Manager) GetNonce(addr std.Address) uint64 { + key := addr.String() + + nonce, found := m.nonces.Get(key) + if !found { + m.nonces.Set(key, 0) + return 0 + } + + return nonce.(uint64) +} + +// UseNonce consumes a nonce and returns the current value, then increments the nonce +func (m *Manager) UseNonce(addr std.Address) { + key := addr.String() + + var currentNonce uint64 = 0 + + val, found := m.nonces.Get(key) + if found { + currentNonce = val.(uint64) + } + + m.nonces.Set(key, currentNonce+1) +} diff --git a/examples/gno.land/p/demo/nonces/nonces_test.gno b/examples/gno.land/p/demo/nonce/nonce_test.gno similarity index 51% rename from examples/gno.land/p/demo/nonces/nonces_test.gno rename to examples/gno.land/p/demo/nonce/nonce_test.gno index a4d0830d6ef..f5b6ddd5fce 100644 --- a/examples/gno.land/p/demo/nonces/nonces_test.gno +++ b/examples/gno.land/p/demo/nonce/nonce_test.gno @@ -1,4 +1,4 @@ -package nonces +package nonce import ( "testing" @@ -7,49 +7,28 @@ import ( "gno.land/p/demo/ufmt" ) -// TestNonceManager tests the NonceManager functions +// TestNonceManager tests the Manager functions func TestNonceManager(t *testing.T) { nm := New() // Use a dummy address for testing address := testutils.TestAddress("dummy") - // Initialize the address in the AVL tree - nm.SetNonce(address, 0) - // Test GetNonce method if nonce := nm.GetNonce(address); nonce != 0 { t.Errorf("expected nonce to be 0, got %d", nonce) } // Test UseNonce method - if nonce := nm.UseNonce(address); nonce != 0 { - t.Errorf("expected used nonce to be 0, got %d", nonce) - } - + nm.UseNonce(address) if newNonce := nm.GetNonce(address); newNonce != 1 { t.Errorf("expected new nonce to be 1, got %d", newNonce) } - // Test UseCheckedNonce method with correct nonce - if err := nm.UseCheckedNonce(address, 1); err != nil { - t.Errorf("expected no error, got %v", err) - } - + nm.UseNonce(address) if finalNonce := nm.GetNonce(address); finalNonce != 2 { t.Errorf("expected final nonce to be 2, got %d", finalNonce) } - - // Test UseCheckedNonce method with incorrect nonce - err := nm.UseCheckedNonce(address, 1) - if err == nil { - t.Errorf("expected error, got nil") - } - - expectedErr := ufmt.Sprintf("InvalidAccountNonce: expected 2, got 1 for account %s", address) - if err.Error() != expectedErr { - t.Errorf("expected error message '%s', got '%s'", expectedErr, err.Error()) - } } func TestMultipleAddresses(t *testing.T) { @@ -59,23 +38,22 @@ func TestMultipleAddresses(t *testing.T) { address1 := testutils.TestAddress("bob") address2 := testutils.TestAddress("alice") - // Initialize the addresses in the AVL tree - nm.SetNonce(address1, 0) - nm.SetNonce(address2, 0) - // Test UseNonce with multiple addresses - if nonce := nm.UseNonce(address1); nonce != 0 { - t.Errorf("expected used nonce to be 0 for address1, got %d", nonce) + + if nonce := nm.GetNonce(address1); nonce != 0 { + t.Errorf("expected final nonce to be 0, got %d", finalNonce) } - if nonce := nm.UseNonce(address2); nonce != 0 { - t.Errorf("expected used nonce to be 0 for address2, got %d", nonce) + if nonce := nm.GetNonce(address1); nonce != 0 { + t.Errorf("expected final nonce to be 0, got %d", finalNonce) } + nm.UseNonce(address1) if nonce := nm.UseNonce(address1); nonce != 1 { t.Errorf("expected used nonce to be 1 for address1, got %d", nonce) } + nm.UseNonce(address2) if nonce := nm.UseNonce(address2); nonce != 1 { t.Errorf("expected used nonce to be 1 for address2, got %d", nonce) } @@ -85,13 +63,11 @@ func TestConcurrentUse(t *testing.T) { nm := New() address := testutils.TestAddress("dummy") - // Initialize the address in the AVL tree - nm.SetNonce(address, 0) - for i := 0; i < 10; i++ { t.Parallel() - if nonce := nm.UseNonce(address); nonce != uint64(i) { + nm.UseNonce(address) + if nonce := nm.GetNonce(address); nonce != uint64(i) { t.Errorf("expected used nonce to be %d, got %d", i, nonce) } } diff --git a/examples/gno.land/p/demo/nonces/nonces.gno b/examples/gno.land/p/demo/nonces/nonces.gno deleted file mode 100644 index e239b5427f8..00000000000 --- a/examples/gno.land/p/demo/nonces/nonces.gno +++ /dev/null @@ -1,64 +0,0 @@ -package nonce - -import ( - "std" - - "gno.land/p/demo/avl" - "gno.land/p/demo/ufmt" -) - -type Manager struct { - nonces *avl.Tree -} - -// New creates a new instance of NonceManager -func New() *NonceManager { - return &NonceManager{ - nonces: avl.NewTree(), - } -} - -// SetNonce sets the initial nonce for an address if it hasn't been set before -func (nm *NonceManager) SetNonce(addr std.Address, nonce uint64) { - found := nm.nonces.Has(owner.String()) - if !found { - nm.nonces.Set(owner.String(), nonce) - } -} - -// GetNonce returns the next unused nonce for an address -func (nm *NonceManager) GetNonce(owner std.Address) uint64 { - nonce, found := nm.nonces.Get(owner.String()) - if !found { - panic("invalid address") - } - - return nonce.(uint64) -} - -// UseNonce consumes a nonce and returns the current value, then increments the nonce -func (nm *NonceManager) UseNonce(addr std.Address) uint64 { - key := owner.String() - - val, found := nm.nonces.Get(key) - if !found { - panic("invalid address") - } - - currentNonce := val.(uint64) - - nm.nonces.Set(key, currentNonce+1) - - return currentNonce -} - -// UseCheckedNonce consumes a nonce if it matches the expected value, otherwise returns an error -func (nm *NonceManager) UseCheckedNonce(owner std.Address, nonce uint64) error { - currentNonce := nm.UseNonce(owner) - - if nonce != currentNonce { - return ufmt.Errorf("InvalidAccountNonce: expected %d, got %d for account %s", currentNonce, nonce, owner) - } - - return nil -} From 386f78e9277a8d6b7ce4bdb84440c08690860f95 Mon Sep 17 00:00:00 2001 From: linhpn99 Date: Thu, 23 May 2024 10:29:38 +0700 Subject: [PATCH 09/25] remove new line --- examples/gno.land/p/demo/nonce/gno.mod | 2 +- examples/gno.land/p/demo/nonce/nonce.gno | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/p/demo/nonce/gno.mod b/examples/gno.land/p/demo/nonce/gno.mod index 36c71058f84..9a6a962d206 100644 --- a/examples/gno.land/p/demo/nonce/gno.mod +++ b/examples/gno.land/p/demo/nonce/gno.mod @@ -4,4 +4,4 @@ require ( gno.land/p/demo/avl v0.0.0-latest gno.land/p/demo/testutils v0.0.0-latest gno.land/p/demo/ufmt v0.0.0-latest -) +) \ No newline at end of file diff --git a/examples/gno.land/p/demo/nonce/nonce.gno b/examples/gno.land/p/demo/nonce/nonce.gno index b5451fa30ff..6ebac7eeaba 100644 --- a/examples/gno.land/p/demo/nonce/nonce.gno +++ b/examples/gno.land/p/demo/nonce/nonce.gno @@ -11,7 +11,7 @@ type Manager struct { nonces *avl.Tree } -// New creates a new instance of NonceManager +// New creates a new instance of Manager func New() *Manager { return &Manager{ nonces: avl.NewTree(), From fe78475fb8dabf6a677fd40d92999fc07639aec5 Mon Sep 17 00:00:00 2001 From: linhpn99 Date: Thu, 23 May 2024 10:31:00 +0700 Subject: [PATCH 10/25] remove ufmt --- examples/gno.land/p/demo/nonce/gno.mod | 1 - examples/gno.land/p/demo/nonce/nonce.gno | 1 - examples/gno.land/p/demo/nonce/nonce_test.gno | 1 - 3 files changed, 3 deletions(-) diff --git a/examples/gno.land/p/demo/nonce/gno.mod b/examples/gno.land/p/demo/nonce/gno.mod index 9a6a962d206..4f981796243 100644 --- a/examples/gno.land/p/demo/nonce/gno.mod +++ b/examples/gno.land/p/demo/nonce/gno.mod @@ -3,5 +3,4 @@ module gno.land/p/demo/nonce require ( gno.land/p/demo/avl v0.0.0-latest gno.land/p/demo/testutils v0.0.0-latest - gno.land/p/demo/ufmt v0.0.0-latest ) \ No newline at end of file diff --git a/examples/gno.land/p/demo/nonce/nonce.gno b/examples/gno.land/p/demo/nonce/nonce.gno index 6ebac7eeaba..a0a6b8c6231 100644 --- a/examples/gno.land/p/demo/nonce/nonce.gno +++ b/examples/gno.land/p/demo/nonce/nonce.gno @@ -4,7 +4,6 @@ import ( "std" "gno.land/p/demo/avl" - "gno.land/p/demo/ufmt" ) type Manager struct { diff --git a/examples/gno.land/p/demo/nonce/nonce_test.gno b/examples/gno.land/p/demo/nonce/nonce_test.gno index f5b6ddd5fce..fde209f4176 100644 --- a/examples/gno.land/p/demo/nonce/nonce_test.gno +++ b/examples/gno.land/p/demo/nonce/nonce_test.gno @@ -4,7 +4,6 @@ import ( "testing" "gno.land/p/demo/testutils" - "gno.land/p/demo/ufmt" ) // TestNonceManager tests the Manager functions From 4b1bbaf8376b7cb7f63e76d1cccd077bdb286c3a Mon Sep 17 00:00:00 2001 From: linhpn99 Date: Thu, 23 May 2024 10:35:36 +0700 Subject: [PATCH 11/25] remove require --- examples/gno.land/p/demo/nonce/nonce_test.gno | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/p/demo/nonce/nonce_test.gno b/examples/gno.land/p/demo/nonce/nonce_test.gno index fde209f4176..23089042060 100644 --- a/examples/gno.land/p/demo/nonce/nonce_test.gno +++ b/examples/gno.land/p/demo/nonce/nonce_test.gno @@ -40,11 +40,11 @@ func TestMultipleAddresses(t *testing.T) { // Test UseNonce with multiple addresses if nonce := nm.GetNonce(address1); nonce != 0 { - t.Errorf("expected final nonce to be 0, got %d", finalNonce) + t.Errorf("expected final nonce to be 0, got %d", nonce) } if nonce := nm.GetNonce(address1); nonce != 0 { - t.Errorf("expected final nonce to be 0, got %d", finalNonce) + t.Errorf("expected final nonce to be 0, got %d", nonce) } nm.UseNonce(address1) From 944e652f4821f1ce81421259ebb5fce5f2058c11 Mon Sep 17 00:00:00 2001 From: linhpn99 Date: Thu, 23 May 2024 10:38:26 +0700 Subject: [PATCH 12/25] remove require --- examples/gno.land/p/demo/nonce/gno.mod | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/examples/gno.land/p/demo/nonce/gno.mod b/examples/gno.land/p/demo/nonce/gno.mod index 4f981796243..1ce91b497fd 100644 --- a/examples/gno.land/p/demo/nonce/gno.mod +++ b/examples/gno.land/p/demo/nonce/gno.mod @@ -1,6 +1 @@ -module gno.land/p/demo/nonce - -require ( - gno.land/p/demo/avl v0.0.0-latest - gno.land/p/demo/testutils v0.0.0-latest -) \ No newline at end of file +module gno.land/p/demo/nonce \ No newline at end of file From 593376bcdee1f99abbaf9be94d3d2fd986880573 Mon Sep 17 00:00:00 2001 From: linhpn99 Date: Thu, 23 May 2024 10:41:54 +0700 Subject: [PATCH 13/25] update --- examples/gno.land/p/demo/nonce/nonce_test.gno | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/p/demo/nonce/nonce_test.gno b/examples/gno.land/p/demo/nonce/nonce_test.gno index 23089042060..3e7319d59a2 100644 --- a/examples/gno.land/p/demo/nonce/nonce_test.gno +++ b/examples/gno.land/p/demo/nonce/nonce_test.gno @@ -48,12 +48,12 @@ func TestMultipleAddresses(t *testing.T) { } nm.UseNonce(address1) - if nonce := nm.UseNonce(address1); nonce != 1 { + if nonce := nm.GetNonce(address1); nonce != 1 { t.Errorf("expected used nonce to be 1 for address1, got %d", nonce) } nm.UseNonce(address2) - if nonce := nm.UseNonce(address2); nonce != 1 { + if nonce := nm.GetNonce(address2); nonce != 1 { t.Errorf("expected used nonce to be 1 for address2, got %d", nonce) } } From 8b03000aba31a76cd9ea1fa4bd171833a45d982b Mon Sep 17 00:00:00 2001 From: linhpn99 Date: Thu, 23 May 2024 16:22:38 +0700 Subject: [PATCH 14/25] wrong type --- examples/gno.land/p/demo/nonce/nonce.gno | 2 +- examples/gno.land/p/demo/nonce/nonce_test.gno | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/gno.land/p/demo/nonce/nonce.gno b/examples/gno.land/p/demo/nonce/nonce.gno index a0a6b8c6231..f55843387ce 100644 --- a/examples/gno.land/p/demo/nonce/nonce.gno +++ b/examples/gno.land/p/demo/nonce/nonce.gno @@ -23,7 +23,7 @@ func (m *Manager) GetNonce(addr std.Address) uint64 { nonce, found := m.nonces.Get(key) if !found { - m.nonces.Set(key, 0) + m.nonces.Set(key, uint64(0)) return 0 } diff --git a/examples/gno.land/p/demo/nonce/nonce_test.gno b/examples/gno.land/p/demo/nonce/nonce_test.gno index 3e7319d59a2..d7948ed6d31 100644 --- a/examples/gno.land/p/demo/nonce/nonce_test.gno +++ b/examples/gno.land/p/demo/nonce/nonce_test.gno @@ -43,7 +43,7 @@ func TestMultipleAddresses(t *testing.T) { t.Errorf("expected final nonce to be 0, got %d", nonce) } - if nonce := nm.GetNonce(address1); nonce != 0 { + if nonce := nm.GetNonce(address2); nonce != 0 { t.Errorf("expected final nonce to be 0, got %d", nonce) } @@ -66,8 +66,8 @@ func TestConcurrentUse(t *testing.T) { t.Parallel() nm.UseNonce(address) - if nonce := nm.GetNonce(address); nonce != uint64(i) { - t.Errorf("expected used nonce to be %d, got %d", i, nonce) + if nonce := nm.GetNonce(address); nonce != uint64(i+1) { + t.Errorf("expected used nonce to be %d, got %d", i+1, nonce) } } } From 84cf5cb42410ba2ec48fc93e10d9a53afa2daf1b Mon Sep 17 00:00:00 2001 From: linhpn99 Date: Thu, 23 May 2024 17:24:26 +0700 Subject: [PATCH 15/25] add new line --- examples/gno.land/p/demo/nonce/gno.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/p/demo/nonce/gno.mod b/examples/gno.land/p/demo/nonce/gno.mod index 1ce91b497fd..c9466847743 100644 --- a/examples/gno.land/p/demo/nonce/gno.mod +++ b/examples/gno.land/p/demo/nonce/gno.mod @@ -1 +1 @@ -module gno.land/p/demo/nonce \ No newline at end of file +module gno.land/p/demo/nonce From aceb6a22f6e9a30f62861c2009bab02e812f7662 Mon Sep 17 00:00:00 2001 From: linhpn99 Date: Sun, 26 May 2024 20:26:43 +0700 Subject: [PATCH 16/25] add new line --- examples/gno.land/p/demo/nonce/gno.mod | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/gno.land/p/demo/nonce/gno.mod b/examples/gno.land/p/demo/nonce/gno.mod index c9466847743..15e62db5e36 100644 --- a/examples/gno.land/p/demo/nonce/gno.mod +++ b/examples/gno.land/p/demo/nonce/gno.mod @@ -1 +1,3 @@ module gno.land/p/demo/nonce + +require gno.land/p/demo/avl v0.0.0-latest From b9355b5c74efb618ab8ee5c732b844ddb8294dd1 Mon Sep 17 00:00:00 2001 From: linhpn99 Date: Sun, 26 May 2024 20:31:16 +0700 Subject: [PATCH 17/25] add import --- examples/gno.land/p/demo/nonce/gno.mod | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/gno.land/p/demo/nonce/gno.mod b/examples/gno.land/p/demo/nonce/gno.mod index 15e62db5e36..9219f6e9967 100644 --- a/examples/gno.land/p/demo/nonce/gno.mod +++ b/examples/gno.land/p/demo/nonce/gno.mod @@ -1,3 +1,7 @@ module gno.land/p/demo/nonce -require gno.land/p/demo/avl v0.0.0-latest +require ( + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/testutils v0.0.0-latest +) + From e39e79d9a6b47741c3c573e9e92e164378a9b3c8 Mon Sep 17 00:00:00 2001 From: linhpn99 Date: Sun, 26 May 2024 20:35:04 +0700 Subject: [PATCH 18/25] make tidy --- examples/gno.land/p/demo/nonce/gno.mod | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/gno.land/p/demo/nonce/gno.mod b/examples/gno.land/p/demo/nonce/gno.mod index 9219f6e9967..7d560e622ce 100644 --- a/examples/gno.land/p/demo/nonce/gno.mod +++ b/examples/gno.land/p/demo/nonce/gno.mod @@ -4,4 +4,3 @@ require ( gno.land/p/demo/avl v0.0.0-latest gno.land/p/demo/testutils v0.0.0-latest ) - From b6a8cf572dac270ee477ca5e80ba01f5ad2682bb Mon Sep 17 00:00:00 2001 From: linhpn99 Date: Tue, 28 May 2024 15:50:27 +0700 Subject: [PATCH 19/25] draft --- examples/gno.land/r/demo/voting/gno.mod | 6 + .../gno.land/r/demo/voting/simple_voting.gno | 150 ++++++++++++++++++ .../r/demo/voting/simple_voting_test.gno | 137 ++++++++++++++++ 3 files changed, 293 insertions(+) create mode 100644 examples/gno.land/r/demo/voting/gno.mod create mode 100644 examples/gno.land/r/demo/voting/simple_voting.gno create mode 100644 examples/gno.land/r/demo/voting/simple_voting_test.gno diff --git a/examples/gno.land/r/demo/voting/gno.mod b/examples/gno.land/r/demo/voting/gno.mod new file mode 100644 index 00000000000..d2799fd4d94 --- /dev/null +++ b/examples/gno.land/r/demo/voting/gno.mod @@ -0,0 +1,6 @@ +module gno.land/r/demo/voting + +require ( + gno.land/p/demo/bitmap v0.0.0-latest + gno.land/p/demo/nonces v0.0.0-latest +) \ No newline at end of file diff --git a/examples/gno.land/r/demo/voting/simple_voting.gno b/examples/gno.land/r/demo/voting/simple_voting.gno new file mode 100644 index 00000000000..b3adedbe286 --- /dev/null +++ b/examples/gno.land/r/demo/voting/simple_voting.gno @@ -0,0 +1,150 @@ +package voting + +import ( + "errors" + "std" + + "gno.land/p/demo/avl" + "gno.land/p/demo/bitmap" + "gno.land/p/demo/nonce" + "gno.land/p/demo/seqid" +) + +// Proposal represents a voting proposal with associated metadata and voting state +type Proposal struct { + owner std.Address // Owner is the address of the user who created the proposal + title string // Title is the name or description of the proposal + votes uint64 // Votes is the total number of votes the proposal has received + voters *bitmap.Bitmap // Voters is a bitmap tracking whether users have voted + userVoteCount *nonce.Manager // UserVoteCount maps user addresses to the number of votes they have cast + maxVotesPerUser uint64 // MaxVotesPerUser is the maximum number of votes a single user can cast + isClosed bool // IsClosed indicates whether the proposal is closed for voting +} + +var ( + voterIndex seqid.ID // Sequential ID generator for voters + proposalIndex seqid.ID // Sequential ID generator for proposals + + proposals *avl.Tree // AVL tree to store proposals + registeredVoters *avl.Tree // AVL tree to store registered voters +) + +// init function initializes the voter and proposal indices and AVL trees +func Init() { + voterIndex = 0 + proposalIndex = 0 + + proposals = avl.NewTree() + registeredVoters = avl.NewTree() +} + +// CreateProposal creates a new proposal with the given title, number of participants, and max votes per user +func CreateProposal(title string, numParticipants uint64, maxVotesPerUser uint64) string { + proposal := &Proposal{ + owner: std.PrevRealm().Addr(), + title: title, + votes: 0, + voters: bitmap.New(numParticipants), + userVoteCount: nonce.New(), + maxVotesPerUser: maxVotesPerUser, + isClosed: false, + } + + // Get the next proposal ID + proposalID := proposalIndex.Next() + + // Store the proposal in the AVL tree + proposals.Set(proposalID.String(), proposal) + + // Return the proposal ID as a string + return proposalID.String() +} + +// RegisterVoter registers a new voter and returns a voter ID +func RegisterVoter() string { + // Get the caller's address + caller := std.PrevRealm().Addr() + + // Check if the address is already registered + if voter, exists := registeredVoters.Get(caller.String()); exists { + voterId := voter.(string) + // Return the existing voter ID + return voterId + } + + // Get the next voter ID + voterID := voterIndex.Next() + + // Store the voter in the AVL tree + registeredVoters.Set(caller.String(), voterID.String()) + + // Return the new voter ID + return voterID.String() +} + +// Vote casts a vote for a proposal identified by proposalID and voterID +func Vote(proposalID, voterID string) error { + // Get the caller's address + caller := std.PrevRealm().Addr() + + var voter seqid.ID + var exists bool + + // Check if the voter is registered + if voter, exists := registeredVoters.Get(caller.String()); exists { + if voter.(string) != voterID { + // Voter ID does not match + return errors.New("invalid voterID") + } + } else { + // Voter is not registered + return errors.New("must register as voter first") + } + + // Get the proposal + proposal, exists := proposals.Get(proposalID) + if !exists { + // Proposal does not exist + return errors.New("proposal not found") + } + + prop := proposal.(*Proposal) + + if prop.isClosed { + // Proposal is closed for voting + return errors.New("proposal is closed") + } + + // Get the current vote count for the caller + currentNonce := prop.userVoteCount.GetNonce(caller) + // Validate nonce + if currentNonce >= prop.maxVotesPerUser { + // User has reached the vote limit + return errors.New("reach limit vote per user") + } + + // Mark the voter as having voted and increment vote count + prop.voters.MustSet(uint64(voter)) + + prop.votes++ + + // Increment the user's vote count + prop.userVoteCount.UseNonce(caller) + + return nil +} + +// GetVotes returns the current vote count for a proposal identified by proposalID +func GetVotes(proposalID string) (uint64, error) { + // Get the proposal + proposal, exists := proposals.Get(proposalID) + if !exists { + // Proposal does not exist + return 0, errors.New("proposal not found") + } + + prop := proposal.(*Proposal) + + // Return the vote count + return prop.votes, nil +} diff --git a/examples/gno.land/r/demo/voting/simple_voting_test.gno b/examples/gno.land/r/demo/voting/simple_voting_test.gno new file mode 100644 index 00000000000..a5515459f7f --- /dev/null +++ b/examples/gno.land/r/demo/voting/simple_voting_test.gno @@ -0,0 +1,137 @@ +package voting + +import ( + "std" + "testing" + + "gno.land/p/demo/bitmap" + "gno.land/p/demo/nonce" + "gno.land/p/demo/seqid" + "gno.land/p/demo/testutils" +) + +// TestCreateProposal tests the creation of a proposal +func TestCreateProposal(t *testing.T) { + Init() + + title := "Test Proposal" + numParticipants := uint64(100) + maxVotesPerUser := uint64(5) + + proposalID := CreateProposal(title, numParticipants, maxVotesPerUser) + + if proposalID == "" { + t.Errorf("Expected proposalID, got empty string") + } + + proposal, exists := proposals.Get(proposalID) + if !exists { + t.Errorf("Proposal not found") + } + + prop := proposal.(*Proposal) + if prop.title != title { + t.Errorf("Expected title %s, got %s", title, prop.title) + } + + if prop.maxVotesPerUser != maxVotesPerUser { + t.Errorf("Expected maxVotesPerUser %d, got %d", maxVotesPerUser, prop.maxVotesPerUser) + } + + // if prop.voters.Size() != numParticipants { + // t.Errorf("Expected total voters %d, got %d", numParticipants, prop.voters.Size()) + // } + + if uint64(proposalIndex) != 1 { + t.Errorf("Expected proposalIndex %d, got %d", 1, uint64(proposalIndex)) + } +} + +// TestRegisterVoter tests voter registration +func TestRegisterVoter(t *testing.T) { + Init() + + address := testutils.TestAddress("dummy") + std.TestSetPrevAddr(address) + + voterID := RegisterVoter() + if voterID == "" { + t.Errorf("Expected voterID, got empty string") + } + + voter, exists := registeredVoters.Get(address.String()) + if !exists { + t.Errorf("Voter not found") + } + + if voter.(string) != voterID { + t.Errorf("Expected voterID %s, got %s", voterID, voter.(string)) + } +} + +// TestVote tests voting on a proposal +func TestVote(t *testing.T) { + Init() + + title := "Test Proposal" + numParticipants := uint64(100) + maxVotesPerUser := uint64(5) + + owner := testutils.TestAddress("owner") + std.TestSetPrevAddr(owner) + + proposalID := CreateProposal(title, numParticipants, maxVotesPerUser) + + voter := testutils.TestAddress("voter") + std.TestSetPrevAddr(voter) + + voterID := RegisterVoter() + + err := Vote(proposalID, voterID) + if err != nil { + t.Errorf("Vote failed: %v", err) + } + + votes, err := GetVotes(proposalID) + if err != nil { + t.Errorf("GetVotes failed: %v", err) + } + if votes != 1 { + t.Errorf("Expected 1 vote, got %d", votes) + } +} + +// TestVoteLimit tests voting limit per user +func TestVoteLimit(t *testing.T) { + Init() + + title := "Test Proposal" + numParticipants := uint64(100) + maxVotesPerUser := uint64(2) + + owner := testutils.TestAddress("owner") + std.TestSetPrevAddr(owner) + + proposalID := CreateProposal(title, numParticipants, maxVotesPerUser) + + voter := testutils.TestAddress("voter") + + std.TestSetPrevAddr(voter) + + voterID := RegisterVoter() + + err := Vote(proposalID, voterID) + if err != nil { + t.Errorf("Vote failed: %v", err) + } + + err = Vote(proposalID, voterID) + if err != nil { + t.Errorf("Vote failed: %v", err) + } + + err = Vote(proposalID, voterID) + if err == nil { + t.Errorf("Expected vote limit error, got nil") + } +} From 0874ae3ebb5039df57946b3242ef1b48a5bb2f2a Mon Sep 17 00:00:00 2001 From: linhpn99 Date: Tue, 28 May 2024 15:56:07 +0700 Subject: [PATCH 20/25] make tidy --- examples/gno.land/r/demo/voting/gno.mod | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/r/demo/voting/gno.mod b/examples/gno.land/r/demo/voting/gno.mod index d2799fd4d94..128adc7f5fa 100644 --- a/examples/gno.land/r/demo/voting/gno.mod +++ b/examples/gno.land/r/demo/voting/gno.mod @@ -1,6 +1,9 @@ module gno.land/r/demo/voting require ( + gno.land/p/demo/avl v0.0.0-latest gno.land/p/demo/bitmap v0.0.0-latest - gno.land/p/demo/nonces v0.0.0-latest -) \ No newline at end of file + gno.land/p/demo/nonce v0.0.0-latest + gno.land/p/demo/seqid v0.0.0-latest + gno.land/p/demo/testutils v0.0.0-latest +) From 6c5fc07f67673975311914b9c8ee8d895b47a04b Mon Sep 17 00:00:00 2001 From: linhpn99 Date: Tue, 28 May 2024 16:03:05 +0700 Subject: [PATCH 21/25] remove useless code --- examples/gno.land/r/demo/voting/simple_voting_test.gno | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/gno.land/r/demo/voting/simple_voting_test.gno b/examples/gno.land/r/demo/voting/simple_voting_test.gno index a5515459f7f..75e177b28ec 100644 --- a/examples/gno.land/r/demo/voting/simple_voting_test.gno +++ b/examples/gno.land/r/demo/voting/simple_voting_test.gno @@ -4,9 +4,6 @@ import ( "std" "testing" - "gno.land/p/demo/bitmap" - "gno.land/p/demo/nonce" - "gno.land/p/demo/seqid" "gno.land/p/demo/testutils" ) From 28f74c9224cc4aad4b57f8c0fdf14d84fd709155 Mon Sep 17 00:00:00 2001 From: linhpn99 Date: Tue, 28 May 2024 16:11:07 +0700 Subject: [PATCH 22/25] undefined packages --- examples/gno.land/r/demo/voting/gno.mod | 7 ------- 1 file changed, 7 deletions(-) diff --git a/examples/gno.land/r/demo/voting/gno.mod b/examples/gno.land/r/demo/voting/gno.mod index 128adc7f5fa..8b9f3ea6325 100644 --- a/examples/gno.land/r/demo/voting/gno.mod +++ b/examples/gno.land/r/demo/voting/gno.mod @@ -1,9 +1,2 @@ module gno.land/r/demo/voting -require ( - gno.land/p/demo/avl v0.0.0-latest - gno.land/p/demo/bitmap v0.0.0-latest - gno.land/p/demo/nonce v0.0.0-latest - gno.land/p/demo/seqid v0.0.0-latest - gno.land/p/demo/testutils v0.0.0-latest -) From 135d365f200012d53398e264aebeb547071a4d69 Mon Sep 17 00:00:00 2001 From: linhpn99 Date: Tue, 28 May 2024 16:15:32 +0700 Subject: [PATCH 23/25] add bitmap --- examples/gno.land/p/demo/bitmap/bitmap.gno | 58 +++++++++++ .../gno.land/p/demo/bitmap/bitmap_test.gno | 96 +++++++++++++++++++ examples/gno.land/p/demo/bitmap/gno.mod | 3 + examples/gno.land/r/demo/voting/gno.mod | 7 ++ 4 files changed, 164 insertions(+) create mode 100644 examples/gno.land/p/demo/bitmap/bitmap.gno create mode 100644 examples/gno.land/p/demo/bitmap/bitmap_test.gno create mode 100644 examples/gno.land/p/demo/bitmap/gno.mod diff --git a/examples/gno.land/p/demo/bitmap/bitmap.gno b/examples/gno.land/p/demo/bitmap/bitmap.gno new file mode 100644 index 00000000000..a351d8c5e54 --- /dev/null +++ b/examples/gno.land/p/demo/bitmap/bitmap.gno @@ -0,0 +1,58 @@ +package bitmap + +import ( + "gno.land/p/demo/ufmt" +) + +// A simple implementation of Bitmap + +// Bitmap represents a bitmap using a slice of bytes +type Bitmap struct { + data []byte +} + +// NewBitmap creates a new Bitmap with a specific size (in bits) +func New(size uint64) *Bitmap { + byteSize := (size + 7) / 8 // Calculate the number of bytes needed + return &Bitmap{ + data: make([]byte, byteSize), + } +} + +// MustSet sets the bit at the given index (0-based) and returns an error if out of bounds +func (b *Bitmap) Set(index uint64) error { + if index >= uint64(len(b.data))*8 { + return ufmt.Errorf("Index out of bounds") + } + + byteIndex := index / 8 + bitIndex := index % 8 + + b.data[byteIndex] |= 1 << uint(bitIndex) // Set the corresponding bit using bitwise OR + + return nil +} + +// Set sets the bit at the given index (0-based) +func (b *Bitmap) MustSet(index uint64) { + if index >= uint64(len(b.data))*8 { + panic("Index out of bounds") + } + + byteIndex := index / 8 + bitIndex := index % 8 + + b.data[byteIndex] |= 1 << uint(bitIndex) // Set the corresponding bit using bitwise OR +} + +// Get checks if the bit at the given index is set +func (b *Bitmap) Get(index uint64) bool { + if index >= uint64(len(b.data))*8 { + panic("Index out of bounds") + } + + byteIndex := index / 8 + bitIndex := index % 8 + + return b.data[byteIndex]&(1< 0 // Check if the corresponding bit is set using bitwise AND +} diff --git a/examples/gno.land/p/demo/bitmap/bitmap_test.gno b/examples/gno.land/p/demo/bitmap/bitmap_test.gno new file mode 100644 index 00000000000..78641f6e02c --- /dev/null +++ b/examples/gno.land/p/demo/bitmap/bitmap_test.gno @@ -0,0 +1,96 @@ +package bitmap + +import ( + "testing" +) + +func TestBitmap_New(t *testing.T) { + size := uint64(16) + bm := New(size) + + if len(bm.data)*8 != int(size) { + t.Errorf("Expected bitmap size: %d, got: %d", size, len(bm.data)*8) + } +} + +func TestBitmap_SetAndGet(t *testing.T) { + bm := New(16) + + // Test setting and getting bits within bounds + err := bm.Set(2) + if err != nil { + t.Errorf("Expected no error, got: %v", err) + } + + if !bm.Get(2) { + t.Errorf("Expected bit at index 2 to be set") + } + + err = bm.Set(15) + if err != nil { + t.Errorf("Expected no error, got: %v", err) + } + + if !bm.Get(15) { + t.Errorf("Expected bit at index 15 to be set") + } + + // Test setting and getting bits out of bounds + err = bm.Set(16) + if err == nil { + t.Errorf("Expected error, got: nil") + } +} + +func TestBitmap_MustSet(t *testing.T) { + bm := New(16) + + // Test setting bits within bounds + defer func() { + if r := recover(); r != nil { + t.Errorf("Expected no panic, but got panic: %v", r) + } + }() + + bm.MustSet(2) + if !bm.Get(2) { + t.Errorf("Expected bit at index 2 to be set") + } + + bm.MustSet(15) + if !bm.Get(15) { + t.Errorf("Expected bit at index 15 to be set") + } + + // Test setting bits out of bounds + defer func() { + if r := recover(); r == nil { + t.Errorf("Expected panic, but no panic occurred") + } + }() + + bm.MustSet(16 * 8) +} + +func TestBitmap_Get(t *testing.T) { + bm := New(16) + + // Test getting bits within bounds + bm.MustSet(2) + if !bm.Get(2) { + t.Errorf("Expected bit at index 2 to be set") + } + + if bm.Get(3) { + t.Errorf("Expected bit at index 3 to be unset") + } + + // Test getting bits out of bounds + defer func() { + if r := recover(); r == nil { + t.Errorf("Expected panic, but no panic occurred") + } + }() + + bm.Get(16 * 8) +} diff --git a/examples/gno.land/p/demo/bitmap/gno.mod b/examples/gno.land/p/demo/bitmap/gno.mod new file mode 100644 index 00000000000..57cb384bee6 --- /dev/null +++ b/examples/gno.land/p/demo/bitmap/gno.mod @@ -0,0 +1,3 @@ +module gno.land/p/demo/bitmap + +require gno.land/p/demo/ufmt v0.0.0-latest diff --git a/examples/gno.land/r/demo/voting/gno.mod b/examples/gno.land/r/demo/voting/gno.mod index 8b9f3ea6325..128adc7f5fa 100644 --- a/examples/gno.land/r/demo/voting/gno.mod +++ b/examples/gno.land/r/demo/voting/gno.mod @@ -1,2 +1,9 @@ module gno.land/r/demo/voting +require ( + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/bitmap v0.0.0-latest + gno.land/p/demo/nonce v0.0.0-latest + gno.land/p/demo/seqid v0.0.0-latest + gno.land/p/demo/testutils v0.0.0-latest +) From ddb5a9ef039d0f425cfdcf6cb8568ef304cd9d85 Mon Sep 17 00:00:00 2001 From: linhpn99 Date: Tue, 28 May 2024 17:24:51 +0700 Subject: [PATCH 24/25] add GetOwner --- .../gno.land/r/demo/voting/simple_voting.gno | 41 +++++++++++++++++++ .../r/demo/voting/simple_voting_test.gno | 13 ++++-- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/examples/gno.land/r/demo/voting/simple_voting.gno b/examples/gno.land/r/demo/voting/simple_voting.gno index b3adedbe286..6a97209b3c7 100644 --- a/examples/gno.land/r/demo/voting/simple_voting.gno +++ b/examples/gno.land/r/demo/voting/simple_voting.gno @@ -134,6 +134,42 @@ func Vote(proposalID, voterID string) error { return nil } +func CloseProposal(proposalID string) error { + // Get the caller's address + caller := std.PrevRealm().Addr() + + // Get the proposal + proposal, exists := proposals.Get(proposalID) + if !exists { + // Proposal does not exist + return errors.New("proposal not found") + } + + prop := proposal.(*Proposal) + + if caller != prop.owner { + return errors.New("unauthorized") + } + + prop.isClosed = true + + return nil +} + +func GetOwner(proposalID string) (string, error) { + // Get the proposal + proposal, exists := proposals.Get(proposalID) + if !exists { + // Proposal does not exist + return "", errors.New("proposal not found") + } + + prop := proposal.(*Proposal) + + // Return the vote count + return prop.owner.String(), nil +} + // GetVotes returns the current vote count for a proposal identified by proposalID func GetVotes(proposalID string) (uint64, error) { // Get the proposal @@ -148,3 +184,8 @@ func GetVotes(proposalID string) (uint64, error) { // Return the vote count return prop.votes, nil } + +func Render(path string) string { + // do something here + return "" +} diff --git a/examples/gno.land/r/demo/voting/simple_voting_test.gno b/examples/gno.land/r/demo/voting/simple_voting_test.gno index 75e177b28ec..fafd8eebe8d 100644 --- a/examples/gno.land/r/demo/voting/simple_voting_test.gno +++ b/examples/gno.land/r/demo/voting/simple_voting_test.gno @@ -15,6 +15,9 @@ func TestCreateProposal(t *testing.T) { numParticipants := uint64(100) maxVotesPerUser := uint64(5) + owner := testutils.TestAddress("owner") + std.TestSetPrevAddr(owner) + proposalID := CreateProposal(title, numParticipants, maxVotesPerUser) if proposalID == "" { @@ -35,9 +38,9 @@ func TestCreateProposal(t *testing.T) { t.Errorf("Expected maxVotesPerUser %d, got %d", maxVotesPerUser, prop.maxVotesPerUser) } - // if prop.voters.Size() != numParticipants { - // t.Errorf("Expected total voters %d, got %d", numParticipants, prop.voters.Size()) - // } + if prop.owner != owner { + t.Errorf("Expected owner %s, got %s", owner.String(), prop.owner.String()) + } if uint64(proposalIndex) != 1 { t.Errorf("Expected proposalIndex %d, got %d", 1, uint64(proposalIndex)) @@ -48,7 +51,7 @@ func TestCreateProposal(t *testing.T) { func TestRegisterVoter(t *testing.T) { Init() - address := testutils.TestAddress("dummy") + address := testutils.TestAddress("owner") std.TestSetPrevAddr(address) voterID := RegisterVoter() @@ -132,3 +135,5 @@ func TestVoteLimit(t *testing.T) { t.Errorf("Expected vote limit error, got nil") } } + +// Todo : add more unit tests From e1cac6b33ec89a590cca95a9e43ce1482d3ccf25 Mon Sep 17 00:00:00 2001 From: linhpn99 Date: Thu, 30 May 2024 15:06:24 +0700 Subject: [PATCH 25/25] use TestSetRealm instead --- .../r/demo/voting/simple_voting_test.gno | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/examples/gno.land/r/demo/voting/simple_voting_test.gno b/examples/gno.land/r/demo/voting/simple_voting_test.gno index fafd8eebe8d..845d4f3a993 100644 --- a/examples/gno.land/r/demo/voting/simple_voting_test.gno +++ b/examples/gno.land/r/demo/voting/simple_voting_test.gno @@ -7,6 +7,11 @@ import ( "gno.land/p/demo/testutils" ) +var ( + addr1 = std.Address("g1damkuetjta047h6lta047h6lta047h6lwwxf76") + addr2 = std.Address("g1r0mlnkc05z0fv49km99z60qnp95tengyqfdr02") +) + // TestCreateProposal tests the creation of a proposal func TestCreateProposal(t *testing.T) { Init() @@ -15,8 +20,7 @@ func TestCreateProposal(t *testing.T) { numParticipants := uint64(100) maxVotesPerUser := uint64(5) - owner := testutils.TestAddress("owner") - std.TestSetPrevAddr(owner) + std.TestSetRealm(std.NewUserRealm(addr1)) proposalID := CreateProposal(title, numParticipants, maxVotesPerUser) @@ -38,8 +42,8 @@ func TestCreateProposal(t *testing.T) { t.Errorf("Expected maxVotesPerUser %d, got %d", maxVotesPerUser, prop.maxVotesPerUser) } - if prop.owner != owner { - t.Errorf("Expected owner %s, got %s", owner.String(), prop.owner.String()) + if prop.owner != addr1 { + t.Errorf("Expected owner %s, got %s", addr1.String(), prop.owner.String()) } if uint64(proposalIndex) != 1 { @@ -51,15 +55,14 @@ func TestCreateProposal(t *testing.T) { func TestRegisterVoter(t *testing.T) { Init() - address := testutils.TestAddress("owner") - std.TestSetPrevAddr(address) + std.TestSetRealm(std.NewUserRealm(addr1)) voterID := RegisterVoter() if voterID == "" { t.Errorf("Expected voterID, got empty string") } - voter, exists := registeredVoters.Get(address.String()) + voter, exists := registeredVoters.Get(addr1.String()) if !exists { t.Errorf("Voter not found") } @@ -77,13 +80,11 @@ func TestVote(t *testing.T) { numParticipants := uint64(100) maxVotesPerUser := uint64(5) - owner := testutils.TestAddress("owner") - std.TestSetPrevAddr(owner) + std.TestSetRealm(std.NewUserRealm(addr1)) proposalID := CreateProposal(title, numParticipants, maxVotesPerUser) - voter := testutils.TestAddress("voter") - std.TestSetPrevAddr(voter) + std.TestSetRealm(std.NewUserRealm(addr2)) voterID := RegisterVoter() @@ -109,14 +110,11 @@ func TestVoteLimit(t *testing.T) { numParticipants := uint64(100) maxVotesPerUser := uint64(2) - owner := testutils.TestAddress("owner") - std.TestSetPrevAddr(owner) + std.TestSetRealm(std.NewUserRealm(addr1)) proposalID := CreateProposal(title, numParticipants, maxVotesPerUser) - voter := testutils.TestAddress("voter") - - std.TestSetPrevAddr(voter) + std.TestSetRealm(std.NewUserRealm(addr2)) voterID := RegisterVoter()