PathrockNetwork Gno Explorer
HomeBlocksTransactionsRealmsPackagesValidatorsAnalytics

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/p/moul/x/daily/pullpayment/v0

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v0
Namespace
moul / x / daily / pullpayment
Files
3 (README)(gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/moul/x/daily/pullpayment/v0
gno
0.9

Files (3)

  • README.mdmarkdown
  • gnomod.tomltoml
  • pullpayment.gnogno
pullpayment.gnogno
1// Package pullpayment is the escrow ledger behind the pull-payment pattern, as2// a pure, reusable package.3//4// The pattern is the classic Solidity answer to reentrancy: never push value to5// an address, credit it and let the recipient withdraw. A push sends control to6// the recipient in the middle of your state transition, and a malicious7// recipient re-enters before you have finished updating. Pull inverts that —8// the recipient calls in, and their own withdrawal is the only state being9// touched.10//11// This package is the BOOKKEEPING half only: who is owed what, and the12// checks-effects-interactions ordering that makes a withdrawal safe. It moves13// no coins. The realm that holds the funds performs the transfer AFTER calling14// Withdraw, which is exactly the ordering the pattern demands — the balance is15// already zeroed when the transfer happens, so a reentrant call finds nothing16// left to take.17//18// Iteration is over sorted addresses, never a built-in map range: gno map19// iteration order is unspecified and a Render built from one can differ between20// nodes, which is a consensus bug rather than a cosmetic one.21//22// A live demo of this package is at23// [r/moul/x/daily/pullpaymentdemo](/r/moul/x/daily/pullpaymentdemo/v0).24package pullpayment2526import (27	"errors"28	"sort"29)3031// MaxPayees bounds the ledger so gas stays predictable.32const MaxPayees = 40963334var (35	ErrBadAmount = errors.New("pullpayment: amount must be positive")36	ErrFull      = errors.New("pullpayment: too many payees")37	ErrNothing   = errors.New("pullpayment: nothing to withdraw")38	ErrOverflow  = errors.New("pullpayment: credit would overflow")39)4041const maxInt64 = int64(9223372036854775807)4243// Ledger records what each address is owed.44type Ledger struct {45	owed      map[string]int6446	total     int6447	withdrawn int6448}4950// New returns an empty Ledger.51func New() *Ledger { return &Ledger{owed: map[string]int64{}} }5253// Credit records that payee is owed amount more. Amounts accumulate: crediting54// twice owes the sum.55func (l *Ledger) Credit(payee string, amount int64) error {56	if amount <= 0 {57		return ErrBadAmount58	}59	cur, seen := l.owed[payee]60	if !seen && len(l.owed) >= MaxPayees {61		return ErrFull62	}63	if cur > maxInt64-amount || l.total > maxInt64-amount {64		return ErrOverflow65	}66	l.owed[payee] = cur + amount67	l.total += amount68	return nil69}7071// Balance returns what payee is currently owed; zero when nothing.72func (l *Ledger) Balance(payee string) int64 { return l.owed[payee] }7374// Withdraw zeroes payee's balance and returns what was owed.75//76// The caller transfers the returned amount AFTER this call. That ordering is77// the point of the pattern: the credit is already gone from the ledger when78// control passes to the recipient, so a reentrant Withdraw returns ErrNothing.79func (l *Ledger) Withdraw(payee string) (int64, error) {80	amount, ok := l.owed[payee]81	if !ok || amount == 0 {82		return 0, ErrNothing83	}84	delete(l.owed, payee) // effects before interactions85	l.total -= amount86	l.withdrawn += amount87	return amount, nil88}8990// Forfeit drops a payee's credit without paying it, returning what was dropped.91func (l *Ledger) Forfeit(payee string) (int64, error) {92	amount, ok := l.owed[payee]93	if !ok || amount == 0 {94		return 0, ErrNothing95	}96	delete(l.owed, payee)97	l.total -= amount98	return amount, nil99}100101// TotalOwed returns the sum of every outstanding balance — what the holding102// realm must keep in reserve.103func (l *Ledger) TotalOwed() int64 { return l.total }104105// TotalWithdrawn returns the lifetime sum of successful withdrawals.106func (l *Ledger) TotalWithdrawn() int64 { return l.withdrawn }107108// Payees returns every address with an outstanding balance, sorted.109func (l *Ledger) Payees() []string {110	out := make([]string, 0, len(l.owed))111	for p := range l.owed {112		out = append(out, p)113	}114	sort.Strings(out)115	return out116}117118// Count returns how many payees are owed something.119func (l *Ledger) Count() int { return len(l.owed) }120121// IsEmpty reports whether nothing is owed to anyone.122func (l *Ledger) IsEmpty() bool { return len(l.owed) == 0 }123124// Iterate calls fn for each payee in sorted order. Returning true stops.125func (l *Ledger) Iterate(fn func(payee string, amount int64) bool) {126	for _, p := range l.Payees() {127		if fn(p, l.owed[p]) {128			return129		}130	}131}132133// CreditMany credits several payees, applying nothing unless every entry is134// valid — a partial split would leave the ledger disagreeing with the funds.135func (l *Ledger) CreditMany(payees []string, amounts []int64) error {136	if len(payees) != len(amounts) {137		return ErrBadAmount138	}139	// Validate first.140	for _, a := range amounts {141		if a <= 0 {142			return ErrBadAmount143		}144	}145	probe := len(l.owed)146	for _, p := range payees {147		if _, seen := l.owed[p]; !seen {148			probe++149		}150	}151	if probe > MaxPayees {152		return ErrFull153	}154	var sum int64155	for _, a := range amounts {156		if sum > maxInt64-a {157			return ErrOverflow158		}159		sum += a160	}161	if l.total > maxInt64-sum {162		return ErrOverflow163	}164	// Then apply.165	for i, p := range payees {166		l.owed[p] += amounts[i]167	}168	l.total += sum169	return nil170}171172// Consistent reports whether TotalOwed equals the sum of the balances. Always173// true through the public API; exported so callers can assert the invariant.174func (l *Ledger) Consistent() bool {175	var sum int64176	for _, a := range l.owed {177		sum += a178	}179	return sum == l.total180}181

Functions

not supported for pure packages by the node (vm/qfuncs)

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