diff --git a/examples/gno.land/r/gnoland/faucet/admin.gno b/examples/gno.land/r/gnoland/faucet/admin.gno index 03798ba92e7..000adb8f310 100644 --- a/examples/gno.land/r/gnoland/faucet/admin.gno +++ b/examples/gno.land/r/gnoland/faucet/admin.gno @@ -5,44 +5,77 @@ import ( "std" ) -func AdminSetInPause(inPause bool) error { +func AdminSetInPause(inPause bool) string { if err := assertIsAdmin(); err != nil { - return err + return err.Error() } gInPause = inPause - return nil + return "" } -func AdminSetMessage(message string) error { +func AdminSetMessage(message string) string { if err := assertIsAdmin(); err != nil { - return err + return err.Error() } gMessage = message - return nil + return "" } -func AdminSetPerTransferSend(send std.Coins) error { +func AdminSetTransferLimit(amount int64) string { if err := assertIsAdmin(); err != nil { - return err + return err.Error() } - gPerTransferSend = send - return nil + gLimit = std.Coin{Denom: "ugnot", Amount: amount} + return "" } -func AdminSetAdminAddr(addr std.Address) error { +func AdminSetAdminAddr(addr std.Address) string { if err := assertIsAdmin(); err != nil { - return err + return err.Error() } gAdminAddr = addr - return nil + return "" } -func AdminSetControllerAddr(addr std.Address) error { +func AdminAddController(addr std.Address) string { if err := assertIsAdmin(); err != nil { - return err + return err.Error() } - gControllerAddr = addr - return nil + + size := gControllers.Size() + + if size >= gControllersSize { + return "can not add more controllers than allowed" + } + + if gControllers.Has(addr.String()) { + return addr.String() + " exists, no need to add." + } + + gControllers.Set(addr.String(), addr) + + return "" +} + +func AdminRemoveController(addr std.Address) string { + if err := assertIsAdmin(); err != nil { + return err.Error() + } + + if !gControllers.Has(addr.String()) { + return addr.String() + " is not on the controller list" + } + + _, ok := gControllers.Remove(addr.String()) + + // it not should happen. + // we will check anyway to prevent issues in the underline implementation. + + if !ok { + return addr.String() + " is not on the controller list" + } + + return "" } func assertIsAdmin() error { diff --git a/examples/gno.land/r/gnoland/faucet/faucet.gno b/examples/gno.land/r/gnoland/faucet/faucet.gno index 0d6c8ee37f5..c68f6d203f0 100644 --- a/examples/gno.land/r/gnoland/faucet/faucet.gno +++ b/examples/gno.land/r/gnoland/faucet/faucet.gno @@ -4,65 +4,88 @@ import ( "errors" "std" + "gno.land/p/demo/avl" "gno.land/p/demo/ufmt" ) var ( // configurable by admin. - gAdminAddr std.Address = "g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq" - gControllerAddr std.Address = "g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq" - gPerTransferSend = std.Coins{std.Coin{"ugnot", 1000000}} + gAdminAddr std.Address = std.Address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") + gControllers = avl.NewTree() + gControllersSize = 10 // limit it to 10 gInPause = false gMessage = "# Community Faucet.\n\n" // internal vars, for stats. gTotalTransferred std.Coins gTotalTransfers = uint(0) + + // per request limit, 350 gnot + gLimit std.Coin = std.Coin{"ugnot", 350000000} ) -func Transfer(to std.Address) error { +func Transfer(to std.Address, send int64) string { if err := assertIsController(); err != nil { - return err + return err.Error() } if gInPause { - return errors.New("faucet in pause") + return errors.New("faucet in pause").Error() } - send := gPerTransferSend + // limit the per request + if send > gLimit.Amount { + return errors.New("Per request limit " + gLimit.String() + " exceed").Error() + } + sendCoins := std.Coins{std.Coin{Denom: "ugnot", Amount: send}} - gTotalTransferred = gTotalTransferred.Add(send) + gTotalTransferred = gTotalTransferred.Add(sendCoins) gTotalTransfers++ - banker := std.GetBanker(std.BankerTypeOrigSend) + banker := std.GetBanker(std.BankerTypeRealmSend) pkgaddr := std.GetOrigPkgAddr() - banker.SendCoins(pkgaddr, to, send) - return nil + banker.SendCoins(pkgaddr, to, sendCoins) + return "" +} + +func GetPerTransferLimit() int64 { + return gLimit.Amount } func Render(path string) string { - banker := std.GetBanker(std.BankerTypeOrigSend) + banker := std.GetBanker(std.BankerTypeRealmSend) balance := banker.GetCoins(std.GetOrigPkgAddr()) - output := gMessage + output := path + gMessage if gInPause { output += "Status: inactive.\n" } else { output += "Status: active.\n" } output += ufmt.Sprintf("Balance: %s.\n", balance.String()) - output += ufmt.Sprintf("Rewarded: %s (in %d times).\n", gTotalTransferred.String(), gTotalTransfers) + output += ufmt.Sprintf("Total transfers: %s (in %d times).\n\n", gTotalTransferred.String(), gTotalTransfers) - if path == "?debug" { - output += ufmt.Sprintf("Admin: %s, Controller: %s\n", gAdminAddr.String(), gControllerAddr.String()) + output += "Package address: " + std.GetOrigPkgAddr().String() + "\n\n" + output += ufmt.Sprintf("Admin: %s\n\n ", gAdminAddr.String()) + output += ufmt.Sprintf("Controllers:\n\n ") + + for i := 0; i < gControllersSize; i++ { + _, v := gControllers.GetByIndex(i) + output += ufmt.Sprintf("%s ", v.(std.Address)) } + + output += "\n\n" + output += ufmt.Sprintf("Per request limit: %s\n\n", gLimit.String()) + return output } func assertIsController() error { caller := std.GetOrigCaller() - if caller != gControllerAddr { - return errors.New("restricted for controller") + + ok := gControllers.Has(caller.String()) + if !ok { + return errors.New(caller.String() + " is not on the controller list") } return nil } diff --git a/examples/gno.land/r/gnoland/faucet/faucet_filetest.gno b/examples/gno.land/r/gnoland/faucet/faucet_filetest.gno deleted file mode 100644 index d3e4b5a0ca9..00000000000 --- a/examples/gno.land/r/gnoland/faucet/faucet_filetest.gno +++ /dev/null @@ -1,75 +0,0 @@ -package main - -import ( - "fmt" - "std" - - "gno.land/p/demo/testutils" - "gno.land/r/gnoland/faucet" -) - -func main() { - var ( - adminaddr = std.Address("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq") - controlleraddr = testutils.TestAddress("controller") - mainaddr = std.TestDerivePkgAddr("main") - faucetaddr = std.TestDerivePkgAddr("gno.land/r/faucet") - test1addr = testutils.TestAddress("test1") - test2addr = testutils.TestAddress("test2") - test3addr = testutils.TestAddress("test3") - ) - - banker := std.GetBanker(std.BankerTypeReadonly) - std.TestIssueCoins(faucetaddr, std.Coins{{"ugnot", 1000000000}}) - std.TestSetOrigPkgAddr(faucetaddr) - - showBalances := func() string { - var ( - mainbal = banker.GetCoins(mainaddr) - faucetbal = banker.GetCoins(faucetaddr) - test1bal = banker.GetCoins(test1addr) - test2bal = banker.GetCoins(test2addr) - test3bal = banker.GetCoins(test3addr) - controllerbal = banker.GetCoins(controlleraddr) - adminbal = banker.GetCoins(adminaddr) - ) - return fmt.Sprintf("main=%q, faucet=%q, test1=%q, test2=%q test3=%q controller=%q admin=%q", - mainbal.String(), faucetbal.String(), test1bal.String(), test2bal.String(), - test3bal.String(), controllerbal.String(), adminbal.String()) - } - - println("before:", showBalances()) - - // simulate a Deposit call. - std.TestSetOrigSend(std.Coins{{"ugnot", 2000000000}}, nil) - std.TestSetOrigCaller(adminaddr) - faucet.AdminSetControllerAddr(controlleraddr) - std.TestSetOrigCaller(controlleraddr) - faucet.Transfer(test1addr) - faucet.Transfer(test2addr) - faucet.Transfer(test1addr) - - println("after: ", showBalances()) - - // simulate a Render(). - ret := faucet.Render("") - println("---") - println("Render: ", ret) - println("---") - println("done") -} - -// Output: -// before: main="200000000ugnot", faucet="1000000000ugnot", test1="", test2="" test3="" controller="" admin="" -// after: main="200000000ugnot", faucet="997000000ugnot", test1="2000000ugnot", test2="1000000ugnot" test3="" controller="" admin="" -// --- -// Render: # Community Faucet. -// -// Status: active. -// Balance: 997000000ugnot. -// Rewarded: 3000000ugnot (in 3 times). -// -// --- -// done - -// Realm: diff --git a/examples/gno.land/r/gnoland/faucet/faucet_test.gno b/examples/gno.land/r/gnoland/faucet/faucet_test.gno index 54214dd0ede..822dc169045 100644 --- a/examples/gno.land/r/gnoland/faucet/faucet_test.gno +++ b/examples/gno.land/r/gnoland/faucet/faucet_test.gno @@ -11,49 +11,103 @@ import ( func TestPackage(t *testing.T) { var ( - adminaddr = std.Address("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq") - controlleraddr = testutils.TestAddress("controller") - test1addr = testutils.TestAddress("test1") + adminaddr = std.Address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") + faucetaddr = std.TestDerivePkgAddr("gno.land/r/faucet") + controlleraddr1 = testutils.TestAddress("controller1") + controlleraddr2 = testutils.TestAddress("controller2") + controlleraddr3 = testutils.TestAddress("controller3") + controlleraddr4 = testutils.TestAddress("controller4") + controlleraddr5 = testutils.TestAddress("controller5") + controlleraddr6 = testutils.TestAddress("controller6") + controlleraddr7 = testutils.TestAddress("controller7") + controlleraddr8 = testutils.TestAddress("controller8") + controlleraddr9 = testutils.TestAddress("controller9") + controlleraddr10 = testutils.TestAddress("controller10") + controlleraddr11 = testutils.TestAddress("controller11") + + test1addr = testutils.TestAddress("test1") ) + // deposit 1000gnot to faucet contract + + std.TestIssueCoins(faucetaddr, std.Coins{{"ugnot", 1000000000}}) + std.TestSetOrigPkgAddr(faucetaddr) + assertBalance(t, faucetaddr, 1000000000) // by default, balance is empty, and as a user I cannot call Transfer, or Admin commands. - std.TestSetOrigSend(std.Coins{{"ugnot", 2000000000}}, nil) + assertBalance(t, test1addr, 0) - assertErr(t, faucet.Transfer(test1addr)) - assertErr(t, faucet.AdminSetControllerAddr(controlleraddr)) - std.TestSetOrigCaller(controlleraddr) - assertErr(t, faucet.Transfer(test1addr)) + std.TestSetOrigCaller(test1addr) + assertErr(t, faucet.Transfer(test1addr, 1000000)) + + assertErr(t, faucet.AdminAddController(controlleraddr1)) + std.TestSetOrigCaller(controlleraddr1) + assertErr(t, faucet.Transfer(test1addr, 1000000)) - // as an admin, set the controller to controlleraddr. + // as an admin, add the controller to contract and deposit more 2000gnot to contract std.TestSetOrigCaller(adminaddr) - assertNoErr(t, faucet.AdminSetControllerAddr(controlleraddr)) + assertNoErr(t, faucet.AdminAddController(controlleraddr1)) + assertBalance(t, faucetaddr, 1000000000) // now, send some tokens as controller. - std.TestSetOrigCaller(controlleraddr) - assertNoErr(t, faucet.Transfer(test1addr)) + std.TestSetOrigCaller(controlleraddr1) + assertNoErr(t, faucet.Transfer(test1addr, 1000000)) assertBalance(t, test1addr, 1000000) - assertNoErr(t, faucet.Transfer(test1addr)) + assertNoErr(t, faucet.Transfer(test1addr, 1000000)) assertBalance(t, test1addr, 2000000) + assertBalance(t, faucetaddr, 998000000) + + // remove controller + // as an admin, remove controller + std.TestSetOrigCaller(adminaddr) + assertNoErr(t, faucet.AdminRemoveController(controlleraddr1)) + std.TestSetOrigCaller(controlleraddr1) + assertErr(t, faucet.Transfer(test1addr, 1000000)) + + // duplicate controller + std.TestSetOrigCaller(adminaddr) + assertNoErr(t, faucet.AdminAddController(controlleraddr1)) + assertErr(t, faucet.AdminAddController(controlleraddr1)) + // add more than more than allowed controllers + assertNoErr(t, faucet.AdminAddController(controlleraddr2)) + assertNoErr(t, faucet.AdminAddController(controlleraddr3)) + assertNoErr(t, faucet.AdminAddController(controlleraddr4)) + assertNoErr(t, faucet.AdminAddController(controlleraddr5)) + assertNoErr(t, faucet.AdminAddController(controlleraddr6)) + assertNoErr(t, faucet.AdminAddController(controlleraddr7)) + assertNoErr(t, faucet.AdminAddController(controlleraddr8)) + assertNoErr(t, faucet.AdminAddController(controlleraddr9)) + assertNoErr(t, faucet.AdminAddController(controlleraddr10)) + assertErr(t, faucet.AdminAddController(controlleraddr11)) + + // send more than per transfer limit + std.TestSetOrigCaller(adminaddr) + faucet.AdminSetTransferLimit(300000000) + std.TestSetOrigCaller(controlleraddr1) + assertErr(t, faucet.Transfer(test1addr, 301000000)) + + // block transefer from the address not on the controllers list. + std.TestSetOrigCaller(controlleraddr11) + assertErr(t, faucet.Transfer(test1addr, 1000000)) } -func assertErr(t *testing.T, err error) { +func assertErr(t *testing.T, err string) { t.Helper() - if err == nil { + + if err == "" { t.Logf("info: got err: %v", err) t.Errorf("expected an error, got nil.") } } -func assertNoErr(t *testing.T, err error) { +func assertNoErr(t *testing.T, err string) { t.Helper() - if err != nil { + if err != "" { t.Errorf("got err: %v.", err) } } func assertBalance(t *testing.T, addr std.Address, expectedBal int64) { t.Helper() - banker := std.GetBanker(std.BankerTypeReadonly) coins := banker.GetCoins(addr) got := coins.AmountOf("ugnot")