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/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/upkeep

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
upkeep
Namespace
g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39
Files
3 (gnomod.toml)
Exported functions
20
Module
gno.land/r/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/upkeep
gno
0.9

Files (3)

  • gnomod.tomltoml
  • upkeep.gnogno
  • upkeep_test.gnogno
upkeep_test.gnogno
1package upkeep23import (4	"chain"5	"chain/banker"6	"strings"7	"testing"89	"gno.land/p/nt/testutils/v0"10	"gno.land/p/nt/uassert/v0"1112	subs "gno.land/r/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/subscriptions"13	guardian "gno.land/r/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/timelock_guardian"14)1516var (17	testAdmin = testutils.TestAddress("ukadmin")18	funder    = testutils.TestAddress("ukfunder")19	poker     = testutils.TestAddress("ukpoker")20	mallory   = testutils.TestAddress("ukmallory")21	provider  = testutils.TestAddress("ukprov")22	subber    = testutils.TestAddress("uksubber")23	towner    = testutils.TestAddress("uktowner")24)2526// Harness facts, carried from the sibling suites: init runs with an27// empty origin (real deploys capture the signer); realm globals persist28// across tests while the banker resets; SetRealm binds to the CALLING29// frame only, so arming is inlined at every site; SkipHeights advances30// the clock 5s per height AND wipes the origin context.31func init() {32	admin = testAdmin33	sweeper = testAdmin34	self = chain.PackageAddress("gno.land/r/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/upkeep")35}3637func ugnot(n int64) chain.Coins { return chain.NewCoins(chain.NewCoin(Denom, n)) }3839func coinsOf(a address) int64 { return banker.NewReadonlyBanker().GetCoin(a, Denom) }4041// conserved asserts the realm's monetary invariant: everything the realm42// holds is either unspent pot or earned-but-unclaimed balance.43func conserved(t *testing.T) {44	t.Helper()45	if Held() != Pot()+UsersTotal() {46		t.Fatalf("conservation violated: Held=%d pot=%d users=%d",47			Held(), Pot(), UsersTotal())48	}49}5051// syncHeld re-issues the realm's expected holdings. The harness banker52// resets between tests while realm globals (pot, ledger) persist, so53// each money-touching test starts by making the banker match the books.54func syncHeld() {55	want := Pot() + UsersTotal()56	have := coinsOf(Address())57	if want > have {58		testing.IssueCoins(Address(), ugnot(want-have))59	}60}6162func fundAs(cur realm, from address, amount int64) {63	testing.SetRealm(testing.NewUserRealm(from))64	testing.SetOriginCaller(from)65	testing.SetOriginSend(ugnot(amount))66	testing.IssueCoins(Address(), ugnot(amount))67	Fund(cross(cur))68}6970func adminSet(cur realm, task string, amount int64) {71	testing.SetRealm(testing.NewUserRealm(testAdmin))72	testing.SetOriginCaller(testAdmin)73	testing.SetOriginSend(nil)74	SetReward(cross(cur), task, amount)75}7677// expirableSub drives the REAL subscriptions realm to a state where one78// subscription is past its grace window, and returns its id. This is79// the downstream fixture for the realm-to-realm tests: nothing is80// mocked.81func expirableSub(cur realm, t *testing.T) int64 {82	t.Helper()83	testing.SetRealm(testing.NewUserRealm(provider))84	testing.SetOriginCaller(provider)85	testing.SetOriginSend(nil)86	planID := subs.CreatePlan(cross(cur), "upkeep-fixture", "d", 1, 10, 1000)8788	testing.SetRealm(testing.NewUserRealm(subber))89	testing.SetOriginCaller(subber)90	testing.SetOriginSend(ugnot(1))91	testing.IssueCoins(subs.Address(), ugnot(1))92	subID := subs.Subscribe(cross(cur), planID)9394	// paidThrough = h+10; expirable at paidThrough+10. Skip well past.95	testing.SkipHeights(25)96	return subID97}9899// maturedAction drives the REAL guardian realm to a matured timelocked100// action and returns its id.101var targetSeq int102103func maturedAction(cur realm, t *testing.T) string {104	t.Helper()105	targetSeq++106	name := "uktarget" + string(rune('a'+targetSeq%26))107	testing.SetRealm(testing.NewUserRealm(towner))108	testing.SetOriginCaller(towner)109	testing.SetOriginSend(nil)110	guardian.RegisterTarget(cross(cur), name, guardian.MinDelayFloor, "")111112	testing.SetRealm(testing.NewUserRealm(towner))113	testing.SetOriginCaller(towner)114	testing.SetOriginSend(nil)115	id := guardian.Schedule(cross(cur), name, "poke-fixture", guardian.MinDelayFloor)116117	testing.SkipHeights(13) // 65s at 5s/height >= the 60s floor118	return id119}120121func TestInitialState(t *testing.T) {122	if Admin() != testAdmin || Pot() != 0 || Pokes() != 0 || Funded() != 0 {123		t.Fatal("unexpected initial state")124	}125	if RewardFor(TaskSubExpire) != 0 || RewardFor(TaskTimelockExecute) != 0 {126		t.Fatal("rewards must default to 0")127	}128	conserved(t)129}130131func TestFundAndRewardConfig(cur realm, t *testing.T) {132	fundAs(cur, funder, 100000)133	if Pot() != 100000 || Funded() != 100000 {134		t.Fatalf("pot=%d funded=%d", Pot(), Funded())135	}136	conserved(t)137138	testing.SetRealm(testing.NewUserRealm(mallory))139	testing.SetOriginCaller(mallory)140	testing.SetOriginSend(nil)141	uassert.AbortsWithMessage(t, cur, "admin only", func() {142		SetReward(cross(cur), TaskSubExpire, 100)143	})144145	testing.SetRealm(testing.NewUserRealm(testAdmin))146	testing.SetOriginCaller(testAdmin)147	testing.SetOriginSend(nil)148	uassert.AbortsContains(t, cur, "reward must be in", func() {149		SetReward(cross(cur), TaskSubExpire, MaxReward+1)150	})151	uassert.AbortsContains(t, cur, "unknown task", func() {152		SetReward(cross(cur), "nosuch", 1)153	})154155	adminSet(cur, TaskSubExpire, 5000)156	adminSet(cur, TaskTimelockExecute, 7000)157	if RewardFor(TaskSubExpire) != 5000 || RewardFor(TaskTimelockExecute) != 7000 {158		t.Fatal("rewards not set")159	}160}161162// The realm-to-realm happy path: poke the REAL subscriptions realm.163func TestPokeExpire(cur realm, t *testing.T) {164	syncHeld()165	subID := expirableSub(cur, t)166167	// Downstream state before: still Active.168	_, _, _, _, _, _, status := subs.SubInfo(subID)169	if status != "active" {170		t.Fatalf("fixture status %q", status)171	}172173	potBefore, balBefore := Pot(), BalanceOf(poker)174	testing.SetRealm(testing.NewUserRealm(poker))175	testing.SetOriginCaller(poker)176	testing.SetOriginSend(nil)177	PokeExpire(cross(cur), subID)178179	// Downstream state changed by OUR realm's crossing call...180	_, _, _, _, _, _, status = subs.SubInfo(subID)181	if status != "expired" {182		t.Fatalf("downstream not expired: %q", status)183	}184	// ...and the reward moved pot -> caller balance atomically.185	if Pot() != potBefore-5000 || BalanceOf(poker) != balBefore+5000 {186		t.Fatalf("reward accounting wrong: pot %d->%d bal %d->%d",187			potBefore, Pot(), balBefore, BalanceOf(poker))188	}189	if Pokes() != 1 {190		t.Fatalf("pokes=%d", Pokes())191	}192	conserved(t)193}194195// Replay: the downstream realm is the replay guard; a second poke on the196// same subscription aborts THERE and no reward is paid HERE.197func TestPokeExpireReplay(cur realm, t *testing.T) {198	syncHeld()199	potBefore, balBefore := Pot(), BalanceOf(mallory)200	testing.SetRealm(testing.NewUserRealm(mallory))201	testing.SetOriginCaller(mallory)202	testing.SetOriginSend(nil)203	uassert.AbortsContains(t, cur, "subscription is expired", func() {204		PokeExpire(cross(cur), 0) // the sub expired in TestPokeExpire205	})206	if Pot() != potBefore || BalanceOf(mallory) != balBefore || Pokes() != 1 {207		t.Fatal("aborted poke mutated reward state")208	}209	conserved(t)210}211212// A premature poke aborts downstream ("not expirable") and pays nothing.213func TestPokeExpirePremature(cur realm, t *testing.T) {214	syncHeld()215	testing.SetRealm(testing.NewUserRealm(provider))216	testing.SetOriginCaller(provider)217	testing.SetOriginSend(nil)218	planID := subs.CreatePlan(cross(cur), "fresh", "d", 1, 5000, 1000)219	testing.SetRealm(testing.NewUserRealm(subber))220	testing.SetOriginCaller(subber)221	testing.SetOriginSend(ugnot(1))222	testing.IssueCoins(subs.Address(), ugnot(1))223	freshID := subs.Subscribe(cross(cur), planID)224225	potBefore := Pot()226	testing.SetRealm(testing.NewUserRealm(poker))227	testing.SetOriginCaller(poker)228	testing.SetOriginSend(nil)229	uassert.AbortsContains(t, cur, "not expirable", func() {230		PokeExpire(cross(cur), freshID)231	})232	uassert.AbortsContains(t, cur, "unknown subscription id", func() {233		PokeExpire(cross(cur), 99999)234	})235	if Pot() != potBefore {236		t.Fatal("failed pokes touched the pot")237	}238	conserved(t)239}240241// The second realm-to-realm integration: the REAL guardian realm.242func TestPokeExecute(cur realm, t *testing.T) {243	syncHeld()244	id := maturedAction(cur, t)245	potBefore, balBefore := Pot(), BalanceOf(poker)246247	testing.SetRealm(testing.NewUserRealm(poker))248	testing.SetOriginCaller(poker)249	testing.SetOriginSend(nil)250	PokeExecute(cross(cur), id)251252	if Pot() != potBefore-7000 || BalanceOf(poker) != balBefore+7000 {253		t.Fatal("timelock reward accounting wrong")254	}255	// Replay refused downstream.256	testing.SetRealm(testing.NewUserRealm(mallory))257	testing.SetOriginCaller(mallory)258	testing.SetOriginSend(nil)259	uassert.AbortsContains(t, cur, "already executed", func() {260		PokeExecute(cross(cur), id)261	})262	uassert.AbortsContains(t, cur, "action not found", func() {263		PokeExecute(cross(cur), "tl-nosuch")264	})265	// Premature: schedule a new one, poke before maturity.266	testing.SetRealm(testing.NewUserRealm(towner))267	testing.SetOriginCaller(towner)268	testing.SetOriginSend(nil)269	guardian.RegisterTarget(cross(cur), "ukearly", guardian.MinDelayFloor, "")270	testing.SetRealm(testing.NewUserRealm(towner))271	testing.SetOriginCaller(towner)272	testing.SetOriginSend(nil)273	early := guardian.Schedule(cross(cur), "ukearly", "x", guardian.MinDelayFloor)274	testing.SetRealm(testing.NewUserRealm(poker))275	testing.SetOriginCaller(poker)276	testing.SetOriginSend(nil)277	uassert.AbortsContains(t, cur, "too early", func() {278		PokeExecute(cross(cur), early)279	})280	conserved(t)281}282283// Pot exhaustion refuses BEFORE the downstream call: no valve fires284// unrewarded through this realm. Entering here the pot is deterministic:285// 100000 funded minus the 5000 and 7000 awarded above = 88000, so four286// MaxReward pokes leave 8000 < MaxReward and the next must refuse.287func TestPotExhaustion(cur realm, t *testing.T) {288	syncHeld()289	adminSet(cur, TaskSubExpire, MaxReward)290291	// Drain by REAL pokes until the pot no longer covers the reward.292	for i := 0; Pot() >= MaxReward; i++ {293		if i >= 10 {294			t.Fatalf("fixture arithmetic broken: pot=%d after %d pokes", Pot(), i)295		}296		id := expirableSub(cur, t)297		testing.SetRealm(testing.NewUserRealm(poker))298		testing.SetOriginCaller(poker)299		testing.SetOriginSend(nil)300		PokeExpire(cross(cur), id)301	}302303	short := expirableSub(cur, t)304	potBefore := Pot()305	testing.SetRealm(testing.NewUserRealm(poker))306	testing.SetOriginCaller(poker)307	testing.SetOriginSend(nil)308	uassert.AbortsContains(t, cur, "reward pot cannot cover", func() {309		PokeExpire(cross(cur), short)310	})311	// Downstream MUST be untouched by the refused poke.312	_, _, _, _, _, _, status := subs.SubInfo(short)313	if status != "active" {314		t.Fatal("refused poke reached the downstream realm")315	}316	if Pot() != potBefore {317		t.Fatal("refused poke touched the pot")318	}319	conserved(t)320	adminSet(cur, TaskSubExpire, 5000) // restore for the later tests321}322323// The refusal boundary, deterministic: unknown task, zero reward, and324// pot-short all refuse before any downstream effect.325func TestPokeRefusals(cur realm, t *testing.T) {326	adminSet(cur, TaskSubExpire, 0)327	testing.SetRealm(testing.NewUserRealm(poker))328	testing.SetOriginCaller(poker)329	testing.SetOriginSend(nil)330	uassert.AbortsContains(t, cur, "no reward configured", func() {331		PokeExpire(cross(cur), 0)332	})333	adminSet(cur, TaskSubExpire, 5000)334335	testing.SetRealm(testing.NewUserRealm(poker))336	testing.SetOriginCaller(poker)337	testing.SetOriginSend(ugnot(5))338	uassert.AbortsWithMessage(t, cur, "this function does not accept coins", func() {339		PokeExpire(cross(cur), 0)340	})341	testing.SetRealm(testing.NewUserRealm(poker))342	testing.SetOriginCaller(poker)343	testing.SetOriginSend(ugnot(5))344	uassert.AbortsWithMessage(t, cur, "this function does not accept coins", func() {345		PokeExecute(cross(cur), "x")346	})347}348349func TestClaims(cur realm, t *testing.T) {350	syncHeld()351	earned := BalanceOf(poker)352	if earned == 0 {353		t.Fatal("fixture: poker should have earnings")354	}355	before := coinsOf(poker)356	testing.SetRealm(testing.NewUserRealm(poker))357	testing.SetOriginCaller(poker)358	testing.SetOriginSend(nil)359	ClaimAll(cross(cur))360	if coinsOf(poker)-before != earned || BalanceOf(poker) != 0 {361		t.Fatal("claim did not move the earned balance")362	}363	testing.SetRealm(testing.NewUserRealm(mallory))364	testing.SetOriginCaller(mallory)365	testing.SetOriginSend(nil)366	uassert.AbortsWithMessage(t, cur, "nothing to claim", func() {367		ClaimAll(cross(cur))368	})369	uassert.AbortsContains(t, cur, "insufficient balance", func() {370		Claim(cross(cur), 1)371	})372	conserved(t)373}374375func TestSweepReserve(cur realm, t *testing.T) {376	syncHeld()377	testing.SetRealm(testing.NewUserRealm(testAdmin))378	testing.SetOriginCaller(testAdmin)379	testing.SetOriginSend(nil)380	uassert.AbortsContains(t, cur, "no surplus to sweep", func() {381		SweepDenom(cross(cur), Denom)382	})383	testing.IssueCoins(Address(), ugnot(77))384	reserveBefore := Pot() + UsersTotal()385	got := coinsOf(testAdmin)386	testing.SetRealm(testing.NewUserRealm(testAdmin))387	testing.SetOriginCaller(testAdmin)388	testing.SetOriginSend(nil)389	SweepDenom(cross(cur), Denom)390	if coinsOf(testAdmin)-got != 77 {391		t.Fatal("sweep amount wrong")392	}393	if Pot()+UsersTotal() != reserveBefore {394		t.Fatal("sweep touched the reserve")395	}396	testing.SetRealm(testing.NewUserRealm(mallory))397	testing.SetOriginCaller(mallory)398	testing.SetOriginSend(nil)399	uassert.AbortsWithMessage(t, cur, "sweeper only", func() {400		SweepDenom(cross(cur), Denom)401	})402	conserved(t)403}404405func TestAdminHandover(cur realm, t *testing.T) {406	testing.SetRealm(testing.NewUserRealm(testAdmin))407	testing.SetOriginCaller(testAdmin)408	testing.SetOriginSend(nil)409	TransferAdmin(cross(cur), funder)410	testing.SetRealm(testing.NewUserRealm(mallory))411	testing.SetOriginCaller(mallory)412	testing.SetOriginSend(nil)413	uassert.AbortsWithMessage(t, cur, "only the staged successor may accept", func() {414		AcceptAdmin(cross(cur))415	})416	testing.SetRealm(testing.NewUserRealm(funder))417	testing.SetOriginCaller(funder)418	testing.SetOriginSend(nil)419	AcceptAdmin(cross(cur))420	if Admin() != funder {421		t.Fatal("authority did not move")422	}423	// Former admin locked out; sweeper moved with admin.424	testing.SetRealm(testing.NewUserRealm(testAdmin))425	testing.SetOriginCaller(testAdmin)426	testing.SetOriginSend(nil)427	uassert.AbortsWithMessage(t, cur, "admin only", func() {428		SetReward(cross(cur), TaskSubExpire, 1)429	})430	uassert.AbortsWithMessage(t, cur, "sweeper only", func() {431		SweepDenom(cross(cur), Denom)432	})433	// Hand back.434	testing.SetRealm(testing.NewUserRealm(funder))435	testing.SetOriginCaller(funder)436	testing.SetOriginSend(nil)437	TransferAdmin(cross(cur), testAdmin)438	testing.SetRealm(testing.NewUserRealm(testAdmin))439	testing.SetOriginCaller(testAdmin)440	testing.SetOriginSend(nil)441	AcceptAdmin(cross(cur))442}443444func TestRender(t *testing.T) {445	out := Render("")446	if !strings.Contains(out, "sub_expire") || !strings.Contains(out, "timelock_execute") {447		t.Fatal("render missing task lines")448	}449	if Render("x") == Render("") {450		t.Fatal("unknown path fell through")451	}452}453

Functions

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

  • Address() string

  • Admin() string

  • BalanceOf(a string) int64

  • Claim(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}, amount int64)

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

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

  • Funded() int64

  • Held() int64

  • PendingAdmin() string

  • PokeExecute(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}, actionID string)

  • PokeExpire(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}, subID int64)

  • Pokes() int64

  • Pot() int64

  • Render(path string) string

  • RewardFor(task string) int64

  • SetReward(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}, task string, amount int64)

  • SweepDenom(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}, denom string)

  • TransferAdmin(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}, successor string)

  • UsersTotal() 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.