-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathmods.go
More file actions
517 lines (435 loc) · 14 KB
/
Copy pathmods.go
File metadata and controls
517 lines (435 loc) · 14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
package tela
import (
"fmt"
"strings"
"github.com/civilware/tela/logger"
"github.com/deroproject/derohe/dvm"
_ "embed"
)
// TELA-MOD structure
type MOD struct {
Name string // The name of the MOD
Tag string // The identifying tag for the MOD
Description string // Description of what the MOD does
FunctionCode func() string // The embedded DVM code
FunctionNames []string // The function names from the DVM code
}
// MODClass structure is used to group similarly functioned MODs
type MODClass struct {
Name string // The name of the MODClass
Tag string // The identifying tag for the MODClass
Rules []MODClassRule // Any rules for the MODClass
}
// MODClassRule structure contains data for the rules used within a MODClass
type MODClassRule struct {
Name string // Name of the rule
Description string // Description of what the rule is enforcing
Verify func([]string, MODClass) error // Verification function checking that the parameters of the rule are met
}
// MODs contains the package's TELA-MOD data structures and access,
// all MODClasses are held in the mods variable and can be separated by its index
type MODs struct {
mods []MOD // MOD data
classes []MODClass // classes hold the MODClass info
index []int // index is the index for the MODClasses: vs, tx...
rules []MODClassRule
}
// Access the package's initialized TELA-MOD data
var Mods MODs
// Tag returns a TELA-MOD tag string by index
func (m *MODs) Tag(i int) (tag string) {
if i < len(m.mods) {
tag = m.mods[i].Tag
}
return
}
// Functions returns the TELA-MOD function code and names for the given tag
func (m *MODs) Functions(tag string) (functionCode string, functionNames []string) {
for _, m := range m.mods {
if m.Tag == tag {
return m.FunctionCode(), m.FunctionNames
}
}
return
}
// GetAllMods returns all TELA-MODs
func (m *MODs) GetAllMods() (mods []MOD) {
mods = m.mods
return
}
// GetAllClasses returns all MODClasses
func (m *MODs) GetAllClasses() (classes []MODClass) {
classes = m.classes
return
}
// GetMod returns a TELA-MOD by its tag string
func (m *MODs) GetMod(tag string) (mod MOD) {
for _, m := range m.mods {
if m.Tag == tag {
mod = m
return
}
}
return
}
// GetClass returns a MODClass from a MOD tag
func (m *MODs) GetClass(modTag string) (class MODClass) {
for _, c := range m.classes {
if strings.HasPrefix(modTag, c.Tag) {
return c
}
}
return
}
// GetRules returns all of the MODClassesRules
func (m *MODs) GetRules() (rules []MODClassRule) {
rules = m.rules
return
}
// Index returns the MODClass index
func (m *MODs) Index() (ci []int) {
ci = m.index
return
}
// Function HowToAddMODs() Uint64
// 10 Create the function set bas file in /TELA-MOD-1/
// 20 Embed the function set bas file below as a new variable
// 30 In initMods() create and append the new MOD in its MODClass's []MOD
// 35 If a new MODClass is required, it can be added similarly to the existing MODClasses
// 50 RETURN 0
// End Function
// // Embed the TELA-MOD smart contract function sets
//go:embed */vs/TELA-MOD-1-VSOO.bas
var TELA_MOD_1_VSOO string
//go:embed */vs/TELA-MOD-1-VSOOIM.bas
var TELA_MOD_1_VSOOIM string
//go:embed */vs/TELA-MOD-1-VSPUBSU.bas
var TELA_MOD_1_VSPUBSU string
//go:embed */vs/TELA-MOD-1-VSPUBOW.bas
var TELA_MOD_1_VSPUBOW string
//go:embed */vs/TELA-MOD-1-VSPUBIM.bas
var TELA_MOD_1_VSPUBIM string
//go:embed */tx/TELA-MOD-1-TXDWA.bas
var TELA_MOD_1_TXDWA string
//go:embed */tx/TELA-MOD-1-TXDWD.bas
var TELA_MOD_1_TXDWD string
//go:embed */tx/TELA-MOD-1-TXTO.bas
var TELA_MOD_1_TXTO string
// Initialize TELA-MOD data
func initMods() {
// Initialize all the package's MODClass rules
{
rules := []MODClassRule{
{
Name: "Single MOD",
Description: "Only one MOD from this MODClass can be used at a time",
Verify: func(tags []string, c MODClass) (err error) {
if hasMultipleClassTags(tags, c.Tag) {
err = fmt.Errorf("conflicting tags for %q MODClass", strings.ToLower(c.Name))
}
return
},
},
{
Name: "Multi MOD",
Description: "Multiple MODs from this MODClass can be used simultaneously",
Verify: func(tags []string, c MODClass) (err error) {
// All MODs are valid so return nil
return
},
},
}
// When new rules are added, any conflict handling will go in verifyRules()
Mods.rules = append(Mods.rules, rules...)
}
// // Variable Store MODClass
{
// Initialize the MODClass
variableStoreMODClass := MODClass{
Name: "Variable store",
Tag: "vs",
Rules: []MODClassRule{Mods.rules[0]},
}
variableStoreMODs := []MOD{
{
Name: fmt.Sprintf("%s %s", variableStoreMODClass.Name, "owner only"),
Tag: variableStoreMODClass.NewTag("oo"),
Description: "Allows the owner of the smart contract to set and delete custom variable stores",
FunctionCode: func() string { return TELA_MOD_1_VSOO },
FunctionNames: []string{"SetVar", "DeleteVar"},
},
{
Name: fmt.Sprintf("%s %s", variableStoreMODClass.Name, "owner only immutable"),
Tag: variableStoreMODClass.NewTag("ooim"),
Description: "Allows the owner of the smart contract to set custom variable stores which cannot be changed or deleted",
FunctionCode: func() string { return TELA_MOD_1_VSOOIM },
FunctionNames: []string{"SetVar"},
},
{
Name: fmt.Sprintf("%s %s", variableStoreMODClass.Name, "public single use"),
Tag: variableStoreMODClass.NewTag("pubsu"),
Description: "Allows all wallets to store variables which the wallet cannot change, the owner of the smart contract can set and delete all variables",
FunctionCode: func() string { return TELA_MOD_1_VSPUBSU },
FunctionNames: []string{"SetVar", "DeleteVar"},
},
{
Name: fmt.Sprintf("%s %s", variableStoreMODClass.Name, "public overwrite"),
Tag: variableStoreMODClass.NewTag("pubow"),
Description: "Allows all wallets to store variables which the wallet can overwrite, the owner of the smart contract can set and delete all variables",
FunctionCode: func() string { return TELA_MOD_1_VSPUBOW },
FunctionNames: []string{"SetVar", "DeleteVar"},
},
{
Name: fmt.Sprintf("%s %s", variableStoreMODClass.Name, "public immutable"),
Tag: variableStoreMODClass.NewTag("pubim"),
Description: "Allows all wallets to store variables which cannot be changed or deleted from the smart contract",
FunctionCode: func() string { return TELA_MOD_1_VSPUBIM },
FunctionNames: []string{"SetVar"},
},
// New MOD for variable store MODClass would go here
}
// Add the variable store MODClass and its MODs
Mods.Add(variableStoreMODClass, variableStoreMODs)
}
// // Transfers MODClass
{
transferMODClass := MODClass{
Name: "Transfers",
Tag: "tx",
Rules: []MODClassRule{Mods.rules[1]},
}
transferMODs := []MOD{
{
Name: "Deposit and withdraw DERO",
Tag: transferMODClass.NewTag("dwd"),
Description: "Stores DERO deposits and owner can withdraw DERO from the smart contract",
FunctionCode: func() string { return TELA_MOD_1_TXDWD },
FunctionNames: []string{"DepositDero", "WithdrawDero"},
},
{
Name: "Deposit and withdraw assets",
Tag: transferMODClass.NewTag("dwa"),
Description: "Stores asset deposits and owner can withdraw tokens from the smart contract",
FunctionCode: func() string { return TELA_MOD_1_TXDWA },
FunctionNames: []string{"DepositAsset", "WithdrawAsset"},
},
{
Name: "Transfer ownership",
Tag: transferMODClass.NewTag("to"),
Description: "Transfer the ownership of the smart contract to another wallet",
FunctionCode: func() string { return TELA_MOD_1_TXTO },
FunctionNames: []string{"TransferOwnership", "ClaimOwnership"},
},
// New MODs for transfers MODClass would go here
}
// Add the transfers MODClass and its MODs
Mods.Add(transferMODClass, transferMODs)
}
// This is checked each Add but we should ensure again there is no conflicts with the initialized MODClasses and MODs
err := Mods.Verify()
if err != nil {
logger.Fatalf("[TELA] MODs: %s\n", err)
}
}
// verifyRules is used to check that the initialized MODClassRules do not conflict with each other
func (mc *MODClass) verifyRules() (err error) {
var hasSingleMODRule bool
for _, r := range mc.Rules {
if r.Name == "Single MOD" {
hasSingleMODRule = true
continue
}
if hasSingleMODRule && r.Name == "Multi MOD" {
err = fmt.Errorf("conflicting rules for MODClass %q", mc.Name)
return
}
}
return
}
// Verify that all MODs present have the appropriate data and that there are no conflicting MODs or MODClasses
func (m *MODs) Verify() (err error) {
var classTags, modTags, modNames []string
for _, c := range m.classes {
err = c.verifyRules()
if err != nil {
return
}
classTags = append(classTags, c.Tag)
}
duplicate, found := hasDuplicateString(classTags)
if found {
err = fmt.Errorf("class tag %q cannot be duplicated", duplicate)
return
}
if len(m.classes) != len(m.index) {
err = fmt.Errorf("invalid MODClass index: %d/%d", len(m.classes), len(m.index))
return
}
for _, mod := range m.mods {
class := m.GetClass(mod.Tag)
if class.Tag == "" {
err = fmt.Errorf("could not get MODClass tag for %q", mod.Tag)
return
}
if mod.FunctionCode == nil {
err = fmt.Errorf("missing function code for %q", mod.Name)
return
}
var sc dvm.SmartContract
sc, _, err = dvm.ParseSmartContract(mod.FunctionCode())
if err != nil {
err = fmt.Errorf("function code for %q is invalid: %s", mod.Name, err)
return
}
if len(mod.FunctionNames) != len(sc.Functions) {
err = fmt.Errorf("missing function names for %q %d/%d", mod.Name, len(mod.FunctionNames), len(sc.Functions))
return
}
modTags = append(modTags, mod.Tag)
modNames = append(modNames, mod.Name)
}
duplicate, found = hasDuplicateString(modTags)
if found {
err = fmt.Errorf("tag %q cannot be duplicated", duplicate)
return
}
duplicate, found = hasDuplicateString(modNames)
if found {
err = fmt.Errorf("name %q cannot be duplicated", duplicate)
return
}
return
}
// Add a new MODClass and its MODs if they are valid
func (m *MODs) Add(class MODClass, mods []MOD) (err error) {
addMod := MODs{
mods: append(mods, m.mods...),
classes: append([]MODClass{class}, m.classes...),
index: append(m.index, len(mods)+len(m.mods)),
}
err = addMod.Verify()
if err != nil {
return
}
// Add the new MODClass
m.classes = append(m.classes, class)
// Add the new MODs
m.mods = append(m.mods, mods...)
// Complete the MODClass by storing its index
m.index = append(m.index, len(m.mods))
return
}
// NewModTag returns a modTag string from the given tag elements
func NewModTag(tags []string) (modTag string) {
modTag = strings.Join(tags, ",")
return
}
// NewTag returns a MOD tag prefixed with the MODClass tag
func (mc *MODClass) NewTag(tag string) string {
return fmt.Sprintf("%s%s", mc.Tag, tag)
}
// Check if any duplicate elements exists
func hasDuplicateString(check []string) (duplicate string, found bool) {
have := map[string]bool{}
for _, ele := range check {
if have[ele] {
found = true
duplicate = ele
return
}
have[ele] = true
}
return
}
// Check tag prefixes ensuring that only one tag from the MODClass is used within the tags
func hasMultipleClassTags(tags []string, prefix string) bool {
have := 0
for _, tag := range tags {
if strings.HasPrefix(tag, prefix) {
have++
}
if have > 1 {
return true
}
}
return false
}
// ModTagsAreValid parses a modTag string formatted as "tag,tag,tag" returning its tags if all tags are valid
// and there is no conflicting tags. If an empty modTag is passed it will return no error and nil tags
func (m *MODs) TagsAreValid(modTag string) (tags []string, err error) {
checkTags := strings.Split(modTag, ",")
if checkTags[0] == "" {
return
}
duplicate, found := hasDuplicateString(checkTags)
if found {
err = fmt.Errorf("found duplicate tag %q", duplicate)
return
}
var modExists bool
for _, tag := range checkTags {
modExists = false
for _, m := range m.mods {
if m.Tag == tag {
modExists = true
break
}
}
if !modExists {
err = fmt.Errorf("tag %q does not exist", tag)
return
}
}
// Go through all MODClasses ensuring that the tags abide by MODClassRules
for _, c := range m.classes {
for _, r := range c.Rules {
err = r.Verify(checkTags, c)
if err != nil {
return
}
}
}
tags = checkTags
return
}
// injectMOD injects a TELA-MOD's functions into the code of a smart contract and returns the new smart contract and new code
func (m *MODs) injectMOD(mod, code string) (modSC dvm.SmartContract, modCode string, err error) {
modSC, _, err = dvm.ParseSmartContract(code)
if err != nil {
err = fmt.Errorf("could not parse MOD base code: %s", err)
return
}
// Find the MOD code to be injected
modCodeSet, functionNames := m.Functions(mod)
if modCodeSet == "" {
err = fmt.Errorf("could not find MOD %q code: %s", mod, err)
return
}
sc, _, err := dvm.ParseSmartContract(modCodeSet)
if err != nil {
err = fmt.Errorf("could not parse MOD %q code: %s", mod, err)
return
}
// Inject the new MOD functions into the smart contract
for _, name := range functionNames {
modSC.Functions[name] = sc.Functions[name]
}
modCode = fmt.Sprintf("%s\n%s", code, modCodeSet)
return
}
// InjectMODs parses the modTag ensuring all tags are valid before injecting the TELA-MOD code for the given MODs in the modTag
func (m *MODs) InjectMODs(modTag, code string) (modSC dvm.SmartContract, modCode string, err error) {
tags, err := m.TagsAreValid(modTag)
if err != nil {
err = fmt.Errorf("modTag has invalid MODs: %s", err)
return
}
modCode = code // tags could be nil otherwise start injecting mods
for _, mod := range tags {
_, modCode, err = m.injectMOD(mod, modCode)
if err != nil {
return
}
}
return
}