gno.land/p/g1t2kg2vtr3fukg43eujkn6x53gfdyakhngt4sfd/gnofly/arena/payout/v0⧉ Package Open in gnoweb ↗
Kind Pure package
Name v0
Namespace g1t2kg2vtr3fukg43eujkn6x53gfdyakhngt4sfd / gnofly / arena / payout
Exported functions n/a — not supported for pure packages by the node (vm/qfuncs)
Module gno.land/p/g1t2kg2vtr3fukg43eujkn6x53gfdyakhngt4sfd/gnofly/arena/payout/v0
gno 0.9 ⧉
1 package payout 2 3 // Both parsers read their input in a single pass, byte by byte, and bound it 4 // before they read it. On chain every interpreted operation is paid for, and 5 // an oversized string must cost its sender nothing but a refusal. 6 // 7 // They only accept an address in its canonical form, see validAddress. not supported for pure packages by the node (vm/qfuncs)
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
8
9 const (
10 // addressLen is the length of a gno address: "g1" and 38 characters.
11 addressLen = 40
12 // maxPlayersLen bounds a players string: MaxPlayers addresses, each
13 // followed by a separator.
14 maxPlayersLen = MaxPlayers * (addressLen + 1 )
15 // maxDeathsLen bounds a deaths string: MaxDeaths entries
16 // of two addresses, each followed by a separator.
17 maxDeathsLen = MaxDeaths * 2 * (addressLen + 1 )
18 )
19
20 // ParsePlayers parses a players string: comma-separated addresses, for
21 // example
22 //
23 // g1aaa...,g1bbb...,g1ccc...
24 //
25 // It returns 1 to MaxPlayers addresses in the order of the string, each one
26 // valid and listed once. Whitespace, empty entries and a trailing separator
27 // are refused. Whether there are enough players for a round is decided by
28 // Rule.Settle, and by the realm that starts the round.
29 func ParsePlayers(s string ) ([]address, error ) {
30 if s == "" {
31 return nil , ErrNoPlayers
32 }
33 if len(s) > maxPlayersLen {
34 return nil , ErrTooManyPlayers
35 }
36 var players []address
37 i := 0
38 for {
39 if len(players) == MaxPlayers {
40 return nil , ErrTooManyPlayers
41 }
42 end := indexFrom(s, i, ',' )
43 addr := address(s[i:end])
44 if !validAddress(addr) {
45 return nil , ErrPlayersFormat
46 }
47 if indexOf(players, addr) >= 0 {
48 return nil , ErrDuplicatePlayer
49 }
50 players = append(players, addr)
51 if end == len(s) {
52 return players, nil
53 }
54 i = end + 1
55 if i == len(s) {
56 return nil , ErrPlayersFormat // a trailing ","
57 }
58 }
59 }
60
61 // ParseDeaths parses a deaths string: "victim:killer" entries
62 // separated by ";", in the order they happened, "victim:" when nobody shot
63 // the victim down, for example
64 //
65 // g1bbb...:g1aaa...;g1ccc...:
66 //
67 // The empty string means nobody died and gives an empty list. At
68 // most MaxDeaths entries are accepted, and every address must be valid.
69 // Whitespace, empty entries and a trailing separator are refused.
70 //
71 // It only checks the syntax. Whether the victims and the killers are players
72 // of the round, and how often they died, is decided by Rule.Settle.
73 func ParseDeaths(s string ) ([]Death, error ) {
74 if s == "" {
75 return nil , nil
76 }
77 if len(s) > maxDeathsLen {
78 return nil , ErrTooManyDeaths
79 }
80 var out []Death
81 i := 0
82 for {
83 if len(out) == MaxDeaths {
84 return nil , ErrTooManyDeaths
85 }
86 colon := indexFrom(s, i, ':' )
87 if colon == len(s) {
88 return nil , ErrDeathsFormat
89 }
90 end := indexFrom(s, colon+ 1 , ';' )
91
92 victim := address(s[i:colon])
93 if !validAddress(victim) {
94 return nil , ErrDeathsFormat
95 }
96 killer := address(s[colon+ 1 : end])
97 if killer != "" && !validAddress(killer) {
98 return nil , ErrDeathsFormat
99 }
100 out = append(out, Death{Victim: victim, Killer: killer})
101 if end == len(s) {
102 return out, nil
103 }
104 i = end + 1
105 if i == len(s) {
106 return nil , ErrDeathsFormat // a trailing ";"
107 }
108 }
109 }
110
111 // validAddress reports whether addr is a valid address in its canonical,
112 // lowercase form.
113 //
114 // Bech32 can also be written in all capitals, and address.IsValid accepts
115 // that. To a realm the capitals would be a second name for the same account,
116 // one that matches no player of a round. Bech32 does not allow mixing cases,
117 // so looking at the first character is enough. The length is checked first:
118 // it is free, and it keeps an overlong string away from the decoder.
119 func validAddress(addr address) bool {
120 return len(addr) == addressLen && addr[ 0 ] == 'g' && addr.IsValid()
121 }
122
123 // indexFrom returns the index of the first c in s at or after i, or len(s)
124 // when there is none.
125 func indexFrom(s string , i int , c byte ) int {
126 for i < len(s) && s[i] != c {
127 i++
128 }
129 return i
130 }
131