ulnbase.gno
6.72 Kb · 227 lines
1package ulnbase
2
3import (
4 "gno.land/p/samcrew/deps/onbloc/json"
5 "gno.land/p/nt/ufmt/v0"
6)
7
8// XXX: not sure having the counts will be an opti on gno, need to benchmark
9
10type UlnConfig struct {
11 Confirmations uint64
12 RequiredDVNCount uint8
13 OptionalDVNCount uint8
14 OptionalDVNThreshold uint8
15 RequiredDVNs []address
16 OptionalDVNs []address
17}
18
19type SetDefaultUlnConfigParam struct {
20 Eid uint32
21 Config *UlnConfig
22}
23
24// Error types
25var (
26 ErrULNUnsorted = "LZ_ULN_Unsorted"
27 ErrULNInvalidRequiredDVNCount = "LZ_ULN_InvalidRequiredDVNCount"
28 ErrULNInvalidOptionalDVNCount = "LZ_ULN_InvalidOptionalDVNCount"
29 ErrULNAtLeastOneDVN = "LZ_ULN_AtLeastOneDVN"
30 ErrULNInvalidOptionalDVNThreshold = "LZ_ULN_InvalidOptionalDVNThreshold"
31 ErrULNInvalidConfirmations = "LZ_ULN_InvalidConfirmations"
32 ErrULNUnsupportedEid = "LZ_ULN_UnsupportedEid"
33)
34
35// Constants
36var (
37 DEFAULT_CONFIG = address("")
38 DEFAULT = uint8(0)
39 NIL_DVN_COUNT = ^uint8(0) // max uint8
40 NIL_CONFIRMATIONS = ^uint64(0) // max uint64
41 MAX_COUNT = (^uint8(0) - 1) / 2
42)
43
44type UlnBase struct {
45 ulnConfigs configStore
46}
47
48func (u *UlnBase) SetDefaultUlnConfigs(params []SetDefaultUlnConfigParam) {
49 for _, param := range params {
50 // Must not use NIL values
51 if param.Config.RequiredDVNCount == NIL_DVN_COUNT {
52 panic(ErrULNInvalidRequiredDVNCount)
53 }
54 if param.Config.OptionalDVNCount == NIL_DVN_COUNT {
55 panic(ErrULNInvalidOptionalDVNCount)
56 }
57 if param.Config.Confirmations == NIL_CONFIRMATIONS {
58 panic(ErrULNInvalidConfirmations)
59 }
60
61 // Must have at least one DVN
62 assertAtLeastOneDVN(param.Config)
63
64 u.setConfig(DEFAULT_CONFIG, param.Eid, param.Config)
65 }
66
67 // Emit event
68 // chain.Emit(ufmt.Sprintf("DefaultUlnConfigsSet(%v)", params))
69}
70
71func (u *UlnBase) GetUlnConfig(oapp address, remoteEid uint32) *UlnConfig {
72 defaultConfig, _ := u.ulnConfigs.get(DEFAULT_CONFIG, remoteEid)
73 customConfig, hasCustom := u.ulnConfigs.get(oapp, remoteEid)
74
75 rtnConfig := *defaultConfig
76
77 // Handle confirmations
78 if hasCustom && customConfig.Confirmations == uint64(DEFAULT) {
79 rtnConfig.Confirmations = defaultConfig.Confirmations
80 } else if hasCustom && customConfig.Confirmations != NIL_CONFIRMATIONS {
81 rtnConfig.Confirmations = customConfig.Confirmations
82 }
83
84 // Handle required DVNs
85 if hasCustom && customConfig.RequiredDVNCount == DEFAULT {
86 if defaultConfig.RequiredDVNCount > 0 {
87 rtnConfig.RequiredDVNs = defaultConfig.RequiredDVNs
88 rtnConfig.RequiredDVNCount = defaultConfig.RequiredDVNCount
89 }
90 } else if hasCustom && customConfig.RequiredDVNCount != NIL_DVN_COUNT {
91 rtnConfig.RequiredDVNs = customConfig.RequiredDVNs
92 rtnConfig.RequiredDVNCount = customConfig.RequiredDVNCount
93 }
94
95 // Handle optional DVNs
96 if hasCustom && customConfig.OptionalDVNCount == DEFAULT {
97 if defaultConfig.OptionalDVNCount > 0 {
98 rtnConfig.OptionalDVNs = defaultConfig.OptionalDVNs
99 rtnConfig.OptionalDVNCount = defaultConfig.OptionalDVNCount
100 rtnConfig.OptionalDVNThreshold = defaultConfig.OptionalDVNThreshold
101 }
102 } else if hasCustom && customConfig.OptionalDVNCount != NIL_DVN_COUNT {
103 rtnConfig.OptionalDVNs = customConfig.OptionalDVNs
104 rtnConfig.OptionalDVNCount = customConfig.OptionalDVNCount
105 rtnConfig.OptionalDVNThreshold = customConfig.OptionalDVNThreshold
106 }
107
108 // The final value must have at least one DVN
109 assertAtLeastOneDVN(&rtnConfig)
110
111 return &rtnConfig
112}
113
114func (u *UlnBase) GetAppUlnConfig(oapp address, remoteEid uint32) *UlnConfig {
115 val, _ := u.ulnConfigs.get(oapp, remoteEid)
116 return val
117}
118
119func (u *UlnBase) SetUlnConfig(remoteEid uint32, oapp address, param *UlnConfig) {
120 u.setConfig(oapp, remoteEid, param)
121
122 // Get ULN config again as a catch all to ensure the config is valid
123 u.GetUlnConfig(oapp, remoteEid)
124
125 // Emit event
126 // chain.Emit(ufmt.Sprintf("UlnConfigSet(%s, %d, %v)", oapp, remoteEid, param))
127}
128
129func (u *UlnBase) IsSupportedEid(remoteEid uint32) bool {
130 defaultConfig, exists := u.ulnConfigs.get(DEFAULT_CONFIG, remoteEid)
131 if !exists {
132 return false
133 }
134 return defaultConfig.RequiredDVNCount > 0 || defaultConfig.OptionalDVNThreshold > 0
135}
136
137func (u *UlnBase) AssertSupportedEid(remoteEid uint32) {
138 if !u.IsSupportedEid(remoteEid) {
139 panic(ufmt.Sprintf("%s: %d", ErrULNUnsupportedEid, remoteEid))
140 }
141}
142
143func assertAtLeastOneDVN(config *UlnConfig) {
144 if config.RequiredDVNCount == 0 && config.OptionalDVNThreshold == 0 {
145 panic(ErrULNAtLeastOneDVN)
146 }
147}
148
149func (u *UlnBase) setConfig(oapp address, eid uint32, param *UlnConfig) {
150 // Required DVNs validation
151 if param.RequiredDVNCount == NIL_DVN_COUNT || param.RequiredDVNCount == DEFAULT {
152 if len(param.RequiredDVNs) != 0 {
153 panic(ErrULNInvalidRequiredDVNCount)
154 }
155 } else {
156 if len(param.RequiredDVNs) != int(param.RequiredDVNCount) || param.RequiredDVNCount > MAX_COUNT {
157 panic(ErrULNInvalidRequiredDVNCount)
158 }
159 u.assertNoDuplicates(param.RequiredDVNs)
160 }
161
162 // Optional DVNs validation
163 if param.OptionalDVNCount == NIL_DVN_COUNT || param.OptionalDVNCount == DEFAULT {
164 if len(param.OptionalDVNs) != 0 {
165 panic(ErrULNInvalidOptionalDVNCount)
166 }
167 if param.OptionalDVNThreshold != 0 {
168 panic(ErrULNInvalidOptionalDVNThreshold)
169 }
170 } else {
171 if len(param.OptionalDVNs) != int(param.OptionalDVNCount) || param.OptionalDVNCount > MAX_COUNT {
172 panic(ErrULNInvalidOptionalDVNCount)
173 }
174 if param.OptionalDVNThreshold == 0 || param.OptionalDVNThreshold > param.OptionalDVNCount {
175 panic(ErrULNInvalidOptionalDVNThreshold)
176 }
177 u.assertNoDuplicates(param.OptionalDVNs)
178 }
179 // Store the config
180 u.ulnConfigs.set(oapp, eid, param)
181}
182
183func (u *UlnBase) assertNoDuplicates(dvns []address) {
184 if len(dvns) == 0 {
185 return
186 }
187
188 lastDVN := dvns[0]
189 for i := 1; i < len(dvns); i++ {
190 if dvns[i] <= lastDVN {
191 panic(ErrULNUnsorted)
192 }
193 lastDVN = dvns[i]
194 }
195}
196
197// Render function for the contract
198func (u *UlnBase) Render(path string) string {
199 return "XXX: TODO"
200}
201
202func (ucfg *UlnConfig) MarshalJSONString() string {
203 if ucfg == nil {
204 return "{}"
205 }
206 node := json.ObjectNode("", map[string]*json.Node{
207 "confirmations": json.NumberNode("", float64(ucfg.Confirmations)),
208 "requiredDVNCount": json.NumberNode("", float64(ucfg.RequiredDVNCount)),
209 "optionalDVNCount": json.NumberNode("", float64(ucfg.OptionalDVNCount)),
210 "optionalDVNThreshold": json.NumberNode("", float64(ucfg.OptionalDVNThreshold)),
211 "requiredDVNs": json.ArrayNode("", addressSliceToJSON(ucfg.RequiredDVNs)),
212 "optionalDVNs": json.ArrayNode("", addressSliceToJSON(ucfg.OptionalDVNs)),
213 })
214 res, err := json.Marshal(node)
215 if err != nil {
216 panic(err)
217 }
218 return string(res)
219}
220
221func addressSliceToJSON(s []address) []*json.Node {
222 res := make([]*json.Node, 0, len(s))
223 for _, elem := range s {
224 res = append(res, json.StringNode("", elem.String()))
225 }
226 return res
227}