Skip to content

Commit

Permalink
build a lookup by all possible cadence source codes for each network
Browse files Browse the repository at this point in the history
  • Loading branch information
bartolomej committed Sep 9, 2024
1 parent 1ece0a8 commit a0ecf9e
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 36 deletions.
47 changes: 31 additions & 16 deletions flix/v1_1/indexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
}

Expand Down Expand Up @@ -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
}
Expand All @@ -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
}
87 changes: 67 additions & 20 deletions flix/v1_1/template.go
Original file line number Diff line number Diff line change
@@ -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"
)

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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{
{
Expand All @@ -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 {
Expand Down

0 comments on commit a0ecf9e

Please sign in to comment.