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/g1leu8d2vsplhehcfkjg50mwgdpxdkt8tztu95wr/bubblerumble4

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
bubblerumble4
Namespace
g1leu8d2vsplhehcfkjg50mwgdpxdkt8tztu95wr
Files
3 (gnomod.toml)
Exported functions
18
Module
gno.land/r/g1leu8d2vsplhehcfkjg50mwgdpxdkt8tztu95wr/bubblerumble4
gno
0.9

Files (3)

  • gnomod.tomltoml
  • bubblerumble4.gnogno
  • bubblerumble4_test.gnogno
bubblerumble4_test.gnogno
1package bubblerumble423import (4	"chain"5	"chain/banker"6	"chain/runtime"7	"crypto/sha256"8	"encoding/hex"9	"strings"10	"testing"11	"time"12)1314const realmPath = "gno.land/r/g1leu8d2vsplhehcfkjg50mwgdpxdkt8tztu95wr/bubblerumble4"1516var (17	alice = address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5")18	bob   = address("g1dmt3sa5ucvecxuhf3j6ne5r0e3z4x7h6c03xc0")19	carol = address("g1akeqsvhucjt8gf5yupyzjxsjd29wv8fayng37c")20	dave  = address("g1leu8d2vsplhehcfkjg50mwgdpxdkt8tztu95wr") // also the admin21)2223func aborts(t *testing.T, want string, f func()) {24	t.Helper()25	r := revive(f)26	if r == nil {27		t.Fatalf("expected an abort containing %q, got none", want)28	}29	msg := ""30	switch e := r.(type) {31	case error:32		msg = e.Error()33	case string:34		msg = e35	}36	if !strings.Contains(msg, want) {37		t.Fatalf("abort %q does not contain %q", msg, want)38	}39}4041func held(who address) int64 {42	return banker.NewReadonlyBanker().GetCoins(who).AmountOf("ugnot")43}4445// fund attaches ugnot to the next call the way a transaction would. The caller46// is set with testing.SetRealm in the test body: it binds to the calling frame.47func fund(ugnot int64) {48	if ugnot > 0 {49		testing.IssueCoins(chain.PackageAddress(realmPath), chain.Coins{{"ugnot", ugnot}})50		testing.SetOriginSend(chain.Coins{{"ugnot", ugnot}})51	} else {52		testing.SetOriginSend(nil)53	}54}5556// heldAtReset is what the realm held when the scenario began: coins from57// earlier scenarios stay in the test realm after reset() forgets their pools.58var heldAtReset int645960func reset() {61	pools = nil62	players = map[address]*Player{}63	teamPaid = map[string]int64{}64	teamWon = map[string]int64{}65	teamSize = map[string]int64{}66	shotsOn = true67	keeper, keeperSeen, commits, head, voided, late = "", time.Time{}, nil, 0, 0, 068	heldAtReset = held(chain.PackageAddress(realmPath))69}7071// the keeper in these tests is carol; dave (the admin) names her, and she commits72func keeperUp(cur realm, t *testing.T, secrets ...[]byte) {73	t.Helper()74	fund(0)75	testing.SetRealm(testing.NewUserRealm(dave))76	SetKeeper(cross(cur), carol)77	if len(secrets) > 0 {78		commitSecrets(cur, secrets...)79	} else {80		fund(0)81		testing.SetRealm(testing.NewUserRealm(carol))82		Heartbeat(cross(cur))83	}84}8586func hashOf(secret []byte) string { sum := sha256.Sum256(secret); return hex.EncodeToString(sum[:]) }8788func commitSecrets(cur realm, secrets ...[]byte) {89	hs := []string{}90	for _, s := range secrets {91		hs = append(hs, hashOf(s))92	}93	fund(0)94	testing.SetRealm(testing.NewUserRealm(carol))95	Commit(cross(cur), strings.Join(hs, ","))96}9798func secret(n byte) []byte { return []byte{n, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 2799100// a pool with dave's first bid, alice's second carrying a shot; the shot is sold when sold is true (else why says101// what the realm should have refused it for)102func shotPool(cur realm, t *testing.T, shot int64, sold bool, why ...string) (int64, *Pool) {103	t.Helper()104	fund(0)105	testing.SetRealm(testing.NewUserRealm(dave))106	id := CreatePool(cross(cur), "shots", 3600, 1_000_000, 0)107	p := pool(id)108	fund(5_000_000)109	testing.SetRealm(testing.NewUserRealm(dave))110	Bid(cross(cur), id, "", "", 0)111	a0 := held(alice)112	if len(why) > 0 {113		if got := p.shotRefusal(alice, shot); !strings.Contains(got, why[0]) {114			t.Fatalf("refusal %q, want %q", got, why[0])115		}116	}117	fund(1_414_214 + shot)118	testing.SetRealm(testing.NewUserRealm(alice))119	Bid(cross(cur), id, "", "", shot)120	if p.Bids != 2 {121		t.Fatalf("the bid did not land: %+v", *p)122	}123	if sold && (len(p.Shots) != 1 || p.ShotEscrow != shot) {124		t.Fatalf("the shot was not sold: shots %d escrow %d", len(p.Shots), p.ShotEscrow)125	}126	// (a test's fund() issues the coins to the realm; alice's own balance only ever sees what comes back)127	if !sold && (len(p.Shots) != 0 || p.ShotEscrow != 0 || held(alice)-a0 != shot || p.find(alice).Paid != 1_414_214) {128		t.Fatalf("the shot should have come back: shots %d escrow %d alice %d paid %d", len(p.Shots), p.ShotEscrow, held(alice)-a0, p.find(alice).Paid)129	}130	return id, p131}132133// The conservation check: every ugnot the realm took in during the scenario is134// either in an open pot or credited to somebody.135func realmHeld() int64 { return held(chain.PackageAddress(realmPath)) - heldAtReset }136137func owedAll() int64 {138	var sum int64139	for _, p := range pools {140		if !p.Paid {141			sum += p.Pot + p.ShotEscrow142		}143	}144	for _, pl := range players {145		sum += pl.Owed146	}147	return sum148}149150func conserved(t *testing.T) {151	t.Helper()152	if realmHeld() != owedAll() {153		t.Fatalf("realm holds %d, pots and credits account for %d", realmHeld(), owedAll())154	}155}156157func owed(a address) int64 { return player(a).Owed }158159func TestSplitSelfRebidFlagLockAndWithdraw(cur realm, t *testing.T) {160	reset()161	fund(0)162	testing.SetRealm(testing.NewUserRealm(dave))163	id := CreatePool(cross(cur), "split", 3600, 1_000_000, 0)164	p := pool(id)165	if p.Life != 10*3600 {166		t.Fatalf("default life is ten windows: %d", p.Life)167	}168169	// first bid: dividends and the inviter's cut fall back into the pot, the creator is paid 5% at once170	d0 := held(dave)171	fund(1_000_000)172	testing.SetRealm(testing.NewUserRealm(alice))173	Bid(cross(cur), id, "", "kr", 0)174	if p.Pot != 950_000 || held(dave)-d0 != 50_000 || p.DivPaid != 0 || p.FirstBidAt.IsZero() {175		t.Fatalf("first bid: pot %d creator +%d div %d", p.Pot, held(dave)-d0, p.DivPaid)176	}177178	// bob, invited by alice, bids 2 GNOT: alice is credited all the dividends and paid 10% as inviter at once179	a0 := held(alice)180	fund(2_000_000)181	testing.SetRealm(testing.NewUserRealm(bob))182	Bid(cross(cur), id, alice.String(), "us", 0)183	if owed(alice) != 600_000 || held(alice)-a0 != 200_000 || p.Pot != 950_000+2_000_000-600_000-200_000-100_000 {184		t.Fatalf("second bid: alice owed %d, +%d in hand, pot %d", owed(alice), held(alice)-a0, p.Pot)185	}186	if player(bob).Referrer != alice || player(alice).Recruits != 1 || p.find(alice).Earned != 600_000 {187		t.Fatalf("inviter: %+v", *player(bob))188	}189190	// bob rebids on himself, switching his flag to KR: allowed; his dividends go191	// to alice, not himself; his flag in THIS pool stays US (locked at his first bid)192	b1 := held(bob)193	fund(2_000_000)194	testing.SetRealm(testing.NewUserRealm(bob))195	Bid(cross(cur), id, "", "kr", 0)196	if p.LastBidder != bob || p.Bids != 3 || owed(alice) != 1_200_000 || owed(bob) != 0 || b1-held(bob) != 0 {197		t.Fatalf("self rebid: last %s bids %d alice owed %d bob owed %d", p.LastBidder, p.Bids, owed(alice), owed(bob))198	}199	if player(bob).Flag != "KR" || p.find(bob).Flag != "US" || p.History[2].Flag != "US" {200		t.Fatalf("flag lock: player %s member %s history %s", player(bob).Flag, p.find(bob).Flag, p.History[2].Flag)201	}202	conserved(t)203204	// alice withdraws what she is owed; a second withdraw has nothing205	a1 := held(alice)206	fund(0)207	testing.SetRealm(testing.NewUserRealm(alice))208	if got := Withdraw(cross(cur)); got != 1_200_000 || held(alice)-a1 != 1_200_000 || owed(alice) != 0 || player(alice).Withdrawn != 1_200_000 {209		t.Fatalf("withdraw: %d, +%d, owed %d", got, held(alice)-a1, owed(alice))210	}211	aborts(t, "nothing to withdraw", func() { Withdraw(cross(cur)) })212	conserved(t)213214	// in a new pool, bob's locked flag is his current one215	fund(0)216	testing.SetRealm(testing.NewUserRealm(dave))217	id2 := CreatePool(cross(cur), "next", 3600, 1_000_000, 0)218	fund(1_000_000)219	testing.SetRealm(testing.NewUserRealm(bob))220	Bid(cross(cur), id2, "", "", 0)221	if pool(id2).find(bob).Flag != "KR" {222		t.Fatalf("new pool flag %s", pool(id2).find(bob).Flag)223	}224}225226func TestBoostFloor(t *testing.T) {227	if c := ClockFor(3600, 100, 1000); c != 360 {228		t.Fatalf("ten times the price on an hour: %d", c)229	}230	if c := ClockFor(600, 100, 1000); c != MinClock {231		t.Fatalf("ten times the price on ten minutes floors at five: %d", c)232	}233	if c := ClockFor(60, 100, 100); c != 60 {234		t.Fatalf("a plain bid on a one-minute window keeps the window: %d", c)235	}236	if c := ClockFor(60, 100, 1000); c != 60 {237		t.Fatalf("no boost can shorten a window under the floor: %d", c)238	}239	if mulDiv(80_000_000_000, 3_000_000_000, 7_000_000_000) != 34_285_714_285 {240		t.Fatal("mulDiv on numbers whose product passes int64")241	}242}243244func TestLastDaySlidesToProRata(cur realm, t *testing.T) {245	reset()246	fund(0)247	testing.SetRealm(testing.NewUserRealm(dave))248	// a one-hour window and a 90-minute life: the life ends before the second clock would249	id := CreatePool(cross(cur), "slide", 3600, 1_000_000, 5400)250	p := pool(id)251	fund(1_000_000)252	testing.SetRealm(testing.NewUserRealm(alice))253	Bid(cross(cur), id, "", "FR", 0)254	if p.LiveBps() != 10000 {255		t.Fatalf("fresh pool live %d", p.LiveBps())256	}257	// 50 minutes pass (5s a block), bob bids exactly the price (more would shorten258	// his clock): 2400 of 5400 seconds of life are left259	testing.SkipHeights(600)260	fund(1_414_214)261	testing.SetRealm(testing.NewUserRealm(bob))262	Bid(cross(cur), id, "", "US", 0)263	if p.LiveBps() != 10000*2400/5400 {264		t.Fatalf("live %d", p.LiveBps())265	}266	if !p.Deadline().Equal(p.LastDay()) {267		t.Fatalf("deadline %v should be the last day %v, not the clock", p.Deadline(), p.LastDay())268	}269	// the last day comes with time still on bob's clock: the pool is over anyway270	testing.SkipHeights(600)271	if !p.Over() || p.LiveBps() != 0 {272		t.Fatalf("over %v live %d", p.Over(), p.LiveBps())273	}274	pot := p.Pot275	a0, b0 := owed(alice), owed(bob) // alice already holds dividends from bob's bid276	fund(0)277	testing.SetRealm(testing.NewUserRealm(carol))278	Claim(cross(cur), id)279	// bob (US) won alone under his flag, so the team share goes to the other bidder,280	// alice; nothing is live, so the rest is split by paid-in281	cut := pot * TeamShare / 100282	rest := pot - cut283	total := p.find(alice).Paid + p.find(bob).Paid284	wantA := cut + rest*p.find(alice).Paid/total285	if owed(alice)-a0 != wantA || owed(bob)-b0 != pot-wantA {286		t.Fatalf("alice +%d (want %d) bob +%d (want %d)", owed(alice)-a0, wantA, owed(bob)-b0, pot-wantA)287	}288	if p.TeamPaid != cut || p.SharedPaid != rest*p.find(alice).Paid/total || p.WinnerPaid != pot-wantA {289		t.Fatalf("bookkeeping: team %d shared %d winner %d", p.TeamPaid, p.SharedPaid, p.WinnerPaid)290	}291	conserved(t)292293	// a pool that closes early with a single bidder credits them everything294	reset()295	fund(0)296	testing.SetRealm(testing.NewUserRealm(dave))297	id = CreatePool(cross(cur), "quick", 60, 1_000_000, 7200)298	p = pool(id)299	fund(1_000_000)300	testing.SetRealm(testing.NewUserRealm(alice))301	Bid(cross(cur), id, "", "FR", 0)302	testing.SkipHeights(13) // 65s: the clock ran out, the life barely started303	fund(0)304	testing.SetRealm(testing.NewUserRealm(bob))305	Claim(cross(cur), id)306	if owed(alice) != p.Pot || p.SharedPaid != 0 || p.TeamPaid != 0 {307		t.Fatalf("solo: alice owed %d of %d, shared %d team %d", owed(alice), p.Pot, p.SharedPaid, p.TeamPaid)308	}309	conserved(t)310}311312func TestFlagWar(cur realm, t *testing.T) {313	reset()314	fund(0)315	testing.SetRealm(testing.NewUserRealm(dave))316	id := CreatePool(cross(cur), "team", 3600, 1_000_000, 0)317	p := pool(id)318	fund(20_000_000)319	testing.SetRealm(testing.NewUserRealm(dave))320	Bid(cross(cur), id, "", "FR", 0)321	fund(1_414_214)322	testing.SetRealm(testing.NewUserRealm(alice))323	Bid(cross(cur), id, "", "FR", 0)324	fund(1_732_051)325	testing.SetRealm(testing.NewUserRealm(carol))326	Bid(cross(cur), id, "", "US", 0)327	fund(2_000_000)328	testing.SetRealm(testing.NewUserRealm(alice))329	Bid(cross(cur), id, "", "", 0) // alice on top, FR330	testing.SkipHeights(721)331	pot := p.Pot332	live := p.LiveBps()333	if live == 0 || live == 10000 {334		t.Fatalf("live %d should be partway", live)335	}336	a0, d0, c0 := owed(alice), owed(dave), owed(carol)337	fund(0)338	testing.SetRealm(testing.NewUserRealm(bob))339	Claim(cross(cur), id)340	// the team share goes to dave, the only OTHER FR bidder; the winner takes none of it341	cut := pot * TeamShare / 100342	rest := pot - cut343	shared := rest - rest*live/10000344	total := p.find(alice).Paid + p.find(dave).Paid + p.find(carol).Paid345	daveShare, carolShare := shared*p.find(dave).Paid/total, shared*p.find(carol).Paid/total346	if owed(dave)-d0 != cut+daveShare {347		t.Fatalf("dave: +%d want team %d + share %d", owed(dave)-d0, cut, daveShare)348	}349	if owed(carol)-c0 != carolShare {350		t.Fatalf("carol (US) gets only her pro-rata share: +%d want %d", owed(carol)-c0, carolShare)351	}352	if owed(alice)-a0 != pot-cut-daveShare-carolShare {353		t.Fatalf("alice: +%d want %d", owed(alice)-a0, pot-cut-daveShare-carolShare)354	}355	if p.WinnerFlag != "FR" || teamWon["FR"] != pot || p.TeamPaid != cut || p.SharedPaid != daveShare+carolShare {356		t.Fatalf("bookkeeping: %+v", *p)357	}358	conserved(t)359	aborts(t, "already paid out", func() { Claim(cross(cur), id) })360361	// a private flag buys nothing: a winner nobody else flew with hands the team362	// share to all the other bidders, pro-rata363	reset()364	fund(0)365	testing.SetRealm(testing.NewUserRealm(dave))366	id = CreatePool(cross(cur), "lone", 60, 1_000_000, 0)367	p = pool(id)368	fund(1_000_000)369	testing.SetRealm(testing.NewUserRealm(alice))370	Bid(cross(cur), id, "", "FR", 0)371	fund(1_414_214)372	testing.SetRealm(testing.NewUserRealm(carol))373	Bid(cross(cur), id, "", "US", 0)374	fund(1_732_051)375	testing.SetRealm(testing.NewUserRealm(bob))376	Bid(cross(cur), id, "", "AQ", 0) // bob, alone under AQ, on top377	testing.SkipHeights(13)378	pot = p.Pot379	a0, c0, b0 := owed(alice), owed(carol), owed(bob)380	fund(0)381	testing.SetRealm(testing.NewUserRealm(dave))382	Claim(cross(cur), id)383	cut = pot * TeamShare / 100384	others := p.find(alice).Paid + p.find(carol).Paid385	// the life (ten minutes) had 65 seconds on it: a sliver of the rest is shared too386	rest = pot - cut387	shared = rest - rest*p.LiveBps()/10000388	total = others + p.find(bob).Paid389	wantA := cut*p.find(alice).Paid/others + shared*p.find(alice).Paid/total390	wantC := cut*p.find(carol).Paid/others + shared*p.find(carol).Paid/total391	if owed(alice)-a0 != wantA || owed(carol)-c0 != wantC || owed(bob)-b0 != pot-wantA-wantC {392		t.Fatalf("lone flag: alice +%d (want %d) carol +%d (want %d) bob +%d", owed(alice)-a0, wantA, owed(carol)-c0, wantC, owed(bob)-b0)393	}394	conserved(t)395}396397func TestRosterCapAndCreateBounds(cur realm, t *testing.T) {398	reset()399	fund(0)400	testing.SetRealm(testing.NewUserRealm(dave))401	id := CreatePool(cross(cur), "cap", 3600, 1_000, 0)402	p := pool(id)403	p.Roster = nil404	for i := 0; i < RosterMax; i++ {405		p.Roster = append(p.Roster, &Member{Addr: address("g1x" + itoa(int64(i))), Paid: 1})406	}407	fund(1_000)408	testing.SetRealm(testing.NewUserRealm(alice))409	Bid(cross(cur), id, "", "", 0)410	if len(p.Roster) != RosterMax || p.Roster[len(p.Roster)-1].Addr != alice || p.Roster[0].Addr != address("g1x1") {411		t.Fatalf("roster cap: len %d first %s last %s", len(p.Roster), p.Roster[0].Addr, p.Roster[len(p.Roster)-1].Addr)412	}413	fund(0)414	testing.SetRealm(testing.NewUserRealm(dave))415	aborts(t, "life must be at least the window", func() { CreatePool(cross(cur), "x", 7200, 1_000_000, 3600) })416	aborts(t, "life must be between", func() { CreatePool(cross(cur), "x", 3600, 1_000_000, 31*24*3600) })417	id = CreatePool(cross(cur), "x", 20*24*3600, 1_000_000, 0)418	if pool(id).Life != MaxLife {419		t.Fatalf("ten windows past the maximum life is the maximum: %d", pool(id).Life)420	}421	id = CreatePool(cross(cur), "y", 10, 1_000_000, 0)422	if pool(id).Life != MinLife {423		t.Fatalf("ten windows under the minimum life is the minimum: %d", pool(id).Life)424	}425}426427428func TestPkgPath(t *testing.T) {429	if !strings.HasSuffix(pkgPath, "/bubblerumble4") || realmPath != pkgPath {430		t.Fatalf("pkgPath %q realmPath %q", pkgPath, realmPath)431	}432}433434func TestKeeperAndCommits(cur realm, t *testing.T) {435	reset()436	// the keeper is named by the deployer alone437	fund(0)438	testing.SetRealm(testing.NewUserRealm(alice))439	aborts(t, "only the deployer", func() { SetKeeper(cross(cur), alice) })440	fund(0)441	testing.SetRealm(testing.NewUserRealm(dave))442	aborts(t, "bad address", func() { SetKeeper(cross(cur), address("nope")) })443	// nobody commits or heartbeats but the keeper — not even the deployer444	fund(0)445	testing.SetRealm(testing.NewUserRealm(dave))446	aborts(t, "only the keeper", func() { Commit(cross(cur), hashOf(secret(1))) })447	aborts(t, "only the keeper", func() { Heartbeat(cross(cur)) })448	keeperUp(cur, t, secret(1), secret(2))449	if len(commits) != 2 || head != 0 || keeperSeen.IsZero() || commitHead() != hashOf(secret(1)) {450		t.Fatalf("commits %d head %d seen %v", len(commits), head, keeperSeen)451	}452	fund(0)453	testing.SetRealm(testing.NewUserRealm(alice))454	aborts(t, "only the keeper", func() { Commit(cross(cur), hashOf(secret(3))) })455	fund(0)456	testing.SetRealm(testing.NewUserRealm(carol))457	aborts(t, "64 hex", func() { Commit(cross(cur), "abc") })458	aborts(t, "already committed", func() { Commit(cross(cur), hashOf(secret(2))) })459	many := []string{}460	for i := 0; i < CommitBatch+1; i++ {461		many = append(many, hashOf(secret(byte(100+i))))462	}463	aborts(t, "hashes", func() { Commit(cross(cur), strings.Join(many, ",")) })464	// the unused list, for a keeper that starts and must know what it holds465	if c := Render("commits"); !strings.Contains(c, hashOf(secret(1))+"\",\""+hashOf(secret(2))) {466		t.Fatalf("commits: %s", c)467	}468	// retire clamps, is the keeper's or the deployer's, and is no sign of life469	seen := keeperSeen470	testing.SkipHeights(2)471	fund(0)472	testing.SetRealm(testing.NewUserRealm(alice))473	aborts(t, "only the keeper or the deployer", func() { Retire(cross(cur), 1) })474	fund(0)475	testing.SetRealm(testing.NewUserRealm(dave))476	Retire(cross(cur), 5)477	if len(commits) != 0 || keeperSeen != seen {478		t.Fatalf("retire: %d left, seen moved %v", len(commits), keeperSeen != seen)479	}480	// a new keeper drops what the old one queued481	commitSecrets(cur, secret(7))482	fund(0)483	testing.SetRealm(testing.NewUserRealm(dave))484	SetKeeper(cross(cur), bob)485	if len(commits) != 0 || keeper != bob {486		t.Fatalf("set keeper: %d commits, keeper %s", len(commits), keeper)487	}488	// the queue's bound489	reset()490	keeperUp(cur, t)491	for i := 0; i < MaxCommits/CommitBatch; i++ {492		hs := []string{}493		for j := 0; j < CommitBatch; j++ {494			hs = append(hs, hashOf([]byte{byte(i), byte(j), 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 495		}496		fund(0)497		testing.SetRealm(testing.NewUserRealm(carol))498		Commit(cross(cur), strings.Join(hs, ","))499	}500	fund(0)501	testing.SetRealm(testing.NewUserRealm(carol))502	aborts(t, "at most", func() { Commit(cross(cur), hashOf(secret(9))) })503	// compaction: past CommitCompact used ones, the queue is rebuilt from the head504	head = CommitCompact + 1505	fund(0)506	testing.SetRealm(testing.NewUserRealm(carol))507	Commit(cross(cur), hashOf(secret(9)))508	if head != 0 || len(commits) != MaxCommits-CommitCompact-1+1 {509		t.Fatalf("compaction: head %d len %d", head, len(commits))510	}511}512513func TestShotSoldPricedOnTake(cur realm, t *testing.T) {514	reset()515	keeperUp(cur, t, secret(1))516	id, p := shotPool(cur, t, 1_000_000, true)517	sh := p.Shots[0]518	// the odds: on the pot a pop would pay from (the shot in, the tip out), against the most alice could take —519	// dave alone is the team, so the 35% cut counts as hers, and nothing has slid: the take is the pot520	potThen := p.Pot + 1_000_000 - RevealTip521	if sh.Take != potThen || sh.Bps != ShotBps(1_000_000-RevealTip, potThen) || sh.Bps == 0 || sh.Bps > ShotMaxBps {522		t.Fatalf("shot: %+v pot %d", *sh, p.Pot)523	}524	if sh.Hash != commits[0] || head != 1 || sh.CommitH != runtime.ChainHeight() || p.find(alice).Paid != 1_414_214+1_000_000 {525		t.Fatalf("commitment: %+v head %d", *sh, head)526	}527	if _, cut := p.payout(alice, potThen, false); p.Take(alice, potThen) != potThen-0 || cut != potThen*TeamShare/100 {528		t.Fatalf("take %d cut %d of %d", p.Take(alice, potThen), cut, potThen)529	}530	conserved(t)531	_ = id532}533534func TestShotComesBack(cur realm, t *testing.T) {535	// no commitment free: the bid stands, the shot's money comes back536	reset()537	keeperUp(cur, t)538	shotPool(cur, t, 1_000_000, false, "no commitment free")539	// the keeper not seen for KeeperFresh: the same540	reset()541	keeperUp(cur, t, secret(1))542	testing.SkipHeights(KeeperFresh/5 + 1)543	shotPool(cur, t, 1_000_000, false, "not been seen")544	// under MinShot545	reset()546	keeperUp(cur, t, secret(1))547	shotPool(cur, t, MinShot-1, false, "smallest shot")548	// shots off549	reset()550	keeperUp(cur, t, secret(1))551	fund(0)552	testing.SetRealm(testing.NewUserRealm(dave))553	SetShots(cross(cur), false)554	shotPool(cur, t, 1_000_000, false, "shots are off")555	// the keeper itself never shoots556	reset()557	keeperUp(cur, t, secret(1))558	fund(0)559	testing.SetRealm(testing.NewUserRealm(dave))560	id := CreatePool(cross(cur), "keeper", 3600, 1_000_000, 0)561	p := pool(id)562	fund(1_000_000 + 1_000_000)563	testing.SetRealm(testing.NewUserRealm(carol))564	Bid(cross(cur), id, "", "", 1_000_000)565	if len(p.Shots) != 0 || p.Bids != 1 || p.find(carol).Paid != 1_000_000 {566		t.Fatalf("the keeper's shot should come back: %+v", *p)567	}568	// a shot that buys no chance at all: a pot so large that MinShot is under one basis point569	reset()570	keeperUp(cur, t, secret(1))571	fund(0)572	testing.SetRealm(testing.NewUserRealm(dave))573	id = CreatePool(cross(cur), "huge", 3600, 1_000_000, 0)574	p = pool(id)575	fund(2_000_000_000_000) // a 2,000,000 GNOT seed576	testing.SetRealm(testing.NewUserRealm(dave))577	Bid(cross(cur), id, "", "", 0)578	fund(1_414_214 + MinShot)579	testing.SetRealm(testing.NewUserRealm(alice))580	Bid(cross(cur), id, "", "", MinShot)581	if len(p.Shots) != 0 || p.ShotEscrow != 0 || p.Bids != 2 || p.find(alice).Paid != 1_414_214 {582		t.Fatalf("a chanceless shot should come back: %+v", *p)583	}584	conserved(t)585	// the winner's share slid under the floor: a 1h window, a 90-minute life; bob keeps the clock alive at 50 minutes,586	// and 70 minutes in the live share is 2222 bps587	reset()588	keeperUp(cur, t, secret(1))589	fund(0)590	testing.SetRealm(testing.NewUserRealm(dave))591	id = CreatePool(cross(cur), "late life", 3600, 1_000_000, 5400)592	p = pool(id)593	fund(1_000_000)594	testing.SetRealm(testing.NewUserRealm(dave))595	Bid(cross(cur), id, "", "", 0)596	testing.SkipHeights(600) // 50 minutes597	fund(1_414_214)598	testing.SetRealm(testing.NewUserRealm(bob))599	Bid(cross(cur), id, "", "", 0)600	testing.SkipHeights(240) // 70 minutes in all601	fund(0)602	testing.SetRealm(testing.NewUserRealm(carol))603	Heartbeat(cross(cur))604	if p.LiveBps() >= LiveFloor || p.Over() {605		t.Fatalf("live %d over %v", p.LiveBps(), p.Over())606	}607	if got := p.shotRefusal(alice, 1_000_000); !strings.Contains(got, "slid under a quarter") {608		t.Fatalf("refusal %q", got)609	}610	fund(1_732_051 + 1_000_000)611	testing.SetRealm(testing.NewUserRealm(alice))612	Bid(cross(cur), id, "", "", 1_000_000)613	if len(p.Shots) != 0 || p.ShotEscrow != 0 || p.Bids != 3 {614		t.Fatalf("a shot in the last quarter should come back: %+v", *p)615	}616	conserved(t)617}618619func TestRevealHitOrMiss(cur realm, t *testing.T) {620	reset()621	keeperUp(cur, t, secret(1))622	id, p := shotPool(cur, t, 1_000_000, true)623	sh := p.Shots[0]624	s1 := hex.EncodeToString(secret(1))625	// too early: nothing to reveal yet626	fund(0)627	testing.SetRealm(testing.NewUserRealm(carol))628	aborts(t, "nothing revealed", func() { Reveal(cross(cur), itoa(id)+":1:"+s1) })629	testing.SkipHeights(ShotDelay)630	h := runtime.ChainHeight()631	fund(0)632	testing.SetRealm(testing.NewUserRealm(carol))633	aborts(t, "nothing revealed", func() { Reveal(cross(cur), itoa(id)+":1:"+hex.EncodeToString(secret(2))) })634	aborts(t, "pool:shot:secret", func() { Reveal(cross(cur), "1:1") })635	aborts(t, "no such shot", func() { Reveal(cross(cur), itoa(id)+":2:"+s1) })636	pot0, k0 := p.Pot, owed(carol)637	want := shotHits(seedFor(id, sh, secret(1), h), sh.Bps) // the same inputs the realm hashes, in the same block638	fund(0)639	testing.SetRealm(testing.NewUserRealm(bob)) // anyone may reveal; the tip is still the keeper's640	Reveal(cross(cur), itoa(id)+":1:"+s1)641	if !sh.Resolved || sh.Void || sh.Won != want || sh.DrawH != h || sh.Revealer != bob || p.ShotEscrow != 0 || p.Pot != pot0+1_000_000-RevealTip || owed(carol)-k0 != RevealTip || p.Late != 0 {642		t.Fatalf("revealed: %+v pot %d→%d carol +%d", *sh, pot0, p.Pot, owed(carol)-k0)643	}644	if want {645		if !p.Popped || p.LastBidder != alice || !p.Over() || p.Misses != 0 || player(alice).Hits != 1 {646			t.Fatalf("a hit pops: %+v", *p)647		}648	} else if p.Popped || p.LastBidder != alice || p.Misses != 1 {649		t.Fatalf("a miss is counted and changes nothing else: %+v", *p)650	}651	fund(0)652	testing.SetRealm(testing.NewUserRealm(carol))653	aborts(t, "nothing revealed", func() { Reveal(cross(cur), itoa(id)+":1:"+s1) }) // twice: skipped654	conserved(t)655	// the draw itself: over every seed in a cycle, the chance is exactly bps in 10000656	hits := 0657	for s := uint64(0); s < 10000; s++ {658		if shotHits(s, 32) {659			hits++660		}661	}662	if hits != 32 {663		t.Fatalf("32 bps should hit 32 of 10000 seeds, got %d", hits)664	}665	// the two outcomes, made sure of: a shot that cannot miss pops the pool and Claim pays the shooter as any666	// winner (dave, the only other bidder, is the team); one that cannot hit is a miss and the pool goes on667	for _, sure := range []int64{10000, 0} {668		reset()669		keeperUp(cur, t, secret(1))670		id, p := shotPool(cur, t, 1_000_000, true)671		p.Shots[0].Bps = sure672		testing.SkipHeights(ShotDelay)673		pot0 := p.Pot674		fund(0)675		testing.SetRealm(testing.NewUserRealm(carol))676		Reveal(cross(cur), itoa(id)+":1:"+hex.EncodeToString(secret(1)))677		if sure == 0 {678			if p.Popped || p.Misses != 1 || p.Over() || p.Pot != pot0+1_000_000-RevealTip {679				t.Fatalf("sure miss: %+v", *p)680			}681			conserved(t)682			continue683		}684		if !p.Popped || p.LastBidder != alice || !p.Over() || player(alice).Hits != 1 || p.Pot != pot0+1_000_000-RevealTip {685			t.Fatalf("sure hit: %+v", *p)686		}687		// a bid after the pop is refunded whole, shot included688		b0 := held(bob)689		fund(2_000_000 + 500_000)690		testing.SetRealm(testing.NewUserRealm(bob))691		Bid(cross(cur), id, "", "", 500_000)692		if held(bob)-b0 != 2_500_000 || p.Bids != 2 {693			t.Fatalf("after the pop: bob %d, bids %d", held(bob)-b0, p.Bids)694		}695		fund(0)696		testing.SetRealm(testing.NewUserRealm(bob))697		Claim(cross(cur), id)698		// the team's cut to dave, and the sliver of the slide the few blocks since the first bid moved his way too699		cut := p.Pot * TeamShare / 100700		if owed(alice) != p.Pot-cut-p.SharedPaid || p.TeamPaid != cut || p.WinnerPaid != owed(alice) || owed(dave) != cut+424_264+p.SharedPaid || p.SharedPaid > cut/100 {701			t.Fatalf("popped payout: alice owed %d of %d, team %d, shared %d, dave %d", owed(alice), p.Pot, p.TeamPaid, p.SharedPaid, owed(dave))702		}703		conserved(t)704	}705}706707func TestRevealOrderAndMany(cur realm, t *testing.T) {708	reset()709	keeperUp(cur, t, secret(1), secret(2))710	id, p := shotPool(cur, t, 1_000_000, true)711	fund(1_732_051 + 1_000_000)712	testing.SetRealm(testing.NewUserRealm(bob))713	Bid(cross(cur), id, "", "", 1_000_000)714	if len(p.Shots) != 2 || p.Shots[1].Hash != commits[1] || head != 2 || p.ShotEscrow != 2_000_000 {715		t.Fatalf("two shots: %d escrow %d head %d", len(p.Shots), p.ShotEscrow, head)716	}717	testing.SkipHeights(ShotDelay)718	s1, s2 := hex.EncodeToString(secret(1)), hex.EncodeToString(secret(2))719	// the second alone waits for the first720	fund(0)721	testing.SetRealm(testing.NewUserRealm(carol))722	aborts(t, "nothing revealed", func() { Reveal(cross(cur), itoa(id)+":2:"+s2) })723	if p.Shots[1].Resolved {724		t.Fatal("out of order")725	}726	// both in one call, in order; a hit on the first closes the pool and voids the second as closed727	fund(0)728	testing.SetRealm(testing.NewUserRealm(carol))729	Reveal(cross(cur), itoa(id)+":1:"+s1+","+itoa(id)+":2:"+s2)730	if !p.Shots[0].Resolved || !p.Shots[1].Resolved || p.ShotEscrow != 0 {731		t.Fatalf("both: %+v %+v", *p.Shots[0], *p.Shots[1])732	}733	if p.Shots[0].Won && (!p.Shots[1].Void || p.Voided != 1) {734		t.Fatalf("a shot after the pop is void: %+v", *p.Shots[1])735	}736	if !p.Shots[0].Won && p.Shots[1].Void {737		t.Fatalf("the second should have been drawn: %+v", *p.Shots[1])738	}739	conserved(t)740	// and made sure of: a first shot that cannot miss voids the second as closed, in the same call741	reset()742	keeperUp(cur, t, secret(1), secret(2))743	id, p = shotPool(cur, t, 1_000_000, true)744	fund(1_732_051 + 1_000_000)745	testing.SetRealm(testing.NewUserRealm(bob))746	Bid(cross(cur), id, "", "", 1_000_000)747	p.Shots[0].Bps = 10000748	testing.SkipHeights(ShotDelay)749	pot0 := p.Pot750	fund(0)751	testing.SetRealm(testing.NewUserRealm(carol))752	Reveal(cross(cur), itoa(id)+":1:"+s1+","+itoa(id)+":2:"+s2)753	if !p.Popped || !p.Shots[1].Void || p.Voided != 1 || p.ShotEscrow != 0 || p.Pot != pot0+2_000_000-RevealTip {754		t.Fatalf("pop then void: %+v %+v pot %d→%d", *p.Shots[0], *p.Shots[1], pot0, p.Pot)755	}756	conserved(t)757}758759func TestVoidsAndLate(cur realm, t *testing.T) {760	// unrevealed past the window: Settle voids it into the pot, no tip, a miss761	reset()762	keeperUp(cur, t, secret(1), secret(2), secret(3))763	id, p := shotPool(cur, t, 1_000_000, true)764	testing.SkipHeights(ShotDelay + ShotWindow + 1)765	pot0, k0 := p.Pot, owed(carol)766	fund(0)767	testing.SetRealm(testing.NewUserRealm(bob))768	Settle(cross(cur), id)769	sh := p.Shots[0]770	if !sh.Resolved || !sh.Void || sh.Won || p.Misses != 1 || p.Voided != 1 || voided != 1 || p.Pot != pot0+1_000_000 || p.ShotEscrow != 0 || owed(carol) != k0 {771		t.Fatalf("void: %+v pot %d→%d", *sh, pot0, p.Pot)772	}773	// a reveal that comes late is a void too, whoever sends it774	fund(1_732_051 + 1_000_000)775	testing.SetRealm(testing.NewUserRealm(alice))776	Bid(cross(cur), id, "", "", 1_000_000)777	testing.SkipHeights(ShotDelay + ShotWindow + 1)778	fund(0)779	testing.SetRealm(testing.NewUserRealm(carol))780	aborts(t, "nothing revealed", func() { Reveal(cross(cur), itoa(id)+":2:"+hex.EncodeToString(secret(2))) }) // on chain the abort reverts its own settling781	fund(0)782	testing.SetRealm(testing.NewUserRealm(carol))783	Settle(cross(cur), id)784	if !p.Shots[1].Void || p.Voided != 2 || p.Misses != 2 || owed(carol) != k0 {785		t.Fatalf("late reveal: %+v", *p.Shots[1])786	}787	// the last block of the window still draws, and counts as late788	fund(2_000_000 + 1_000_000)789	testing.SetRealm(testing.NewUserRealm(alice))790	Bid(cross(cur), id, "", "", 1_000_000)791	testing.SkipHeights(ShotDelay + ShotWindow)792	fund(0)793	testing.SetRealm(testing.NewUserRealm(carol))794	Reveal(cross(cur), itoa(id)+":3:"+hex.EncodeToString(secret(3)))795	if !p.Shots[2].Resolved || p.Shots[2].Void || p.Late != 1 || late != 1 || p.Shots[2].DrawH != runtime.ChainHeight() {796		t.Fatalf("window end: %+v late %d", *p.Shots[2], p.Late)797	}798	conserved(t)799800	// a pool that closes with a shot pending: Claim settles it first, into the pot the winner takes801	reset()802	keeperUp(cur, t, secret(4))803	fund(0)804	testing.SetRealm(testing.NewUserRealm(dave))805	id = CreatePool(cross(cur), "closing", 60, 1_000_000, 0)806	p = pool(id)807	fund(1_000_000)808	testing.SetRealm(testing.NewUserRealm(dave))809	Bid(cross(cur), id, "", "", 0)810	// a shot that would be due: planted, since MinClock keeps a real one clear of the close811	p.Shots = append(p.Shots, &Shot{N: 1, Bidder: alice, Paid: 500_000, Take: p.Pot, Bps: 100, CommitH: runtime.ChainHeight(), Hash: commits[0]})812	p.ShotEscrow += 500_000813	testing.IssueCoins(chain.PackageAddress(realmPath), chain.Coins{{"ugnot", 500_000}})814	testing.SkipHeights(13) // the clock ran out815	fund(0)816	testing.SetRealm(testing.NewUserRealm(carol))817	aborts(t, "nothing revealed", func() { Reveal(cross(cur), itoa(id)+":1:"+hex.EncodeToString(secret(4))) }) // the pool closed first818	fund(0)819	testing.SetRealm(testing.NewUserRealm(carol))820	Settle(cross(cur), id)821	if !p.Shots[0].Void || p.ShotEscrow != 0 || p.Pot != 950_000+500_000 { // the creator's 5% left the pot at the bid822		t.Fatalf("closed first: %+v pot %d", *p.Shots[0], p.Pot)823	}824	fund(0)825	testing.SetRealm(testing.NewUserRealm(bob))826	Claim(cross(cur), id)827	if owed(dave) != p.Pot || !p.Paid {828		t.Fatalf("the pot, shot included, is the winner's: owed %d of %d", owed(dave), p.Pot)829	}830	conserved(t)831}832833func TestTakeCountsTeamAndSlide(cur realm, t *testing.T) {834	reset()835	keeperUp(cur, t, secret(1))836	// alice under TV, bob under TV with a small bid, dave flagless: a pop for alice pays her the pot less what has837	// slid; the team's 35% goes to bob, and the take counts it as hers838	fund(0)839	testing.SetRealm(testing.NewUserRealm(dave))840	id := CreatePool(cross(cur), "team", 3600, 1_000_000, 7200)841	p := pool(id)842	fund(1_000_000)843	testing.SetRealm(testing.NewUserRealm(dave))844	Bid(cross(cur), id, "", "", 0)845	fund(1_414_214)846	testing.SetRealm(testing.NewUserRealm(bob))847	Bid(cross(cur), id, "", "TV", 0)848	fund(1_732_051)849	testing.SetRealm(testing.NewUserRealm(alice))850	Bid(cross(cur), id, "", "TV", 0)851	pot := p.Pot852	toWinner, cut := p.payout(alice, pot, false)853	if cut != pot*TeamShare/100 || toWinner != pot-cut || p.Take(alice, pot) != pot {854		t.Fatalf("fresh pool: toWinner %d cut %d take %d of %d", toWinner, cut, p.Take(alice, pot), pot)855	}856	// halfway through the life, half the rest has slid to the roster by paid-in: the take is the pot less the others' part857	testing.SkipHeights(720)858	if p.LiveBps() > 5000 || p.LiveBps() < 4990 {859		t.Fatalf("live %d", p.LiveBps())860	}861	toWinner, cut = p.payout(alice, pot, false)862	rest := pot - cut863	shared := rest - mulDiv(rest, p.LiveBps(), 10000)864	var total, others int64865	for _, m := range p.Roster {866		total += m.Paid867		if m.Addr != alice {868			others += mulDiv(shared, m.Paid, total)869		}870	}871	_ = others872	if p.Take(alice, pot) != toWinner+cut || toWinner >= pot-cut || p.Take(alice, pot) <= pot/2 {873		t.Fatalf("slid: toWinner %d cut %d take %d of %d", toWinner, cut, p.Take(alice, pot), pot)874	}875	// a shooter alone on the roster takes everything876	reset()877	keeperUp(cur, t, secret(1))878	fund(0)879	testing.SetRealm(testing.NewUserRealm(dave))880	id = CreatePool(cross(cur), "alone", 3600, 1_000_000, 0)881	p = pool(id)882	fund(1_000_000 + 1_000_000)883	testing.SetRealm(testing.NewUserRealm(alice))884	Bid(cross(cur), id, "", "", 1_000_000)885	if len(p.Shots) != 1 || p.Shots[0].Take != p.Pot+1_000_000-RevealTip {886		t.Fatalf("alone: %+v pot %d", *p.Shots[0], p.Pot)887	}888	conserved(t)889}890891func TestRender(cur realm, t *testing.T) {892	reset()893	keeperUp(cur, t, secret(1))894	fund(0)895	testing.SetRealm(testing.NewUserRealm(dave))896	id := CreatePool(cross(cur), "r", 3600, 1_000_000, 0)897	fund(1_000_000)898	testing.SetRealm(testing.NewUserRealm(alice))899	Bid(cross(cur), id, "", "FR", 0)900	fund(1_414_214 + 200_000)901	testing.SetRealm(testing.NewUserRealm(bob))902	Bid(cross(cur), id, alice.String(), "US", 200_000)903	all := Render("json")904	for _, want := range []string{"\"split\":{\"pot\":55,\"div\":30,\"ref\":10,\"creator\":5,\"team\":35,\"roster\":100,\"shotMaxBps\":100,\"shotFair\":8000,\"shotDelay\":3,\"shotWindow\":2,\"minClock\":300,\"minShot\":100000,\"revealTip\":20000,\"keeperFresh\":300,\"liveFloor\":2500}", "\"shotsOn\":true", "\"keeper\":\"" + carol.String() + "\"", "\"keeperSeen\":", "\"commitsFree\":0,\"commitHead\":\"\",\"voided\":0,\"late\":0", "\"bidders\":2", "\"flag\":\"US\"", "\"liveBps\":", "\"lastDay\":", "\"shots\":1,\"misses\":0,\"voided\":0,\"late\":0,\"pending\":1,\"escrow\":200000", "\"height\":"} {905		if !strings.Contains(all, want) {906			t.Errorf("json missing %s in %s", want, all)907		}908	}909	one := Render("json/1")910	for _, want := range []string{"\"shot\":200000", "\"shotlog\":[{\"n\":1", "\"commit\":\"" + hashOf(secret(1)) + "\"", "\"revealAt\":", "\"revealBy\":", "\"hit\":false,\"void\":false", "\"take\":"} {911		if !strings.Contains(one, want) {912			t.Errorf("detail missing %s in %s", want, one)913		}914	}915	me := Render("me/" + alice.String())916	if !strings.Contains(me, "\"owed\":424264") || !strings.Contains(me, "\"withdrawn\":0") {917		t.Errorf("me: %s", me)918	}919	if !strings.Contains(Render(""), "Bubble Rumble v4") || !strings.Contains(Render("1"), "a 0.2 GNOT shot") || !strings.Contains(Render("1"), "unrevealed: 0") {920		t.Errorf("markdown: %s", Render("1"))921	}922	if !strings.Contains(Render("commits"), "\"commits\":[]") {923		t.Errorf("commits: %s", Render("commits"))924	}925}926

Functions

  • Bid(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}, id int64, ref string, flag string, shot int64)

  • Cancel(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}, id 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}, id int64)

  • ClockFor(window int64, price int64, sent int64) int64

  • Commit(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}, hashes string)

  • CreatePool(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}, name string, window int64, basePrice int64, life int64) int64

  • GNOT(ugnot int64) string

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

  • Owed(a string) int64

  • Price(base int64, n int64) int64

  • Render(path string) string

  • Retire(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 int64)

  • Reveal(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}, reveals string)

  • SetKeeper(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}, addr string)

  • SetShots(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}, on bool)

  • Settle(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}, id int64)

  • ShotBps(shot int64, take int64) 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}) int64

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

Rendered

RenderedRawgnoweb ↗
,
28
,
29
,
30
,
31
} }
31
,
32
}))

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.