diff --git a/go.mod b/go.mod index 33f97477b7..d87995686c 100644 --- a/go.mod +++ b/go.mod @@ -11,11 +11,11 @@ require ( github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 github.com/onflow/atree v0.1.0-beta1.0.20211008192952-6f3786263f8a github.com/rivo/uniseg v0.2.0 + github.com/schollz/progressbar/v3 v3.8.3 github.com/stretchr/testify v1.7.0 go.uber.org/goleak v1.1.10 - golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a + golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect - golang.org/x/sys v0.0.0-20210223095934-7937bea0104d // indirect golang.org/x/text v0.3.6 golang.org/x/tools v0.0.0-20200828161849-5deb26317202 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect diff --git a/go.sum b/go.sum index c2331422f0..83f2de24c0 100644 --- a/go.sum +++ b/go.sum @@ -4,14 +4,16 @@ github.com/c-bata/go-prompt v0.2.5 h1:3zg6PecEywxNn0xiqcXHD96fkbxghD+gdB2tbsYfl+ github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fxamacker/cbor/v2 v2.2.1-0.20210927235116-3d6d5d1de29b h1:85oJb8jRevEXzzY3jtDas1Y5qw9iqsbOhdc5lH86vHs= github.com/fxamacker/cbor/v2 v2.2.1-0.20210927235116-3d6d5d1de29b/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/fxamacker/circlehash v0.0.2 h1:69lo0MXfjBh4bHOtklI0rXTXj9FOiP949lW1YSWCNQE= github.com/fxamacker/circlehash v0.0.2/go.mod h1:3aq3OfVvsWtkWMb6A1owjOQFA+TLsD5FgJflnaQwtMM= github.com/go-test/deep v1.0.5 h1:AKODKU3pDH1RzZzm6YZu77YWtEAq6uh1rLIAQlay2qc= github.com/go-test/deep v1.0.5/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= +github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -24,13 +26,17 @@ github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3 github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-tty v0.0.3 h1:5OfyWorkyO7xP52Mq7tB36ajHDG5OHrmBGIS/DtakQI= github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/onflow/atree v0.1.0-beta1.0.20211008192952-6f3786263f8a h1:iNsmJ+ILuZvqJGuWHYxH3DN4BnzfDGZLbUbH55aGW+Q= github.com/onflow/atree v0.1.0-beta1.0.20211008192952-6f3786263f8a/go.mod h1:aVPT7+rB8nnfFRrlxOJl51s+v5NUrevt0+3S+viDbew= github.com/pkg/term v1.1.0 h1:xIAAdCMh3QIAy+5FrE8Ad8XoDhEU4ufwbaSozViP9kk= @@ -39,8 +45,11 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/schollz/progressbar/v3 v3.8.3 h1:FnLGl3ewlDUP+YdSwveXBaXs053Mem/du+wr7XSYKl8= +github.com/schollz/progressbar/v3 v3.8.3/go.mod h1:pWnVCjSBZsT2X3nx9HfRdnCDrpbevliMeoEVhStwHko= github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -58,8 +67,8 @@ go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -86,9 +95,13 @@ golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201014080544-cc95f250f6bc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210223095934-7937bea0104d h1:u0GOGnBJ3EKE/tNqREhhGiCzE9jFXydDo2lf7hOwGuc= -golang.org/x/sys v0.0.0-20210223095934-7937bea0104d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 h1:xrCZDmdtoloIiooiA9q0OQb9r8HejIHYoHGhGCe1pGg= +golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= diff --git a/runtime/cmd/decode-state-values/main.go b/runtime/cmd/decode-state-values/main.go index 1a3f4f2beb..c64c98c22e 100644 --- a/runtime/cmd/decode-state-values/main.go +++ b/runtime/cmd/decode-state-values/main.go @@ -20,186 +20,410 @@ package main -// -//import ( -// "bufio" -// "compress/gzip" -// "encoding/hex" -// "encoding/json" -// "flag" -// "io" -// "log" -// "os" -// "runtime" -// "sync" -// "sync/atomic" -// -// "github.com/schollz/progressbar/v3" -// -// "github.com/onflow/cadence/runtime/common" -// "github.com/onflow/cadence/runtime/interpreter" -//) -// -//var roundtripFlag = flag.Bool("roundtrip", false, "encode and decode the decoded value and ensure equality") -//var gzipFlag = flag.Bool("gzip", false, "set true if input file is gzipped") -// -//type keyPart struct { -// Value string -//} -// -//type key struct { -// KeyParts []keyPart -//} -// -//type entry struct { -// Value string -// Key key -//} -// -//func worker(jobs <-chan entry, wg *sync.WaitGroup, decoded *uint64) { -// defer wg.Done() -// -// var err error -// var data []byte -// -// for e := range jobs { -// -// data, err = hex.DecodeString(e.Value) -// if err != nil { -// log.Fatal(err) -// } -// -// var version uint16 -// data, version = interpreter.StripMagic(data) -// if version == 0 { -// continue -// } -// -// rawOwner, err := hex.DecodeString(e.Key.KeyParts[1].Value) -// if err != nil { -// log.Fatal(err) -// } -// -// owner := common.BytesToAddress(rawOwner) -// -// decodeFunction := interpreter.DecodeValue -// if version <= 4 { -// decodeFunction = interpreter.DecodeValueV4 -// } -// -// var value interpreter.Value -// value, err = decodeFunction(data, &owner, nil, version, nil) -// if err != nil { -// log.Fatalf("failed to decode value: %s\n%s\n", err, e.Value) -// } -// -// atomic.AddUint64(decoded, 1) -// -// if *roundtripFlag { -// reEncodeDecode(value, owner) -// } -// } -//} -// -//func reEncodeDecode(value interpreter.Value, owner common.Address) { -// data, deferrals, err := interpreter.EncodeValue(value, nil, true, nil) -// if err != nil { -// log.Fatalf("failed to encode value: %s\n%s\n", err, value) -// } -// -// if len(deferrals.Values) > 0 { -// log.Fatalf("re-encoding produced deferred values:\n%s\n", value) -// } -// -// if len(deferrals.Moves) > 0 { -// log.Fatalf("re-encoding produced deferred moves:\n%s\n", value) -// } -// -// newValue, err := interpreter.DecodeValue(data, &owner, nil, interpreter.CurrentEncodingVersion, nil) -// if err != nil { -// log.Fatalf("failed to decode re-encoded value: %s\n%s\n", err, value) -// } -// -// equatableValue, ok := value.(interpreter.EquatableValue) -// if !ok { -// log.Fatalf("cannot compare unequatable %[1]T\n%[1]s\n", value) -// } -// -// if !equatableValue.Equal(newValue, interpreter.ReturnEmptyLocationRange) { -// log.Fatalf("values are unequal:\n%s\n%s\n", value, newValue) -// } -//} -// -//func main() { -// flag.Parse() -// -// args := flag.Args() -// if len(args) < 1 { -// panic("missing path argument") -// } -// -// file, err := os.Open(args[0]) -// if err != nil { -// log.Fatal(err) -// } -// defer file.Close() -// -// jobs := make(chan entry) -// -// var decoded uint64 -// -// var wg sync.WaitGroup -// -// workerCount := runtime.NumCPU() -// -// for i := 0; i < workerCount; i++ { -// wg.Add(1) -// go worker(jobs, &wg, &decoded) -// } -// -// stat, err := file.Stat() -// if err != nil { -// log.Fatal(err) -// } -// -// fileSize := stat.Size() -// -// bar := progressbar.DefaultBytes(fileSize, "(processed JSON bytes)") -// -// progressReader := progressbar.NewReader(file, bar) -// -// var inputReader io.Reader = &progressReader -// if *gzipFlag { -// gzipReader, err := gzip.NewReader(inputReader) -// if err != nil { -// log.Fatal(err) -// } -// defer gzipReader.Close() -// inputReader = gzipReader -// } -// -// reader := bufio.NewReader(inputReader) -// -// decoder := json.NewDecoder(reader) -// for { -// var e entry -// -// err = decoder.Decode(&e) -// if err != nil { -// if err == io.EOF { -// break -// } -// log.Fatal(err) -// } -// -// jobs <- e -// } -// -// close(jobs) -// -// wg.Wait() -// -// println() -// -// log.Printf("successfully decoded %d values\n", decoded) -//} +import ( + "bufio" + "bytes" + "compress/gzip" + "encoding/hex" + "encoding/json" + "flag" + "io" + "log" + "os" + goRuntime "runtime" + "strings" + + "github.com/onflow/atree" + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/interpreter" + "github.com/schollz/progressbar/v3" +) + +type stringSlice []string + +func (s stringSlice) String() string { + return strings.Join(s, ", ") +} + +func (s *stringSlice) Set(v string) error { + *s = append(*s, v) + return nil +} + +var addressesFlag stringSlice + +func init() { + flag.Var(&addressesFlag, "addresses", "only keep ledger keys for given addresses") +} + +var gzipFlag = flag.Bool("gzip", false, "set true if input file is gzipped") +var printFlag = flag.Bool("print", false, "print parsed data (filtered, if addresses are given)") +var loadFlag = flag.Bool("load", false, "load the parsed data") + +const keyPartCount = 3 + +type storageKey [keyPartCount]string + +var storage = map[storageKey][]byte{} + +var storagePathSeparator = "\x1f" + +// '$' + 8 byte index +const slabKeyLength = 9 + +func decodeSlab(id atree.StorageID, data []byte) (atree.Slab, error) { + return atree.DecodeSlab( + id, + data, + interpreter.CBORDecMode, + interpreter.DecodeStorable, + interpreter.DecodeTypeInfo, + ) +} + +func storageIDStorageKey(id atree.StorageID) storageKey { + return storageKey{ + string(id.Address[:]), + "", + "$" + string(id.Index[:]), + } +} + +// slabStorage + +type slabStorage struct{} + +var _ atree.SlabStorage = &slabStorage{} + +func (s *slabStorage) Retrieve(id atree.StorageID) (atree.Slab, bool, error) { + data, ok := storage[storageIDStorageKey(id)] + if !ok { + return nil, false, nil + } + + slab, err := decodeSlab(id, data) + if err != nil { + return nil, true, err + } + + return slab, true, nil +} + +func (s *slabStorage) Store(id atree.StorageID, slab atree.Slab) error { + panic("unexpected Store call") +} + +func (s *slabStorage) Remove(_ atree.StorageID) error { + panic("unexpected Remove call") +} + +func (s *slabStorage) GenerateStorageID(_ atree.Address) (atree.StorageID, error) { + panic("unexpected GenerateStorageID call") +} + +func (s *slabStorage) Count() int { + return len(storage) +} + +// interpreterStorage + +type interpreterStorage struct { + *slabStorage +} + +var _ interpreter.Storage = &interpreterStorage{} + +func (i interpreterStorage) ValueExists(_ *interpreter.Interpreter, _ common.Address, key string) bool { + panic("unexpected ValueExists call") +} + +func (i interpreterStorage) ReadValue(_ *interpreter.Interpreter, _ common.Address, _ string) interpreter.OptionalValue { + panic("unexpected ReadValue call") +} + +func (i interpreterStorage) WriteValue(_ *interpreter.Interpreter, _ common.Address, _ string, _ interpreter.OptionalValue) { + panic("unexpected WriteValue call") +} + +// load + +func load() { + log.Println("Loading decoded values ...") + + slabStorage := &slabStorage{} + + interpreterStorage := &interpreterStorage{ + slabStorage: slabStorage, + } + + inter, err := interpreter.NewInterpreter( + nil, + nil, + interpreter.WithStorage(interpreterStorage), + ) + if err != nil { + log.Fatalf("Failed to create interpreter: %s", err) + } + + bar := progressbar.Default(int64(len(storage))) + + for storageKey, data := range storage { + bar.Add(1) + + var isStoragePath bool + + // Check the key is a non-root slab or a storage path + key := storageKey[2] + + var address atree.Address + copy(address[:], storageKey[0]) + + // If the key is for a slab (format '$' + storage index), + // then attempt to decode the slab + + isSlab := len(key) == slabKeyLength && key[0] == '$' + if isSlab { + + var storageIndex atree.StorageIndex + // Skip '$' prefix + copy(storageIndex[:], key[1:]) + + storageID := atree.StorageID{ + Address: address, + Index: storageIndex, + } + + _, err := decodeSlab(storageID, data) + if err != nil { + log.Printf( + "Failed to decode slab @ 0x%x %s: %s (size: %d)", + address, + storageID.Index, + err, + len(data), + ) + continue + } + } else { + // If the key is an account path, + // decode the storable, and load the value + + keyParts := strings.SplitN(key, storagePathSeparator, 2) + + isStoragePath = len(keyParts) == 2 && + common.PathDomainFromIdentifier(keyParts[0]) != common.PathDomainUnknown + + if isStoragePath { + + reader := bytes.NewReader(data) + decoder := interpreter.CBORDecMode.NewStreamDecoder(reader) + storable, err := interpreter.DecodeStorable(decoder, atree.StorageIDUndefined) + if err != nil { + log.Printf( + "Failed to decode storable @ 0x%x %s: %s (data: %x)", + address, key, err, data, + ) + continue + } + + atreeValue, err := storable.StoredValue(slabStorage) + if err != nil { + log.Printf( + "Failed to load stored value @ 0x%x %s: %s", + address, key, err, + ) + continue + } + + value, err := interpreter.ConvertStoredValue(atreeValue) + if err != nil { + log.Printf( + "Failed to convert stored value @ 0x%x %s: %s", + address, key, err, + ) + continue + } + + interpreter.InspectValue( + value, + func(v interpreter.Value) bool { + + if composite, ok := v.(*interpreter.CompositeValue); ok && + composite.Kind == common.CompositeKindResource { + + uuid := composite.GetField(inter, interpreter.ReturnEmptyLocationRange, "uuid") + if _, ok := uuid.(interpreter.UInt64Value); !ok { + log.Printf( + "Failed to get UUID for resource @ 0x%x %s", + address, key, + ) + } + } + + return true + }, + ) + + goRuntime.GC() + } + } + } +} + +type encodedKeyPart struct { + Value string +} + +type encodedKey struct { + KeyParts []encodedKeyPart +} + +type encodedEntry struct { + Value string + Key encodedKey +} + +func main() { + flag.Parse() + + args := flag.Args() + if len(args) < 1 { + panic("missing path argument") + } + + var addresses []common.Address + + for _, hexAddress := range addressesFlag { + address, err := common.HexToAddress(hexAddress) + if err != nil { + log.Fatalf("Invalid address: %s", hexAddress) + } + addresses = append(addresses, address) + } + + file, err := os.Open(args[0]) + if err != nil { + log.Fatal(err) + } + defer file.Close() + + read(file, addresses) + + if *loadFlag { + load() + } + + if *printFlag { + for key, value := range storage { + var keyParts []encodedKeyPart + + for _, keyPart := range key { + keyParts = append(keyParts, encodedKeyPart{ + Value: hex.EncodeToString([]byte(keyPart)), + }) + } + + entry := encodedEntry{ + Value: hex.EncodeToString(value), + Key: encodedKey{ + KeyParts: keyParts, + }, + } + + encoded, err := json.Marshal(entry) + if err != nil { + log.Fatal(err) + } + println(encoded) + } + } +} + +func read(file *os.File, addresses []common.Address) { + + log.Println("Reading file ...") + + filter := len(addresses) > 0 + + stat, err := file.Stat() + if err != nil { + log.Fatal(err) + } + + fileSize := stat.Size() + + bar := progressbar.DefaultBytes(fileSize, "(processed JSON bytes)") + + progressReader := progressbar.NewReader(file, bar) + defer progressReader.Close() + + var inputReader io.Reader = &progressReader + if *gzipFlag { + gzipReader, err := gzip.NewReader(inputReader) + if err != nil { + log.Fatal(err) + } + defer gzipReader.Close() + inputReader = gzipReader + } + + reader := bufio.NewReader(inputReader) + + decoder := json.NewDecoder(reader) + +payloadLoop: + for { + var e encodedEntry + + err = decoder.Decode(&e) + if err != nil { + if err == io.EOF { + break + } + log.Fatal(err) + } + + if len(e.Key.KeyParts) < keyPartCount { + log.Fatalf("Invalid storage key parts: %#+v\n", e.Key) + } + + var storageKey [keyPartCount]string + for i := 0; i < keyPartCount; i++ { + keyPart := e.Key.KeyParts[i].Value + k, err := hex.DecodeString(keyPart) + if err != nil { + log.Fatalf( + "Failed to hex-decode key part %d of %s (%s): %s", + i, e.Key, keyPart, err, + ) + } + // Treat bytes as string, + // so resulting array of strings can be used as a map key + storageKey[i] = string(k) + } + + if filter { + owner := common.BytesToAddress([]byte(storageKey[0])) + var found bool + for _, address := range addresses { + if owner == address { + found = true + break + } + } + if !found { + continue payloadLoop + } + } + + data, err := hex.DecodeString(e.Value) + if err != nil { + log.Fatalf("Invalid value: %s", err) + } + + // Ignore empty slabs + if len(data) > 0 { + storage[storageKey] = data + } + } +} diff --git a/runtime/interpreter/storage.go b/runtime/interpreter/storage.go index 7a5b872085..1ea6a9d5ad 100644 --- a/runtime/interpreter/storage.go +++ b/runtime/interpreter/storage.go @@ -38,14 +38,14 @@ func StoredValue(storable atree.Storable, storage atree.SlabStorage) Value { } func MustConvertStoredValue(value atree.Value) Value { - converted, err := convertStoredValue(value) + converted, err := ConvertStoredValue(value) if err != nil { panic(err) } return converted } -func convertStoredValue(value atree.Value) (Value, error) { +func ConvertStoredValue(value atree.Value) (Value, error) { switch value := value.(type) { case *atree.Array: return &ArrayValue{