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/g19t6f4f4ptt3m949jznalfsx2h8aj696llul0nj/million/v0

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v0
Namespace
g19t6f4f4ptt3m949jznalfsx2h8aj696llul0nj / million
Files
4 (README)(gnomod.toml)
Exported functions
20
Module
gno.land/r/g19t6f4f4ptt3m949jznalfsx2h8aj696llul0nj/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/g19t6f4f4ptt3m949jznalfsx2h8aj696llul0nj/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, DefaultMaxBatch, 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 TestSetMaxBatch(cur realm, t *testing.T) {227	orig := MaxBatchSize()228229	testing.SetRealm(testing.NewUserRealm(alice))230	uassert.AbortsWithMessage(t, cur, "owner only", func() { SetMaxBatch(cross(cur), 100) })231232	testing.SetRealm(testing.NewUserRealm(owner))233	uassert.AbortsContains(t, cur, "max batch must be between", func() { SetMaxBatch(cross(cur), 0) })234	uassert.AbortsContains(t, cur, "max batch must be between", func() { SetMaxBatch(cross(cur), HardMaxBatch+1) })235236	SetMaxBatch(cross(cur), 3)237	uassert.Equal(t, 3, MaxBatchSize())238239	// The new limit is enforced by PaintBatch.240	testing.SetRealm(testing.NewUserRealm(alice))241	testing.SetOriginSend(ugnot(4 * price))242	fund(4 * price)243	uassert.AbortsContains(t, cur, "too many pixels", func() {244		PaintBatch(cross(cur), "0,500,1;1,500,1;2,500,1;3,500,1")245	})246247	testing.SetRealm(testing.NewUserRealm(owner))248	SetMaxBatch(cross(cur), orig)249	uassert.Equal(t, orig, MaxBatchSize())250}251252func TestWithdraw(cur realm, t *testing.T) {253	testing.IssueCoins(realmAddr, ugnot(1_000))254	ro := banker.NewReadonlyBanker()255	bobBefore := ro.GetCoin(bob, Denom)256257	testing.SetRealm(testing.NewUserRealm(alice))258	testing.SetOriginSend(nil)259	uassert.AbortsWithMessage(t, cur, "owner only", func() { Withdraw(cross(cur), alice, 1) })260261	testing.SetRealm(testing.NewUserRealm(owner))262	testing.SetOriginSend(nil)263	uassert.AbortsWithMessage(t, cur, "amount must be positive", func() { Withdraw(cross(cur), bob, 0) })264	uassert.AbortsWithMessage(t, cur, "invalid address", func() { Withdraw(cross(cur), address("nope"), 1) })265	Withdraw(cross(cur), bob, 600)266267	uassert.Equal(t, bobBefore+600, ro.GetCoin(bob, Denom))268	uassert.Equal(t, int64(400), ro.GetCoin(realmAddr, Denom))269	// Leave the realm empty for the tests that assert on it.270	Withdraw(cross(cur), bob, 400)271}272273func TestTransferOwnership(cur realm, t *testing.T) {274	original := owner275276	testing.SetRealm(testing.NewUserRealm(alice))277	testing.SetOriginSend(nil)278	uassert.AbortsWithMessage(t, cur, "owner only", func() { TransferOwnership(cross(cur), alice) })279280	testing.SetRealm(testing.NewUserRealm(original))281	testing.SetOriginSend(nil)282	uassert.AbortsWithMessage(t, cur, "invalid address", func() { TransferOwnership(cross(cur), address("nope")) })283	TransferOwnership(cross(cur), alice)284	uassert.Equal(t, alice, Owner())285286	testing.SetRealm(testing.NewUserRealm(alice))287	testing.SetOriginSend(nil)288	TransferOwnership(cross(cur), original)289	uassert.Equal(t, original, Owner())290}291292func TestVersionAndDirtyRows(cur realm, t *testing.T) {293	v := Version()294	uassert.Equal(t, "", DirtyRows(v))295296	testing.SetRealm(testing.NewUserRealm(alice))297	testing.SetOriginSend(ugnot(3 * price))298	fund(3 * price)299	PaintBatch(cross(cur), "0,700,1;1,700,2;0,701,3")300301	uassert.Equal(t, v+1, Version())302	uassert.Equal(t, "700,701", DirtyRows(v))303	uassert.Equal(t, "", DirtyRows(Version()))304305	// A second call on one of those rows moves only that row forward.306	testing.SetRealm(testing.NewUserRealm(bob))307	testing.SetOriginSend(ugnot(price))308	fund(price)309	Paint(cross(cur), 5, 701, 4)310	uassert.Equal(t, v+2, Version())311	uassert.Equal(t, "701", DirtyRows(v+1))312	uassert.Equal(t, "700,701", DirtyRows(v))313314	// A rejected paint changes nothing.315	testing.SetRealm(testing.NewUserRealm(bob))316	testing.SetOriginSend(ugnot(1))317	uassert.AbortsContains(t, cur, "must send exactly", func() { Paint(cross(cur), 5, 702, 4) })318	uassert.Equal(t, v+2, Version())319	uassert.Equal(t, "", DirtyRows(v+2))320321	// Negative "since" behaves like zero: painted rows only, never blank ones.322	uassert.Equal(t, DirtyRows(0), DirtyRows(-1))323	uassert.True(t, !strings.Contains(","+DirtyRows(0)+",", ",799,"), "row 799 was never painted")324}325326func TestRender(t *testing.T) {327	out := Render("")328	uassert.True(t, strings.Contains(out, "# Million Gno"), "title")329	uassert.True(t, strings.Contains(out, "| Size | 1250 x 800 |"), "size row")330	uassert.True(t, strings.Contains(out, "| Price per pixel | 50000ugnot |"), "price row")331	uassert.True(t, strings.Contains(out, "| Admin | "+owner.String()+" |"), "admin row")332	uassert.True(t, strings.Contains(out, "$help&func=PaintBatch"), "help link")333}334335func itoa(n int) string {336	if n == 0 {337		return "0"338	}339	var b []byte340	for n > 0 {341		b = append([]byte{byte('0' + n%10)}, b...)342		n /= 10343	}344	return string(b)345}346

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

  • SetMaxBatch(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}, n int)

  • 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.