Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

ratelimitdemo.gno

3.11 Kb · 88 lines
 1// Package ratelimitdemo is a small on-chain demo of the token-bucket rate
 2// limiter provided by the [p/moul/x/daily/ratelimit](/p/moul/x/daily/ratelimit/v0)
 3// library.
 4//
 5// It holds a single package-level [ratelimit.Limiter] and wires it to the
 6// chain: the tick is runtime.ChainHeight() and each caller is keyed by its
 7// address (unsafe.PreviousRealm().Address()). It contains no limiter logic of
 8// its own — refill/consume math all lives in the library.
 9package ratelimitdemo
10
11import (
12	"strconv"
13
14	"chain/runtime"
15	"chain/runtime/unsafe"
16
17	"gno.land/p/moul/x/daily/ratelimit/v0"
18)
19
20// limiter starts at 0.5 tokens/block (one every two blocks), capacity 5.
21var limiter = ratelimit.New(0.5, 5.0)
22
23// Allow consumes one token for the calling realm/user at the current block
24// height and reports whether the request is permitted. Returns false (and
25// consumes nothing) when the caller's bucket is empty.
26func Allow(cur realm) bool {
27	addr := unsafe.PreviousRealm().Address()
28	return limiter.Allow(string(addr), runtime.ChainHeight())
29}
30
31// SetConfig updates the shared rate (tokens per block) and burst (capacity).
32// rate is clamped to >= 0, burst to >= 1.
33func SetConfig(cur realm, rate, burst float64) {
34	limiter.SetConfig(rate, burst)
35}
36
37// Tokens reports how many whole tokens `addr` has available at the current
38// block height, without mutating any state.
39func Tokens(addr address) int {
40	return limiter.Tokens(string(addr), runtime.ChainHeight())
41}
42
43// --- rendering --------------------------------------------------------------
44
45func ftoa(f float64) string { return strconv.FormatFloat(f, 'g', -1, 64) }
46
47// Render shows the limiter config and a table of known callers with their
48// current tokens and last-seen height. Path "/addr/<address>" focuses one
49// caller.
50func Render(path string) string {
51	now := runtime.ChainHeight()
52	rate, burst := limiter.Config()
53
54	if len(path) > 6 && path[:6] == "/addr/" {
55		key := path[6:]
56		out := "# Rate Limiter — caller\n\n"
57		out += "Address: `" + key + "`\n\n"
58		out += "- Current tokens: **" + strconv.Itoa(limiter.Tokens(key, now)) + "** / " + ftoa(burst) + "\n"
59		out += "- Now (height): " + strconv.FormatInt(now, 10) + "\n"
60		return out
61	}
62
63	out := "# Rate Limiter\n\n"
64	out += "Demo of the [`p/moul/x/daily/ratelimit`](/p/moul/x/daily/ratelimit/v0) library — "
65	out += "an on-chain token-bucket limiter (port of `golang.org/x/time/rate`).\n\n"
66	out += "## Config\n\n"
67	out += "| Parameter | Value |\n|---|---|\n"
68	out += "| Rate (tokens/block) | " + ftoa(rate) + " |\n"
69	out += "| Burst (capacity) | " + ftoa(burst) + " |\n"
70	out += "| Clock height | " + strconv.FormatInt(now, 10) + " |\n\n"
71
72	out += "## Callers\n\n"
73	if limiter.Len() == 0 {
74		out += "_No caller has hit the limiter yet._\n\n"
75	} else {
76		out += "| Address | Tokens | Last height |\n|---|---|---|\n"
77		limiter.Iterate(now, func(key string, tokens int, last int64) bool {
78			out += "| `" + key + "` | " + strconv.Itoa(tokens) +
79				" | " + strconv.FormatInt(last, 10) + " |\n"
80			return false
81		})
82		out += "\n"
83	}
84
85	out += "---\n"
86	out += "Call `Allow(cur)` to consume a token; read `Tokens(addr)` for a caller's balance.\n"
87	return out
88}