From ebfb4965c78ebd8d15181f632e02dd3e4cd0ee61 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman Date: Thu, 18 Jul 2019 16:18:20 -0500 Subject: [PATCH] Add tests for decoding packed storage --- libraries/shared/storage/utils/decoder.go | 19 +++- .../shared/storage/utils/decoder_test.go | 91 ++++++++++++++++--- libraries/shared/storage/utils/value.go | 1 + 3 files changed, 94 insertions(+), 17 deletions(-) diff --git a/libraries/shared/storage/utils/decoder.go b/libraries/shared/storage/utils/decoder.go index 88cd6474..149e41ad 100644 --- a/libraries/shared/storage/utils/decoder.go +++ b/libraries/shared/storage/utils/decoder.go @@ -29,6 +29,8 @@ func Decode(row StorageDiffRow, metadata StorageValueMetadata) (interface{}, err return decodeUint256(row.StorageValue.Bytes()), nil case Uint48: return decodeUint48(row.StorageValue.Bytes()), nil + case Uint128: + return decodeUint128(row.StorageValue.Bytes()), nil case Address: return decodeAddress(row.StorageValue.Bytes()), nil case Bytes32: @@ -45,6 +47,11 @@ func decodeUint256(raw []byte) string { return n.String() } +func decodeUint128(raw []byte) string { + n := big.NewInt(0).SetBytes(raw) + return n.String() +} + func decodeUint48(raw []byte) string { n := big.NewInt(0).SetBytes(raw) return n.String() @@ -54,14 +61,13 @@ func decodeAddress(raw []byte) string { return common.BytesToAddress(raw).Hex() } -//this may need to return a slice of strings, a string for each item func decodePackedSlot(raw []byte, packedTypes map[int]ValueType) []string{ storageSlot := raw var results []string //the reason we're using a map and not a slice is that golang doesn't guarantee the order of a slice for _, valueType := range packedTypes { lengthOfStorageSlot := len(storageSlot) - lengthOfItem := getLengthOfItem(valueType) + lengthOfItem := getNumberOfBytes(valueType) itemStartingIndex := lengthOfStorageSlot - lengthOfItem value := storageSlot[itemStartingIndex:] decodedValue := decodeIndividualItems(value, valueType) @@ -78,15 +84,20 @@ func decodeIndividualItems(itemBytes []byte, valueType ValueType) string { switch valueType { case Uint48: return decodeUint48(itemBytes) + case Uint128: + return decodeUint128(itemBytes) default: panic(fmt.Sprintf("can't decode unknown type: %d", valueType)) } } -func getLengthOfItem(valueType ValueType) int{ +func getNumberOfBytes(valueType ValueType) int{ + // 8 bits per byte switch valueType { case Uint48: - return 6 + return 48 / 8 + case Uint128: + return 128 / 8 default: panic(fmt.Sprintf("ValueType %d not recognized", valueType)) } diff --git a/libraries/shared/storage/utils/decoder_test.go b/libraries/shared/storage/utils/decoder_test.go index a01f40db..edb02ee0 100644 --- a/libraries/shared/storage/utils/decoder_test.go +++ b/libraries/shared/storage/utils/decoder_test.go @@ -17,11 +17,10 @@ package utils_test import ( - "math/big" - "github.com/ethereum/go-ethereum/common" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "math/big" "github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils" ) @@ -38,6 +37,17 @@ var _ = Describe("Storage decoder", func() { Expect(result).To(Equal(big.NewInt(0).SetBytes(fakeInt.Bytes()).String())) }) + It("decodes uint128", func() { + fakeInt := common.HexToHash("0000000000000000000000000000000000000000000000000000000000011123") + row := utils.StorageDiffRow{StorageValue: fakeInt} + metadata := utils.StorageValueMetadata{Type: utils.Uint128} + + result, err := utils.Decode(row, metadata) + + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(Equal(big.NewInt(0).SetBytes(fakeInt.Bytes()).String())) + }) + It("decodes uint48", func() { fakeInt := common.HexToHash("0000000000000000000000000000000000000000000000000000000000000123") row := utils.StorageDiffRow{StorageValue: fakeInt} @@ -49,8 +59,20 @@ var _ = Describe("Storage decoder", func() { Expect(result).To(Equal(big.NewInt(0).SetBytes(fakeInt.Bytes()).String())) }) + It("decodes address", func() { + fakeAddress := common.HexToAddress("0x12345") + row := utils.StorageDiffRow{StorageValue: fakeAddress.Hash()} + metadata := utils.StorageValueMetadata{Type: utils.Address} + + result, err := utils.Decode(row, metadata) + + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(Equal(fakeAddress.Hex())) + }) + Describe("when there are multiple items packed in the storage slot", func() { - It("decodes the first uint48 item packed in", func() { + It("decodes uint48 items", func() { + //this is a real storage data example packedStorage := common.HexToHash("000000000000000000000000000000000000000000000002a300000000002a30") row := utils.StorageDiffRow{StorageValue: packedStorage} packedTypes := map[int]utils.ValueType{} @@ -68,20 +90,63 @@ var _ = Describe("Storage decoder", func() { Expect(err).NotTo(HaveOccurred()) expectedResult1 := big.NewInt(0).SetBytes(common.HexToHash("2a30").Bytes()).String() expectedResult2 := big.NewInt(0).SetBytes(common.HexToHash("2a300").Bytes()).String() - Expect(decodedValues[0]).To(Equal(expectedResult1)) - Expect(decodedValues[1]).To(Equal(expectedResult2)) + Expect(decodedValues).To(ConsistOf(expectedResult1, expectedResult2)) }) - }) + It("decodes 5 uint48 items", func() { + //TODO: this packedStorageHex was generated by hand, it would be nice to test this against + //real storage data that has several items packed into it + packedStorageHex := "0000000A5D1AFFFFFFFFFFFE00000009F3C600000002A300000000002A30" - It("decodes address", func() { - fakeAddress := common.HexToAddress("0x12345") - row := utils.StorageDiffRow{StorageValue: fakeAddress.Hash()} - metadata := utils.StorageValueMetadata{Type: utils.Address} + packedStorage := common.HexToHash(packedStorageHex) + row := utils.StorageDiffRow{StorageValue: packedStorage} + packedTypes := map[int]utils.ValueType{} + packedTypes[0] = utils.Uint48 + packedTypes[1] = utils.Uint48 + packedTypes[2] = utils.Uint48 + packedTypes[3] = utils.Uint48 + packedTypes[4] = utils.Uint48 - result, err := utils.Decode(row, metadata) + metadata := utils.StorageValueMetadata{ + Type: utils.PackedSlot, + PackedTypes: packedTypes, + } - Expect(err).NotTo(HaveOccurred()) - Expect(result).To(Equal(fakeAddress.Hex())) + result, err := utils.Decode(row, metadata) + decodedValues := result.([]string) + + Expect(err).NotTo(HaveOccurred()) + expectedResult1 := big.NewInt(0).SetBytes(common.HexToHash("A5D1A").Bytes()).String() + expectedResult2 := big.NewInt(0).SetBytes(common.HexToHash("FFFFFFFFFFFE").Bytes()).String() + expectedResult3 := big.NewInt(0).SetBytes(common.HexToHash("9F3C6").Bytes()).String() + expectedResult4 := big.NewInt(0).SetBytes(common.HexToHash("2a300").Bytes()).String() + expectedResult5 := big.NewInt(0).SetBytes(common.HexToHash("2a30").Bytes()).String() + Expect(decodedValues).To(ConsistOf(expectedResult1, expectedResult2, expectedResult3, expectedResult4, expectedResult5)) + }) + + It("decodes 2 uint128 items", func() { + //TODO: this packedStorageHex was generated by hand, it would be nice to test this against + //real storage data that has several items packed into it + packedStorageHex := "000000038D7EA4C67FF8E502B6730000" + + "0000000000000000AB54A98CEB1F0AD2" + packedStorage := common.HexToHash(packedStorageHex) + row := utils.StorageDiffRow{StorageValue: packedStorage} + packedTypes := map[int]utils.ValueType{} + packedTypes[0] = utils.Uint128 + packedTypes[1] = utils.Uint128 + + metadata := utils.StorageValueMetadata{ + Type: utils.PackedSlot, + PackedTypes: packedTypes, + } + + result, err := utils.Decode(row, metadata) + decodedValues := result.([]string) + + Expect(err).NotTo(HaveOccurred()) + expectedResult1 := big.NewInt(0).SetBytes(common.HexToHash("000000038D7EA4C67FF8E502B6730000").Bytes()).String() + expectedResult2 := big.NewInt(0).SetBytes(common.HexToHash("AB54A98CEB1F0AD2").Bytes()).String() + Expect(decodedValues).To(ConsistOf(expectedResult1, expectedResult2)) + }) }) }) diff --git a/libraries/shared/storage/utils/value.go b/libraries/shared/storage/utils/value.go index 0580b677..c64a8d55 100644 --- a/libraries/shared/storage/utils/value.go +++ b/libraries/shared/storage/utils/value.go @@ -21,6 +21,7 @@ type ValueType int const ( Uint256 ValueType = iota Uint48 + Uint128 Bytes32 Address PackedSlot