forked from ethereum-optimism/optimism
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
op-bindings: Canonicalize bindings, regenerate
- Loading branch information
Showing
25 changed files
with
512 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package ast | ||
|
||
import ( | ||
"regexp" | ||
"sort" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/ethereum-optimism/optimism/op-bindings/solc" | ||
) | ||
|
||
var remapTypeRe = regexp.MustCompile(`^t_[\w_]+\([\w]+\)([\d]+)$`) | ||
|
||
// 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. | ||
// It works in two passes: | ||
// | ||
// 1. First, it finds all AST IDs in storage and types, and builds a | ||
// map to replace them in the second pass. | ||
// 2. The second pass performs the replacement. | ||
// | ||
// This function returns a copy of the passed-in storage layout. The | ||
// inefficiency comes from replaceType, which performs a linear | ||
// search of all replacements when performing substring matches of | ||
// composite types. | ||
func CanonicalizeASTIDs(in *solc.StorageLayout) *solc.StorageLayout { | ||
lastId := uint(1000) | ||
astIDRemappings := make(map[uint]uint) | ||
typeRemappings := make(map[string]string) | ||
|
||
for _, slot := range in.Storage { | ||
astIDRemappings[slot.AstId] = lastId | ||
lastId++ | ||
} | ||
|
||
// Go map iteration order is random, so we need to sort | ||
// keys here in order to prevent non-determinism. | ||
var sortedOldTypes sort.StringSlice | ||
for oldType := range in.Types { | ||
sortedOldTypes = append(sortedOldTypes, oldType) | ||
} | ||
sortedOldTypes.Sort() | ||
|
||
for _, oldType := range sortedOldTypes { | ||
matches := remapTypeRe.FindAllStringSubmatch(oldType, -1) | ||
if len(matches) == 0 { | ||
continue | ||
} | ||
|
||
replaceAstID := matches[0][1] | ||
newType := strings.Replace(oldType, replaceAstID, strconv.Itoa(int(lastId)), 1) | ||
typeRemappings[oldType] = newType | ||
lastId++ | ||
} | ||
|
||
outLayout := &solc.StorageLayout{ | ||
Types: make(map[string]solc.StorageLayoutType), | ||
} | ||
for _, slot := range in.Storage { | ||
outLayout.Storage = append(outLayout.Storage, solc.StorageLayoutEntry{ | ||
AstId: astIDRemappings[slot.AstId], | ||
Contract: slot.Contract, | ||
Label: slot.Label, | ||
Offset: slot.Offset, | ||
Slot: slot.Slot, | ||
Type: replaceType(typeRemappings, slot.Type), | ||
}) | ||
} | ||
|
||
for _, oldType := range sortedOldTypes { | ||
value := in.Types[oldType] | ||
newType := replaceType(typeRemappings, oldType) | ||
outLayout.Types[newType] = solc.StorageLayoutType{ | ||
Encoding: value.Encoding, | ||
Label: value.Label, | ||
NumberOfBytes: value.NumberOfBytes, | ||
Key: replaceType(typeRemappings, value.Key), | ||
Value: replaceType(typeRemappings, value.Value), | ||
} | ||
} | ||
return outLayout | ||
} | ||
|
||
func replaceType(typeRemappings map[string]string, in string) string { | ||
if typeRemappings[in] != "" { | ||
return typeRemappings[in] | ||
} | ||
|
||
for oldType, newType := range typeRemappings { | ||
if strings.Contains(in, oldType) { | ||
return strings.Replace(in, oldType, newType, 1) | ||
} | ||
} | ||
|
||
return in | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package ast | ||
|
||
import ( | ||
"encoding/json" | ||
"os" | ||
"path" | ||
"testing" | ||
|
||
"github.com/ethereum-optimism/optimism/op-bindings/solc" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
type astIDTest struct { | ||
In *solc.StorageLayout `json:"in"` | ||
Out *solc.StorageLayout `json:"out"` | ||
} | ||
|
||
func TestCanonicalize(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
filename string | ||
}{ | ||
{ | ||
"simple", | ||
"simple.json", | ||
}, | ||
{ | ||
"remap public variables", | ||
"public-variables.json", | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
f, err := os.Open(path.Join("testdata", tt.filename)) | ||
require.NoError(t, err) | ||
dec := json.NewDecoder(f) | ||
var testData astIDTest | ||
require.NoError(t, dec.Decode(&testData)) | ||
require.NoError(t, f.Close()) | ||
|
||
// Run 100 times to make sure that we aren't relying | ||
// on random map iteration order. | ||
for i := 0; i < 100; i++ { | ||
require.Equal(t, testData.Out, CanonicalizeASTIDs(testData.In)) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
{ | ||
"in": { | ||
"storage": [ | ||
{ | ||
"astId": 37343, | ||
"contract": "contracts/universal/ProxyAdmin.sol:ProxyAdmin", | ||
"label": "owner", | ||
"offset": 0, | ||
"slot": "0", | ||
"type": "t_address" | ||
}, | ||
{ | ||
"astId": 27905, | ||
"contract": "contracts/universal/ProxyAdmin.sol:ProxyAdmin", | ||
"label": "proxyType", | ||
"offset": 0, | ||
"slot": "1", | ||
"type": "t_mapping(t_address,t_enum(ProxyType)27899)" | ||
}, | ||
{ | ||
"astId": 27910, | ||
"contract": "contracts/universal/ProxyAdmin.sol:ProxyAdmin", | ||
"label": "implementationName", | ||
"offset": 0, | ||
"slot": "2", | ||
"type": "t_mapping(t_address,t_string_storage)" | ||
}, | ||
{ | ||
"astId": 27914, | ||
"contract": "contracts/universal/ProxyAdmin.sol:ProxyAdmin", | ||
"label": "addressManager", | ||
"offset": 0, | ||
"slot": "3", | ||
"type": "t_contract(AddressManager)4431" | ||
}, | ||
{ | ||
"astId": 27918, | ||
"contract": "contracts/universal/ProxyAdmin.sol:ProxyAdmin", | ||
"label": "upgrading", | ||
"offset": 20, | ||
"slot": "3", | ||
"type": "t_bool" | ||
} | ||
], | ||
"types": { | ||
"t_address": { | ||
"encoding": "inplace", | ||
"label": "address", | ||
"numberOfBytes": "20" | ||
}, | ||
"t_bool": { | ||
"encoding": "inplace", | ||
"label": "bool", | ||
"numberOfBytes": "1" | ||
}, | ||
"t_contract(AddressManager)4431": { | ||
"encoding": "inplace", | ||
"label": "contract AddressManager", | ||
"numberOfBytes": "20" | ||
}, | ||
"t_enum(ProxyType)27899": { | ||
"encoding": "inplace", | ||
"label": "enum ProxyAdmin.ProxyType", | ||
"numberOfBytes": "1" | ||
}, | ||
"t_mapping(t_address,t_enum(ProxyType)27899)": { | ||
"encoding": "mapping", | ||
"key": "t_address", | ||
"label": "mapping(address => enum ProxyAdmin.ProxyType)", | ||
"numberOfBytes": "32", | ||
"value": "t_enum(ProxyType)27899" | ||
}, | ||
"t_mapping(t_address,t_string_storage)": { | ||
"encoding": "mapping", | ||
"key": "t_address", | ||
"label": "mapping(address => string)", | ||
"numberOfBytes": "32", | ||
"value": "t_string_storage" | ||
}, | ||
"t_string_storage": { | ||
"encoding": "bytes", | ||
"label": "string", | ||
"numberOfBytes": "32" | ||
} | ||
} | ||
}, | ||
"out": { | ||
"storage": [ | ||
{ | ||
"astId": 1000, | ||
"contract": "contracts/universal/ProxyAdmin.sol:ProxyAdmin", | ||
"label": "owner", | ||
"offset": 0, | ||
"slot": "0", | ||
"type": "t_address" | ||
}, | ||
{ | ||
"astId": 1001, | ||
"contract": "contracts/universal/ProxyAdmin.sol:ProxyAdmin", | ||
"label": "proxyType", | ||
"offset": 0, | ||
"slot": "1", | ||
"type": "t_mapping(t_address,t_enum(ProxyType)1006)" | ||
}, | ||
{ | ||
"astId": 1002, | ||
"contract": "contracts/universal/ProxyAdmin.sol:ProxyAdmin", | ||
"label": "implementationName", | ||
"offset": 0, | ||
"slot": "2", | ||
"type": "t_mapping(t_address,t_string_storage)" | ||
}, | ||
{ | ||
"astId": 1003, | ||
"contract": "contracts/universal/ProxyAdmin.sol:ProxyAdmin", | ||
"label": "addressManager", | ||
"offset": 0, | ||
"slot": "3", | ||
"type": "t_contract(AddressManager)1005" | ||
}, | ||
{ | ||
"astId": 1004, | ||
"contract": "contracts/universal/ProxyAdmin.sol:ProxyAdmin", | ||
"label": "upgrading", | ||
"offset": 20, | ||
"slot": "3", | ||
"type": "t_bool" | ||
} | ||
], | ||
"types": { | ||
"t_address": { | ||
"encoding": "inplace", | ||
"label": "address", | ||
"numberOfBytes": "20" | ||
}, | ||
"t_bool": { | ||
"encoding": "inplace", | ||
"label": "bool", | ||
"numberOfBytes": "1" | ||
}, | ||
"t_contract(AddressManager)1005": { | ||
"encoding": "inplace", | ||
"label": "contract AddressManager", | ||
"numberOfBytes": "20" | ||
}, | ||
"t_enum(ProxyType)1006": { | ||
"encoding": "inplace", | ||
"label": "enum ProxyAdmin.ProxyType", | ||
"numberOfBytes": "1" | ||
}, | ||
"t_mapping(t_address,t_enum(ProxyType)1006)": { | ||
"encoding": "mapping", | ||
"key": "t_address", | ||
"label": "mapping(address => enum ProxyAdmin.ProxyType)", | ||
"numberOfBytes": "32", | ||
"value": "t_enum(ProxyType)1006" | ||
}, | ||
"t_mapping(t_address,t_string_storage)": { | ||
"encoding": "mapping", | ||
"key": "t_address", | ||
"label": "mapping(address => string)", | ||
"numberOfBytes": "32", | ||
"value": "t_string_storage" | ||
}, | ||
"t_string_storage": { | ||
"encoding": "bytes", | ||
"label": "string", | ||
"numberOfBytes": "32" | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.