Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions 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}