Commit 9ad41460 authored by Mark Tyneway's avatar Mark Tyneway

op-bindings: make deterministic

parent 34a90409
......@@ -13,7 +13,9 @@ compile:
cd $(contracts-dir) && \
yarn build
bindings: compile
bindings: compile bindings-build
bindings-build:
go run ./gen/main.go \
-forge-artifacts ../packages/contracts-bedrock/forge-artifacts \
-out ./bindings \
......
......@@ -11,6 +11,14 @@ import (
var remapTypeRe = regexp.MustCompile(`^(t_[\w_]+\([\w]+\))([\d]+)(_[\w]+)?$`)
// typeRemapping represents a mapping between an a type generated by solc
// and a canonicalized type. This is because solc inserts the ast id into
// certain types.
type typeRemapping struct {
oldType string
newType string
}
// CanonicalizeASTIDs canonicalizes AST IDs in storage layouts so that they
// don't cause unnecessary conflicts/diffs. The implementation is not
// particularly efficient, but is plenty fast enough for our purposes.
......@@ -42,16 +50,26 @@ func CanonicalizeASTIDs(in *solc.StorageLayout) *solc.StorageLayout {
}
sortedOldTypes.Sort()
seenTypes := make(map[string]bool)
for _, oldType := range sortedOldTypes {
if seenTypes[oldType] || oldType == "" {
continue
}
matches := remapTypeRe.FindAllStringSubmatch(oldType, -1)
if len(matches) == 0 {
continue
}
if strings.Contains(oldType, "storage") {
continue
}
replaceAstID := matches[0][2]
newType := strings.Replace(oldType, replaceAstID, strconv.Itoa(int(lastId)), 1)
typeRemappings[oldType] = newType
lastId++
seenTypes[oldType] = true
}
outLayout := &solc.StorageLayout{
......@@ -71,13 +89,18 @@ func CanonicalizeASTIDs(in *solc.StorageLayout) *solc.StorageLayout {
for _, oldType := range sortedOldTypes {
value := in.Types[oldType]
newType := replaceType(typeRemappings, oldType)
outLayout.Types[newType] = solc.StorageLayoutType{
layout := solc.StorageLayoutType{
Encoding: value.Encoding,
Label: value.Label,
NumberOfBytes: value.NumberOfBytes,
Key: replaceType(typeRemappings, value.Key),
Value: replaceType(typeRemappings, value.Value),
}
if value.Base != "" {
layout.Base = replaceType(typeRemappings, value.Base)
}
outLayout.Types[newType] = layout
}
return outLayout
}
......@@ -87,6 +110,24 @@ func replaceType(typeRemappings map[string]string, in string) string {
return typeRemappings[in]
}
// Track the number of matches
matches := []typeRemapping{}
for oldType, newType := range typeRemappings {
if strings.Contains(in, oldType) {
matches = append(matches, typeRemapping{oldType, newType})
}
}
// Sometimes types are nested, so handle the recursive case iteratively
if len(matches) > 1 {
for _, match := range matches {
in = strings.Replace(in, match.oldType, match.newType, 1)
}
return in
}
// Its safe to return on the first match here as the case with multiple
// is handled above
for oldType, newType := range typeRemappings {
if strings.Contains(in, oldType) {
return strings.Replace(in, oldType, newType, 1)
......
......@@ -32,6 +32,10 @@ func TestCanonicalize(t *testing.T) {
"values in storage",
"values-in-storage.json",
},
{
"custom types",
"custom-types.json",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
......
{
"in": {
"storage": [
{
"astId": 59243,
"contract": "contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory",
"label": "_initialized",
"offset": 0,
"slot": "0",
"type": "t_uint8"
},
{
"astId": 59246,
"contract": "contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory",
"label": "_initializing",
"offset": 1,
"slot": "0",
"type": "t_bool"
},
{
"astId": 59671,
"contract": "contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory",
"label": "__gap",
"offset": 0,
"slot": "1",
"type": "t_array(t_uint256)50_storage"
},
{
"astId": 59115,
"contract": "contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory",
"label": "_owner",
"offset": 0,
"slot": "51",
"type": "t_address"
},
{
"astId": 59235,
"contract": "contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory",
"label": "__gap",
"offset": 0,
"slot": "52",
"type": "t_array(t_uint256)49_storage"
},
{
"astId": 4350,
"contract": "contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory",
"label": "gameImpls",
"offset": 0,
"slot": "101",
"type": "t_mapping(t_userDefinedValueType(GameType)8945,t_contract(IDisputeGame)5664)"
},
{
"astId": 4357,
"contract": "contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory",
"label": "_disputeGames",
"offset": 0,
"slot": "102",
"type": "t_mapping(t_userDefinedValueType(Hash)8927,t_userDefinedValueType(GameId)8939)"
},
{
"astId": 4362,
"contract": "contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory",
"label": "_disputeGameList",
"offset": 0,
"slot": "103",
"type": "t_array(t_userDefinedValueType(GameId)8939)dyn_storage"
}
],
"types": {
"t_address": {
"encoding": "inplace",
"label": "address",
"numberOfBytes": "20"
},
"t_array(t_uint256)49_storage": {
"encoding": "inplace",
"label": "uint256[49]",
"numberOfBytes": "1568",
"base": "t_uint256"
},
"t_array(t_uint256)50_storage": {
"encoding": "inplace",
"label": "uint256[50]",
"numberOfBytes": "1600",
"base": "t_uint256"
},
"t_array(t_userDefinedValueType(GameId)8939)dyn_storage": {
"encoding": "dynamic_array",
"label": "GameId[]",
"numberOfBytes": "32",
"base": "t_userDefinedValueType(GameId)8939"
},
"t_bool": {
"encoding": "inplace",
"label": "bool",
"numberOfBytes": "1"
},
"t_contract(IDisputeGame)5664": {
"encoding": "inplace",
"label": "contract IDisputeGame",
"numberOfBytes": "20"
},
"t_mapping(t_userDefinedValueType(GameType)8945,t_contract(IDisputeGame)5664)": {
"encoding": "mapping",
"key": "t_userDefinedValueType(GameType)8945",
"label": "mapping(GameType => contract IDisputeGame)",
"numberOfBytes": "32",
"value": "t_contract(IDisputeGame)5664"
},
"t_mapping(t_userDefinedValueType(Hash)8927,t_userDefinedValueType(GameId)8939)": {
"encoding": "mapping",
"key": "t_userDefinedValueType(Hash)8927",
"label": "mapping(Hash => GameId)",
"numberOfBytes": "32",
"value": "t_userDefinedValueType(GameId)8939"
},
"t_uint256": {
"encoding": "inplace",
"label": "uint256",
"numberOfBytes": "32"
},
"t_uint8": {
"encoding": "inplace",
"label": "uint8",
"numberOfBytes": "1"
},
"t_userDefinedValueType(GameId)8939": {
"encoding": "inplace",
"label": "GameId",
"numberOfBytes": "32"
},
"t_userDefinedValueType(GameType)8945": {
"encoding": "inplace",
"label": "GameType",
"numberOfBytes": "1"
},
"t_userDefinedValueType(Hash)8927": {
"encoding": "inplace",
"label": "Hash",
"numberOfBytes": "32"
}
}
},
"out": {
"storage": [
{
"astId": 1000,
"contract": "contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory",
"label": "_initialized",
"offset": 0,
"slot": "0",
"type": "t_uint8"
},
{
"astId": 1001,
"contract": "contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory",
"label": "_initializing",
"offset": 1,
"slot": "0",
"type": "t_bool"
},
{
"astId": 1002,
"contract": "contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory",
"label": "__gap",
"offset": 0,
"slot": "1",
"type": "t_array(t_uint256)50_storage"
},
{
"astId": 1003,
"contract": "contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory",
"label": "_owner",
"offset": 0,
"slot": "51",
"type": "t_address"
},
{
"astId": 1004,
"contract": "contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory",
"label": "__gap",
"offset": 0,
"slot": "52",
"type": "t_array(t_uint256)49_storage"
},
{
"astId": 1005,
"contract": "contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory",
"label": "gameImpls",
"offset": 0,
"slot": "101",
"type": "t_mapping(t_userDefinedValueType(GameType)1010,t_contract(IDisputeGame)1008)"
},
{
"astId": 1006,
"contract": "contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory",
"label": "_disputeGames",
"offset": 0,
"slot": "102",
"type": "t_mapping(t_userDefinedValueType(Hash)1011,t_userDefinedValueType(GameId)1009)"
},
{
"astId": 1007,
"contract": "contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory",
"label": "_disputeGameList",
"offset": 0,
"slot": "103",
"type": "t_array(t_userDefinedValueType(GameId)1009)dyn_storage"
}
],
"types": {
"t_address": {
"encoding": "inplace",
"label": "address",
"numberOfBytes": "20"
},
"t_array(t_uint256)49_storage": {
"encoding": "inplace",
"label": "uint256[49]",
"numberOfBytes": "1568",
"base": "t_uint256"
},
"t_array(t_uint256)50_storage": {
"encoding": "inplace",
"label": "uint256[50]",
"numberOfBytes": "1600",
"base": "t_uint256"
},
"t_array(t_userDefinedValueType(GameId)1009)dyn_storage": {
"encoding": "dynamic_array",
"label": "GameId[]",
"numberOfBytes": "32",
"base": "t_userDefinedValueType(GameId)1009"
},
"t_bool": {
"encoding": "inplace",
"label": "bool",
"numberOfBytes": "1"
},
"t_contract(IDisputeGame)1008": {
"encoding": "inplace",
"label": "contract IDisputeGame",
"numberOfBytes": "20"
},
"t_mapping(t_userDefinedValueType(GameType)1010,t_contract(IDisputeGame)1008)": {
"encoding": "mapping",
"key": "t_userDefinedValueType(GameType)1010",
"label": "mapping(GameType => contract IDisputeGame)",
"numberOfBytes": "32",
"value": "t_contract(IDisputeGame)1008"
},
"t_mapping(t_userDefinedValueType(Hash)1011,t_userDefinedValueType(GameId)1009)": {
"encoding": "mapping",
"key": "t_userDefinedValueType(Hash)1011",
"label": "mapping(Hash => GameId)",
"numberOfBytes": "32",
"value": "t_userDefinedValueType(GameId)1009"
},
"t_uint256": {
"encoding": "inplace",
"label": "uint256",
"numberOfBytes": "32"
},
"t_uint8": {
"encoding": "inplace",
"label": "uint8",
"numberOfBytes": "1"
},
"t_userDefinedValueType(GameId)1009": {
"encoding": "inplace",
"label": "GameId",
"numberOfBytes": "32"
},
"t_userDefinedValueType(GameType)1010": {
"encoding": "inplace",
"label": "GameType",
"numberOfBytes": "1"
},
"t_userDefinedValueType(Hash)1011": {
"encoding": "inplace",
"label": "Hash",
"numberOfBytes": "32"
}
}
}
}
......@@ -64,6 +64,7 @@ type StorageLayoutType struct {
NumberOfBytes uint `json:"numberOfBytes,string"`
Key string `json:"key,omitempty"`
Value string `json:"value,omitempty"`
Base string `json:"base,omitempty"`
}
type CompilerOutputEvm struct {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment