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/moul/x/compact/v0

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v0
Namespace
moul / x / compact
Files
5 (README)(gnomod.toml)
Exported functions
7
Module
gno.land/r/moul/x/compact/v0
gno
0.9

Files (5)

  • README.mdmarkdown
  • gnomod.tomltoml
  • compact.gnogno
  • compact_test.gnogno
  • example_test.gnogno
compact_test.gnogno
1package compact23import (4	"strings"5	"testing"67	"gno.land/p/moul/kit/ui/v0"8	"gno.land/p/moul/x/storagecost/v1"9	"gno.land/p/nt/testutils/v0"10	"gno.land/p/nt/uassert/v0"11)1213var (14	alice = testutils.TestAddress("alice")15	bob   = testutils.TestAddress("bob")16	// janitor never adds anything, it only compacts. The whole point of the17	// realm is that this is the address the chain pays.18	janitor = testutils.TestAddress("janitor")19)2021// drain empties the board and asserts it, so a test starts from a known22// state whatever ran before it. Indices are append-addressed for the life of23// the realm, so every test asserts on counts and never on absolute indices.24func drain(cur realm, t *testing.T) {25	t.Helper()26	for i := 0; i < entries.TotalSize(); i++ {27		if e, ok := entryAt(i); ok {28			testing.SetRealm(testing.NewUserRealm(e.Author))29			Drop(cross(cur), i)30		}31	}32	testing.SetRealm(testing.NewUserRealm(janitor))33	Compact(cross(cur))34	live, _ := Fragmentation()35	uassert.Equal(t, 0, live)36}3738func TestDropFreesNothingUntilCompact(cur realm, t *testing.T) {39	drain(cur, t)40	testing.SetRealm(testing.NewUserRealm(alice))41	for i := 0; i < 8; i++ {42		Add(cross(cur), "entry")43	}44	live, allocated := Fragmentation()45	uassert.Equal(t, 8, live)46	uassert.Equal(t, 0, Reclaimable())4748	// Dropping everything moves the live count but frees no nodes by itself.49	base := allocated - 850	for i := base; i < allocated; i++ {51		Drop(cross(cur), i)52	}53	live, allocated2 := Fragmentation()54	uassert.Equal(t, 0, live)55	uassert.Equal(t, allocated, allocated2, "a soft delete must not change the index space")56	uassert.True(t, Reclaimable() > 0, "a fully dead tail should be reclaimable")5758	// And compaction is what actually frees them.59	testing.SetRealm(testing.NewUserRealm(janitor))60	freed := Compact(cross(cur))61	uassert.True(t, freed > 0)62	uassert.Equal(t, 0, Reclaimable())63	// Compacting twice is not an error, it just frees nothing, so two bots64	// racing for the same nodes both survive rather than one aborting.65	uassert.Equal(t, 0, Compact(cross(cur)))66}6768// TestScatteredHolesReclaimLessThanTheGap is the claim the Render makes, and69// the reason the realm publishes two numbers instead of one. No other test70// could catch it: they all drop contiguous runs.71func TestScatteredHolesReclaimLessThanTheGap(cur realm, t *testing.T) {72	drain(cur, t)73	testing.SetRealm(testing.NewUserRealm(alice))74	base := entries.TotalSize()75	for i := 0; i < 16; i++ {76		Add(cross(cur), "entry")77	}78	// Every other index, so no whole subtree dies.79	for i := base; i < base+16; i += 2 {80		Drop(cross(cur), i)81	}82	live, allocated := Fragmentation()83	gap := allocated - live84	uassert.True(t, gap >= 8, "eight holes were made")85	uassert.True(t, Reclaimable() < gap,86		"scattered holes must reclaim fewer nodes than the gap suggests")8788	drain(cur, t)89}9091func TestOnlyTheAuthorMayDrop(cur realm, t *testing.T) {92	drain(cur, t)93	testing.SetRealm(testing.NewUserRealm(alice))94	i := Add(cross(cur), "alice's entry")9596	testing.SetRealm(testing.NewUserRealm(bob))97	uassert.AbortsWithMessage(t, cur, "compact: only the author may drop an entry", func() {98		Drop(cross(cur), i)99	})100	// But compaction stays permissionless, which is the asymmetry that makes101	// the mechanism work: choosing what dies is owned, reclaiming is not.102	testing.SetRealm(testing.NewUserRealm(janitor))103	uassert.NotAborts(t, cur, func() { Compact(cross(cur)) })104105	drain(cur, t)106}107108func TestAddRejectsBadInput(cur realm, t *testing.T) {109	drain(cur, t)110	testing.SetRealm(testing.NewUserRealm(alice))111	uassert.AbortsWithMessage(t, cur, "compact: empty entry", func() {112		Add(cross(cur), "")113	})114	uassert.AbortsWithMessage(t, cur, "compact: entry too long, 257 bytes against a 256 cap", func() {115		Add(cross(cur), strings.Repeat("x", maxText+1))116	})117	uassert.AbortsContains(t, cur, "compact: no live entry at", func() {118		Drop(cross(cur), 99999)119	})120}121122// TestQuoteCountsNodesRatherThanGuessingFromPayload is the design claim of the123// realm. The quote must depend on the NODE COUNT and not on how fat the124// entries were, which is exactly where a payload-derived bounty goes wrong.125func TestQuoteCountsNodesRatherThanGuessingFromPayload(cur realm, t *testing.T) {126	drain(cur, t)127128	// An empty board advertises nothing and must not claim a profit.129	empty := Quote()130	uassert.Equal(t, int64(0), empty.Bytes)131	uassert.False(t, empty.Worth())132133	// Eight tiny entries, dropped and measured.134	testing.SetRealm(testing.NewUserRealm(alice))135	base := entries.TotalSize()136	for i := 0; i < 8; i++ {137		Add(cross(cur), "x")138	}139	for i := base; i < base+8; i++ {140		Drop(cross(cur), i)141	}142	tiny := Quote()143	tinyNodes := Reclaimable()144	testing.SetRealm(testing.NewUserRealm(janitor))145	Compact(cross(cur))146147	// Eight fat entries, same count, same treatment.148	testing.SetRealm(testing.NewUserRealm(alice))149	base = entries.TotalSize()150	fat := strings.Repeat("y", maxText)151	for i := 0; i < 8; i++ {152		Add(cross(cur), fat)153	}154	for i := base; i < base+8; i++ {155		Drop(cross(cur), i)156	}157	big := Quote()158159	bigNodes := Reclaimable()160161	// 256x the payload, and the quote is still purely a function of the node162	// count. Not the same TOTAL: the two batches sit at different offsets in163	// an append-addressed index space, so the tree shape differs and one164	// reclaimed a node more than the other. That is the honest version of the165	// claim, and asserting equal totals (which an earlier draft of this test166	// did) only passed by luck of the offsets.167	uassert.True(t, tinyNodes > 0 && bigNodes > 0)168	uassert.Equal(t, storagecost.EstimateNodes(int64(tinyNodes)), tiny.Bytes)169	uassert.Equal(t, storagecost.EstimateNodes(int64(bigNodes)), big.Bytes)170171	// The rate is what must not move: same bytes per reclaimed node whether172	// the entries were 1 byte or 256. A payload-derived bounty would differ173	// by 256x here, which is the failure this realm is built to avoid.174	uassert.Equal(t, tiny.Bytes/int64(tinyNodes), big.Bytes/int64(bigNodes),175		"bytes per reclaimed node must not depend on payload size")176177	drain(cur, t)178}179180// TestRenderEscapesTheBoard: an entry is a caller's string landing in an181// inline markdown slot, so it goes through ui.Excerpt or it is an injection.182func TestRenderEscapesTheBoard(cur realm, t *testing.T) {183	drain(cur, t)184	testing.SetRealm(testing.NewUserRealm(alice))185	Add(cross(cur), "[Claim 100 GNOT](https://evil.example)\n# Official")186187	out := Render("")188	uassert.False(t, strings.Contains(out, "](https://evil.example)"), out)189	uassert.False(t, strings.Contains(out, "\n# Official"), out)190	uassert.True(t, strings.Contains(out, "Claim 100 GNOT"), out)191	uassert.True(t, strings.Contains(out, ui.Addr(alice)), out)192193	drain(cur, t)194}195196func TestRenderShowsBothIntegersAndTheAction(cur realm, t *testing.T) {197	drain(cur, t)198199	out := Render("")200	uassert.True(t, strings.Contains(out, "# Compact"), out)201	uassert.True(t, strings.Contains(out, "Add the first entry"), out)202	uassert.True(t, strings.Contains(out, "nothing to reclaim"), out)203204	testing.SetRealm(testing.NewUserRealm(alice))205	base := entries.TotalSize()206	for i := 0; i < 8; i++ {207		Add(cross(cur), "entry")208	}209	for i := base; i < base+8; i++ {210		Drop(cross(cur), i)211	}212	out = Render("")213	uassert.True(t, strings.Contains(out, "reclaimable nodes"), out)214	uassert.True(t, strings.Contains(out, "$help&func=Compact"), out)215216	drain(cur, t)217}218219// TestRenderNeverEmitsTwoBlankLines is what lets ExampleRender stay220// meaningful: gno collapses them, so output containing them can never be221// pinned by an example.222func TestRenderNeverEmitsTwoBlankLines(cur realm, t *testing.T) {223	drain(cur, t)224	testing.SetRealm(testing.NewUserRealm(alice))225	i := Add(cross(cur), "one")226	Add(cross(cur), "two")227	Drop(cross(cur), i)228229	for _, path := range []string{"", "anything"} {230		out := Render(path)231		uassert.False(t, strings.Contains(out, "\n\n\n"), "blank-line run in Render("+path+")")232	}233234	drain(cur, t)235}236

Functions

  • Add(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}, text string) int

  • Compact(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}) int

  • Drop(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}, index int)

  • Fragmentation() (live int, allocated int)

  • Quote() struct{Bytes int64; Refund int64; Fee int64; Net int64; BreakEven int64}

  • Reclaimable() int

  • Render(path string) string

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.