PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/r/g1sw5xklxjjuv0yvuxy5f5s3l3mnj0nqq626a9wr/million/v0

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v0
Namespace
g1sw5xklxjjuv0yvuxy5f5s3l3mnj0nqq626a9wr / million
Files
4 (README)(gnomod.toml)
Exported functions
19
Module
gno.land/r/g1sw5xklxjjuv0yvuxy5f5s3l3mnj0nqq626a9wr/million/v0
gno
0.9

Files (4)

  • README.mdmarkdown
  • gnomod.tomltoml
  • million.gnogno
  • million_test.gnogno
million_test.gnogno
1package million23import (4	"strings"5	"testing"67	"chain"8	"chain/banker"910	"gno.land/p/nt/testutils/v0"11	"gno.land/p/nt/uassert/v0"12)1314var (15	deployer = testutils.TestAddress("deployer")16	alice    = testutils.TestAddress("alice")17	bob      = testutils.TestAddress("bob")18)1920var realmAddr = chain.PackageAddress("gno.land/r/g1sw5xklxjjuv0yvuxy5f5s3l3mnj0nqq626a9wr/million/v0")2122// Tests own the admin role through a test address rather than DefaultAdmin.23func init() {24	owner = deployer25}2627// fund credits the realm with what the next paint call claims to send, as28// the chain would before running the message, and resets the per-message29// origin-send spending limit that the previous call used up.30func fund(n int64) {31	testing.SetOriginSpend(nil)32	if n > 0 {33		testing.IssueCoins(realmAddr, ugnot(n))34	}35}3637func balanceOf(a address) int64 {38	return banker.NewReadonlyBanker().GetCoin(a, Denom)39}4041func ugnot(n int64) chain.Coins {42	return chain.NewCoins(chain.NewCoin(Denom, n))43}4445// testing.SetRealm only affects the frame that calls it, so it cannot be46// wrapped in a helper: every test sets the caller inline.4748func TestInitialState(t *testing.T) {49	uassert.Equal(t, Width, GetWidth())50	uassert.Equal(t, Height, GetHeight())51	uassert.Equal(t, DefaultPrice, Price())52	uassert.Equal(t, MaxBatch, MaxBatchSize())53	uassert.Equal(t, 0, Pixel(0, 0))54	uassert.Equal(t, deployer, Owner())55	uassert.Equal(t, 16, len(strings.Split(Palette(), ",")))56}5758func TestPaintOnePixel(cur realm, t *testing.T) {59	adminBefore := balanceOf(owner)6061	testing.SetRealm(testing.NewUserRealm(alice))62	testing.SetOriginSend(ugnot(price))63	fund(price)64	Paint(cross(cur), 10, 20, 6)6566	uassert.Equal(t, 6, Pixel(10, 20))67	uassert.Equal(t, 1, Painted())68	uassert.Equal(t, price, Revenue())69	uassert.Equal(t, int64(1), Paints())70	// The payment went straight to the admin, nothing stayed in the realm.71	uassert.Equal(t, adminBefore+price, balanceOf(owner))72	uassert.Equal(t, int64(0), balanceOf(realmAddr))7374	// Anyone can repaint for the same price; the count does not grow.75	testing.SetRealm(testing.NewUserRealm(bob))76	testing.SetOriginSend(ugnot(price))77	fund(price)78	Paint(cross(cur), 10, 20, 15)79	uassert.Equal(t, 15, Pixel(10, 20))80	uassert.Equal(t, 1, Painted())81	uassert.Equal(t, 2*price, Revenue())82	uassert.Equal(t, adminBefore+2*price, balanceOf(owner))83}8485func TestAdminPaintsForFree(cur realm, t *testing.T) {86	adminBefore := balanceOf(owner)87	revenueBefore := Revenue()8889	testing.SetRealm(testing.NewUserRealm(owner))90	testing.SetOriginSend(nil)91	Paint(cross(cur), 400, 400, 8)92	uassert.Equal(t, 8, Pixel(400, 400))9394	testing.SetRealm(testing.NewUserRealm(owner))95	testing.SetOriginSend(nil)96	PaintBatch(cross(cur), "401,400,9;402,400,10")97	uassert.Equal(t, 10, Pixel(402, 400))9899	// Free means exactly free: the admin must not attach coins either.100	testing.SetRealm(testing.NewUserRealm(owner))101	testing.SetOriginSend(ugnot(price))102	uassert.AbortsContains(t, cur, "must send exactly 0ugnot", func() { Paint(cross(cur), 403, 400, 1) })103104	uassert.Equal(t, revenueBefore, Revenue())105	uassert.Equal(t, adminBefore, balanceOf(owner))106}107108func TestPaintRejectsWrongPayment(cur realm, t *testing.T) {109	before := Painted()110111	testing.SetRealm(testing.NewUserRealm(alice))112	testing.SetOriginSend(ugnot(price-1))113	uassert.AbortsContains(t, cur, "must send exactly", func() { Paint(cross(cur), 0, 0, 1) })114115	testing.SetRealm(testing.NewUserRealm(alice))116	testing.SetOriginSend(ugnot(price+1))117	uassert.AbortsContains(t, cur, "must send exactly", func() { Paint(cross(cur), 0, 0, 1) })118119	testing.SetRealm(testing.NewUserRealm(alice))120	testing.SetOriginSend(nil)121	uassert.AbortsContains(t, cur, "must send exactly", func() { Paint(cross(cur), 0, 0, 1) })122123	testing.SetRealm(testing.NewUserRealm(alice))124	testing.SetOriginSend(chain.NewCoins(chain.NewCoin("uatom", price)))125	uassert.AbortsContains(t, cur, "only ugnot is accepted", func() { Paint(cross(cur), 0, 0, 1) })126127	uassert.Equal(t, before, Painted())128	uassert.Equal(t, 0, Pixel(0, 0))129}130131func TestPaintRejectsInvalidPixels(cur realm, t *testing.T) {132	testing.SetRealm(testing.NewUserRealm(alice))133	testing.SetOriginSend(ugnot(price))134	uassert.AbortsContains(t, cur, "out of range", func() { Paint(cross(cur), Width, 0, 1) })135	uassert.AbortsContains(t, cur, "out of range", func() { Paint(cross(cur), 0, Height, 1) })136	uassert.AbortsContains(t, cur, "out of range", func() { Paint(cross(cur), -1, 0, 1) })137	uassert.AbortsContains(t, cur, "color must be", func() { Paint(cross(cur), 0, 0, 0) })138	uassert.AbortsContains(t, cur, "color must be", func() { Paint(cross(cur), 0, 0, 16) })139	uassert.AbortsContains(t, cur, "color must be", func() { Paint(cross(cur), 0, 0, -1) })140}141142func TestPaintRejectsCodeRealm(cur realm, t *testing.T) {143	// Regression for the OriginSend bypass: an intermediate realm forwards144	// the call while the envelope claims the coins were sent.145	testing.SetRealm(testing.NewCodeRealm("gno.land/r/attacker/wrapper"))146	testing.SetOriginSend(ugnot(price))147	uassert.AbortsContains(t, cur, "directly by a user", func() { Paint(cross(cur), 1, 1, 1) })148	uassert.Equal(t, 0, Pixel(1, 1))149}150151func TestPaintBatch(cur realm, t *testing.T) {152	before := Painted()153	testing.SetRealm(testing.NewUserRealm(alice))154	testing.SetOriginSend(ugnot(3 * price))155	fund(3 * price)156	PaintBatch(cross(cur), "100,100,1;101,100,2;102,100,3;")157158	uassert.Equal(t, 1, Pixel(100, 100))159	uassert.Equal(t, 2, Pixel(101, 100))160	uassert.Equal(t, 3, Pixel(102, 100))161	uassert.Equal(t, before+3, Painted())162163	row := Rows(100, 101)164	uassert.Equal(t, Width, len(row))165	uassert.Equal(t, "123", row[100:103])166}167168func TestPaintBatchIsAtomic(cur realm, t *testing.T) {169	// One bad pixel rejects the whole batch before anything is written.170	testing.SetRealm(testing.NewUserRealm(alice))171	testing.SetOriginSend(ugnot(2*price))172	uassert.AbortsContains(t, cur, "out of range", func() { PaintBatch(cross(cur), "200,200,1;9999,0,1") })173	uassert.Equal(t, 0, Pixel(200, 200))174175	testing.SetRealm(testing.NewUserRealm(alice))176	testing.SetOriginSend(ugnot(price))177	uassert.AbortsContains(t, cur, "must send exactly", func() { PaintBatch(cross(cur), "200,200,1;201,200,1") })178	uassert.Equal(t, 0, Pixel(200, 200))179180	testing.SetRealm(testing.NewUserRealm(alice))181	testing.SetOriginSend(ugnot(price))182	uassert.AbortsContains(t, cur, "x,y,color", func() { PaintBatch(cross(cur), "garbage") })183	uassert.AbortsContains(t, cur, "x,y,color", func() { PaintBatch(cross(cur), "") })184}185186func TestPaintBatchTooLarge(cur realm, t *testing.T) {187	var sb strings.Builder188	for i := 0; i <= MaxBatch; i++ {189		if i > 0 {190			sb.WriteByte(';')191		}192		sb.WriteString("0,")193		sb.WriteString(itoa(i))194		sb.WriteString(",1")195	}196	testing.SetRealm(testing.NewUserRealm(alice))197	testing.SetOriginSend(ugnot(int64(MaxBatch+1)*price))198	uassert.AbortsContains(t, cur, "too many pixels", func() { PaintBatch(cross(cur), sb.String()) })199}200201func TestSetPrice(cur realm, t *testing.T) {202	testing.SetRealm(testing.NewUserRealm(alice))203	testing.SetOriginSend(nil)204	uassert.AbortsWithMessage(t, cur, "owner only", func() { SetPrice(cross(cur), 1) })205206	testing.SetRealm(testing.NewUserRealm(owner))207	testing.SetOriginSend(nil)208	uassert.AbortsWithMessage(t, cur, "price must not be negative", func() { SetPrice(cross(cur), -1) })209	SetPrice(cross(cur), 0)210	uassert.Equal(t, int64(0), Price())211212	// Free painting requires sending nothing.213	testing.SetRealm(testing.NewUserRealm(alice))214	testing.SetOriginSend(nil)215	Paint(cross(cur), 300, 300, 4)216	uassert.Equal(t, 4, Pixel(300, 300))217	testing.SetRealm(testing.NewUserRealm(alice))218	testing.SetOriginSend(ugnot(1))219	uassert.AbortsContains(t, cur, "must send exactly", func() { Paint(cross(cur), 300, 301, 4) })220221	testing.SetRealm(testing.NewUserRealm(owner))222	testing.SetOriginSend(nil)223	SetPrice(cross(cur), DefaultPrice)224}225226func TestWithdraw(cur realm, t *testing.T) {227	testing.IssueCoins(realmAddr, ugnot(1_000))228	ro := banker.NewReadonlyBanker()229	bobBefore := ro.GetCoin(bob, Denom)230231	testing.SetRealm(testing.NewUserRealm(alice))232	testing.SetOriginSend(nil)233	uassert.AbortsWithMessage(t, cur, "owner only", func() { Withdraw(cross(cur), alice, 1) })234235	testing.SetRealm(testing.NewUserRealm(owner))236	testing.SetOriginSend(nil)237	uassert.AbortsWithMessage(t, cur, "amount must be positive", func() { Withdraw(cross(cur), bob, 0) })238	uassert.AbortsWithMessage(t, cur, "invalid address", func() { Withdraw(cross(cur), address("nope"), 1) })239	Withdraw(cross(cur), bob, 600)240241	uassert.Equal(t, bobBefore+600, ro.GetCoin(bob, Denom))242	uassert.Equal(t, int64(400), ro.GetCoin(realmAddr, Denom))243	// Leave the realm empty for the tests that assert on it.244	Withdraw(cross(cur), bob, 400)245}246247func TestTransferOwnership(cur realm, t *testing.T) {248	original := owner249250	testing.SetRealm(testing.NewUserRealm(alice))251	testing.SetOriginSend(nil)252	uassert.AbortsWithMessage(t, cur, "owner only", func() { TransferOwnership(cross(cur), alice) })253254	testing.SetRealm(testing.NewUserRealm(original))255	testing.SetOriginSend(nil)256	uassert.AbortsWithMessage(t, cur, "invalid address", func() { TransferOwnership(cross(cur), address("nope")) })257	TransferOwnership(cross(cur), alice)258	uassert.Equal(t, alice, Owner())259260	testing.SetRealm(testing.NewUserRealm(alice))261	testing.SetOriginSend(nil)262	TransferOwnership(cross(cur), original)263	uassert.Equal(t, original, Owner())264}265266func TestVersionAndDirtyRows(cur realm, t *testing.T) {267	v := Version()268	uassert.Equal(t, "", DirtyRows(v))269270	testing.SetRealm(testing.NewUserRealm(alice))271	testing.SetOriginSend(ugnot(3 * price))272	fund(3 * price)273	PaintBatch(cross(cur), "0,700,1;1,700,2;0,701,3")274275	uassert.Equal(t, v+1, Version())276	uassert.Equal(t, "700,701", DirtyRows(v))277	uassert.Equal(t, "", DirtyRows(Version()))278279	// A second call on one of those rows moves only that row forward.280	testing.SetRealm(testing.NewUserRealm(bob))281	testing.SetOriginSend(ugnot(price))282	fund(price)283	Paint(cross(cur), 5, 701, 4)284	uassert.Equal(t, v+2, Version())285	uassert.Equal(t, "701", DirtyRows(v+1))286	uassert.Equal(t, "700,701", DirtyRows(v))287288	// A rejected paint changes nothing.289	testing.SetRealm(testing.NewUserRealm(bob))290	testing.SetOriginSend(ugnot(1))291	uassert.AbortsContains(t, cur, "must send exactly", func() { Paint(cross(cur), 5, 702, 4) })292	uassert.Equal(t, v+2, Version())293	uassert.Equal(t, "", DirtyRows(v+2))294295	// Negative "since" behaves like zero: painted rows only, never blank ones.296	uassert.Equal(t, DirtyRows(0), DirtyRows(-1))297	uassert.True(t, !strings.Contains(","+DirtyRows(0)+",", ",799,"), "row 799 was never painted")298}299300func TestRender(t *testing.T) {301	out := Render("")302	uassert.True(t, strings.Contains(out, "# Million Gno"), "title")303	uassert.True(t, strings.Contains(out, "| Size | 1250 x 800 |"), "size row")304	uassert.True(t, strings.Contains(out, "| Price per pixel | 50000ugnot |"), "price row")305	uassert.True(t, strings.Contains(out, "| Admin | "+owner.String()+" |"), "admin row")306	uassert.True(t, strings.Contains(out, "$help&func=PaintBatch"), "help link")307}308309func itoa(n int) string {310	if n == 0 {311		return "0"312	}313	var b []byte314	for n > 0 {315		b = append([]byte{byte('0' + n%10)}, b...)316		n /= 10317	}318	return string(b)319}320

Functions

  • DirtyRows(since int64) string

  • GetHeight() int

  • GetWidth() int

  • MaxBatchSize() int

  • Owner() string

  • Paint(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, x int, y int, color int)

  • PaintBatch(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, pixels string)

  • Painted() int

  • Paints() int64

  • Palette() string

  • Pixel(x int, y int) int

  • Price() int64

  • Render(string) string

  • Revenue() int64

  • Rows(from int, to int) string

  • SetPrice(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, ugnot int64)

  • TransferOwnership(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, newOwner string)

  • Version() int64

  • Withdraw(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, to string, amount int64)

Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

Rendered

RenderedRawgnoweb ↗

vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.