From a0ecf9ecddbaa7dc5d15f0b9d23babedddc5fb61 Mon Sep 17 00:00:00 2001 From: Bart Date: Mon, 9 Sep 2024 14:01:23 +0200 Subject: [PATCH] build a lookup by all possible cadence source codes for each network --- flix/v1_1/indexer.go | 47 +++++++++++++++-------- flix/v1_1/template.go | 87 +++++++++++++++++++++++++++++++++---------- 2 files changed, 98 insertions(+), 36 deletions(-) diff --git a/flix/v1_1/indexer.go b/flix/v1_1/indexer.go index 721ae43..0ef1ada 100644 --- a/flix/v1_1/indexer.go +++ b/flix/v1_1/indexer.go @@ -7,14 +7,20 @@ import ( ) type TemplateIndexer struct { - store []*Template + // lookupByAstHash stores a lookup from Cadence AST hash to template + // Note: this map is not garbage collectible. + // If we ever need to free memory for unused templates, + //we'd have to use a different approach. + lookupByAstHash map[string]*Template + // templates stores all available (deduplicated) templates + templates []*Template } const templatesDirPath = "./templates/" func NewIndexer() *TemplateIndexer { return &TemplateIndexer{ - store: make([]*Template, 0), + lookupByAstHash: make(map[string]*Template), } } @@ -55,23 +61,37 @@ func (i *TemplateIndexer) SeedFromFs() error { return err } - i.add(template) + err = i.add(template) + if err != nil { + return err + } } } return nil } -func (i *TemplateIndexer) add(template *Template) { - i.store = append(i.store, template) +func (i *TemplateIndexer) add(template *Template) error { + astHashes, err := template.CadenceAstHashes() + if err != nil { + return err + } + + for _, astHash := range astHashes { + i.lookupByAstHash[string(astHash)] = template + } + + i.templates = append(i.templates, template) + + return nil } func (i *TemplateIndexer) List() []*Template { - return i.store + return i.templates } func (i *TemplateIndexer) GetByID(id string) *Template { - for _, template := range i.store { + for _, template := range i.lookupByAstHash { if template.Id == id { return template } @@ -80,14 +100,9 @@ func (i *TemplateIndexer) GetByID(id string) *Template { } func (i *TemplateIndexer) GetBySource(cadenceSource []byte) (*Template, error) { - for _, template := range i.store { - isMatch, err := template.MatchesSource(cadenceSource) - if err != nil { - return nil, err - } - if isMatch { - return template, nil - } + astHash, err := cadenceAstHash(cadenceSource) + if err != nil { + return nil, err } - return nil, nil + return i.lookupByAstHash[string(astHash)], nil } diff --git a/flix/v1_1/template.go b/flix/v1_1/template.go index f1ee77d..83ba7b9 100644 --- a/flix/v1_1/template.go +++ b/flix/v1_1/template.go @@ -1,12 +1,12 @@ package v1_1 import ( - "bytes" "crypto/sha256" "encoding/json" "fmt" "github.com/onflow/cadence/runtime/parser" "github.com/turbolent/prettier" + "regexp" "strings" ) @@ -41,8 +41,8 @@ type dependencies struct { } type contractDependency struct { - Contracts string `json:"contract"` - Networks []contractNetwork `json:"networks"` + Contract string `json:"contract"` + Networks []contractNetwork `json:"networks"` } type contractNetwork struct { @@ -91,30 +91,69 @@ func NewFromJson(rawJson []byte) (*Template, error) { return &parsed, nil } -func (t *Template) CadenceAstHash() ([]byte, error) { - astHash, err := cadenceAstHash([]byte(t.Data.Cadence.Body)) +// CadenceAstHashes returns all Cadence AST hashes that map to the semantically equivalent source code +func (l *Template) CadenceAstHashes() ([][]byte, error) { + supportedNetworkNames := []string{ + "emulator", + "testnet", + "mainnet", + } + var astHashes [][]byte + + // Also include plan source code, without patched imports + astHash, err := cadenceAstHash([]byte(l.Data.Cadence.Body)) if err != nil { return nil, err } - return astHash, nil -} + astHashes = append(astHashes, astHash) -func (t *Template) MatchesSource(source []byte) (bool, error) { - astHash1, err := cadenceAstHash(source) - if err != nil { - return false, err + for _, networkName := range supportedNetworkNames { + sourceCode := l.sourceCodeForNetwork(networkName) + if sourceCode == "" { + // Network not supported for this template + continue + } + + astHash, err := cadenceAstHash([]byte(sourceCode)) + if err != nil { + return nil, err + } + astHashes = append(astHashes, astHash) } - astHash2, err := t.CadenceAstHash() - if err != nil { - return false, err + return astHashes, nil +} + +// sourceCodeForNetwork returns source code for a specific network name. +// Returns an empty string if no source code can be produced for a given network. +func (l *Template) sourceCodeForNetwork(networkName string) string { + sourceCode := l.Data.Cadence.Body + + for _, deps := range l.Data.Dependencies { + for _, dep := range deps.Contracts { + var network *contractNetwork + for _, net := range dep.Networks { + if net.Network == networkName { + network = &net + } + } + + if network == nil { + // Can't build source code for this network + return "" + } + + importPattern := regexp.MustCompile(fmt.Sprintf(`import +"%s"`, dep.Contract)) + replacementImport := fmt.Sprintf("import %s from %s", dep.Contract, prefixedAddress(network.Address)) + sourceCode = string(importPattern.ReplaceAll([]byte(sourceCode), []byte(replacementImport))) + } } - return bytes.Equal(astHash1, astHash2), nil + return sourceCode } -func (t *Template) GetMessage(key, tag string) string { - for _, msg := range t.Data.Messages { +func (l *Template) GetMessage(key, tag string) string { + for _, msg := range l.Data.Messages { if msg.Key == key { for _, msgI18n := range msg.I18n { if msgI18n.Tag == tag { @@ -126,8 +165,8 @@ func (t *Template) GetMessage(key, tag string) string { return "" } -func (t *Template) SetMessage(key, tag, translation string) { - for _, msg := range t.Data.Messages { +func (l *Template) SetMessage(key, tag, translation string) { + for _, msg := range l.Data.Messages { if msg.Key == key { for _, msgI18n := range msg.I18n { if msgI18n.Tag == tag { @@ -138,7 +177,7 @@ func (t *Template) SetMessage(key, tag, translation string) { } } - t.Data.Messages = append(t.Data.Messages, message{ + l.Data.Messages = append(l.Data.Messages, message{ Key: key, I18n: []i18n{ { @@ -149,6 +188,14 @@ func (t *Template) SetMessage(key, tag, translation string) { }) } +func prefixedAddress(address string) string { + if strings.HasPrefix(address, "0x") { + return address + } else { + return "0x" + address + } +} + func cadenceAstHash(source []byte) ([]byte, error) { program, err := parser.ParseProgram(nil, source, parser.Config{}) if err != nil {