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

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

ratelimitdemo.gno

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