-
Notifications
You must be signed in to change notification settings - Fork 33
VDB-318 Allow for packed storage slots #121
Changes from all commits
b0fff9a
ebfb496
51915f7
d30b4ea
69ad521
2c66afb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,31 +23,82 @@ import ( | |
"github.com/ethereum/go-ethereum/common" | ||
) | ||
|
||
const ( | ||
bitsPerByte = 8 | ||
) | ||
|
||
func Decode(row StorageDiffRow, metadata StorageValueMetadata) (interface{}, error) { | ||
switch metadata.Type { | ||
case Uint256: | ||
return decodeUint256(row.StorageValue.Bytes()), nil | ||
return decodeInteger(row.StorageValue.Bytes()), nil | ||
case Uint48: | ||
return decodeUint48(row.StorageValue.Bytes()), nil | ||
return decodeInteger(row.StorageValue.Bytes()), nil | ||
case Uint128: | ||
return decodeInteger(row.StorageValue.Bytes()), nil | ||
case Address: | ||
return decodeAddress(row.StorageValue.Bytes()), nil | ||
case Bytes32: | ||
return row.StorageValue.Hex(), nil | ||
case PackedSlot: | ||
return decodePackedSlot(row.StorageValue.Bytes(), metadata.PackedTypes), nil | ||
default: | ||
panic(fmt.Sprintf("can't decode unknown type: %d", metadata.Type)) | ||
} | ||
} | ||
|
||
func decodeUint256(raw []byte) string { | ||
n := big.NewInt(0).SetBytes(raw) | ||
return n.String() | ||
} | ||
|
||
func decodeUint48(raw []byte) string { | ||
func decodeInteger(raw []byte) string { | ||
n := big.NewInt(0).SetBytes(raw) | ||
return n.String() | ||
} | ||
|
||
func decodeAddress(raw []byte) string { | ||
return common.BytesToAddress(raw).Hex() | ||
} | ||
|
||
func decodePackedSlot(raw []byte, packedTypes map[int]ValueType) map[int]string { | ||
storageSlotData := raw | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this assignment is superfluous; could name the argument There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assigned the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh yeah that makes perfect sense, sorry I missed that! |
||
decodedStorageSlotItems := map[int]string{} | ||
numberOfTypes := len(packedTypes) | ||
|
||
for position := 0; position < numberOfTypes; position++ { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could maybe replace this with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maps also don't have a guaranteed iteration order (golang actually purposefully randomizes the intersection order 😆: https://blog.golang.org/go-maps-in-action), so if we iterate over them with I think an alternative to the current implementation could be to sort the
Do you think that's more readable? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point! I'm fine to stick with the current implementation given that we need a guaranteed iteration order. I am wondering how much it would muck things up to try an implementation that doesn't require a specific iteration order. Thinking that perhaps we could implement a function that returns the relevant slice for a given index provided the indexes and types of all values packed into the slot, but I'm also not sure whether or not that would be more readable 🤔 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, i need to think about this a bit more. 🤔 do you think it's worth adding a story in the back log to address this later? |
||
//get length of remaining storage date | ||
lengthOfStorageData := len(storageSlotData) | ||
|
||
//get item details (type, length, starting index, value bytes) | ||
itemType := packedTypes[position] | ||
lengthOfItem := getNumberOfBytes(itemType) | ||
itemStartingIndex := lengthOfStorageData - lengthOfItem | ||
itemValueBytes := storageSlotData[itemStartingIndex:] | ||
|
||
//decode item's bytes and set in results map | ||
decodedValue := decodeIndividualItems(itemValueBytes, itemType) | ||
decodedStorageSlotItems[position] = decodedValue | ||
|
||
//pop last item off raw slot data before moving on | ||
storageSlotData = storageSlotData[0:itemStartingIndex] | ||
} | ||
|
||
return decodedStorageSlotItems | ||
} | ||
|
||
func decodeIndividualItems(itemBytes []byte, valueType ValueType) string { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe this should be singular? |
||
switch valueType { | ||
case Uint48: | ||
return decodeInteger(itemBytes) | ||
case Uint128: | ||
return decodeInteger(itemBytes) | ||
default: | ||
panic(fmt.Sprintf("can't decode unknown type: %d", valueType)) | ||
} | ||
} | ||
|
||
func getNumberOfBytes(valueType ValueType) int { | ||
switch valueType { | ||
case Uint48: | ||
return 48 / bitsPerByte | ||
case Uint128: | ||
return 128 / bitsPerByte | ||
default: | ||
panic(fmt.Sprintf("ValueType %d not recognized", valueType)) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,27 +16,54 @@ | |
|
||
package utils | ||
|
||
import "fmt" | ||
|
||
type ValueType int | ||
|
||
const ( | ||
Uint256 ValueType = iota | ||
Uint48 | ||
Uint128 | ||
Bytes32 | ||
Address | ||
PackedSlot | ||
) | ||
|
||
type Key string | ||
|
||
type StorageValueMetadata struct { | ||
Name string | ||
Keys map[Key]string | ||
Type ValueType | ||
Name string | ||
Keys map[Key]string | ||
Type ValueType | ||
PackedNames map[int]string //zero indexed position in map => name of packed item | ||
PackedTypes map[int]ValueType //zero indexed position in map => type of packed item | ||
} | ||
|
||
func GetStorageValueMetadata(name string, keys map[Key]string, valueType ValueType) StorageValueMetadata { | ||
return getMetadata(name, keys, valueType, nil, nil) | ||
} | ||
|
||
func GetStorageValueMetadataForPackedSlot(name string, keys map[Key]string, valueType ValueType, packedNames map[int]string, packedTypes map[int]ValueType) StorageValueMetadata { | ||
return getMetadata(name, keys, valueType, packedNames, packedTypes) | ||
} | ||
|
||
func GetStorageValueMetadata(name string, keys map[Key]string, t ValueType) StorageValueMetadata { | ||
func getMetadata(name string, keys map[Key]string, valueType ValueType, packedNames map[int]string, packedTypes map[int]ValueType) StorageValueMetadata { | ||
assertPackedSlotArgs(valueType, packedNames, packedTypes) | ||
|
||
return StorageValueMetadata{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is probably unnecessary but I'm wondering if we might want to put a guard here to check that if |
||
Name: name, | ||
Keys: keys, | ||
Type: t, | ||
Name: name, | ||
Keys: keys, | ||
Type: valueType, | ||
PackedNames: packedNames, | ||
PackedTypes: packedTypes, | ||
} | ||
} | ||
|
||
func assertPackedSlotArgs(valueType ValueType, packedNames map[int]string, packedTypes map[int]ValueType) { | ||
if valueType == PackedSlot && (packedTypes == nil || packedNames == nil) { | ||
panic(fmt.Sprintf("ValueType is PackedSlot. Expected PackedNames and PackedTypes to not be nil, but got PackedNames = %v and PackedTypes = %v", packedNames, packedTypes)) | ||
} else if (packedNames != nil && packedTypes != nil) && valueType != PackedSlot { | ||
panic(fmt.Sprintf("PackedNames and PackedTypes passed in. Expected ValueType to equal PackedSlot (%v), but got %v.", PackedSlot, valueType)) | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know that
2a300
is "172800" in decimal and2a30
is "10800", which is why you're setting theseexpectedPassedValue
s, but I don't understand how you know where to get the 2 slots from "000000000000000000000000000000000000000000000002a300000000002a30" for the expected values (or how the 0 padding works - is that in the solidity documentation?). Why is2a30
the first slot and2a300
the second? how do you know the second isn't2a3000
edit: I commented on this before seeing the
decodePackedSlot
function, which looks like it might help me understand