change semantics of contract overrides again: any direct/indirect dependencies of contracts/libraries specified with override addresses should not be deployed if they are not elsewhere referenced as direct/indirect deps of non-override contracts.

This commit is contained in:
Jared Wasinger 2024-12-13 15:39:44 +07:00 committed by Felix Lange
parent 736d587990
commit 47192a758c
4 changed files with 59 additions and 74 deletions

View File

@ -194,6 +194,8 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
return c.abi.UnpackIntoInterface(res[0], method, output) return c.abi.UnpackIntoInterface(res[0], method, output)
} }
// CallRaw executes an eth_call against the contract with the raw calldata as
// input. It returns the call's return data or an error.
func (c *BoundContract) CallRaw(opts *CallOpts, input []byte) ([]byte, error) { func (c *BoundContract) CallRaw(opts *CallOpts, input []byte) ([]byte, error) {
return c.call(opts, input) return c.call(opts, input)
} }

View File

@ -311,10 +311,10 @@ func bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
events[original.Name] = &tmplEvent{Original: original, Normalized: normalized} events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
} }
for _, original := range evmABI.Errors { for _, original := range evmABI.Errors {
// TODO: I copied this from events. I think it should be correct but not totally sure // TODO: I copied this from events (above in this function). I think it should be correct but not totally sure
// even if it is correct, should consider deduplicating this into its own function. // even if it is correct, should consider deduplicating this into its own function.
// Normalize the event for capital cases and non-anonymous outputs // Normalize the error for capital cases and non-anonymous outputs
normalized := original normalized := original
// Ensure there is no duplicated identifier // Ensure there is no duplicated identifier

View File

@ -241,28 +241,29 @@ func TestContractLinking(t *testing.T) {
map[rune]struct{}{'a': {}}, map[rune]struct{}{'a': {}},
map[rune]struct{}{}}) map[rune]struct{}{}})
// two contracts share some dependencies. one contract is marked as an override. only the override contract // two contracts ('a' and 'f') share some dependencies. contract 'a' is marked as an override. expect that any of
// is not deployed. its dependencies are all deployed (even the ones not used by f), and the ones shared with f // its depdencies that aren't shared with 'f' are not deployed.
// are not redeployed
testLinkCase(t, linkTestCaseInput{map[rune][]rune{ testLinkCase(t, linkTestCaseInput{map[rune][]rune{
'a': {'b', 'c', 'd', 'e'}, 'a': {'b', 'c', 'd', 'e'},
'f': {'g', 'c', 'd', 'h'}}, 'f': {'g', 'c', 'd', 'h'}},
map[rune]struct{}{'a': {}}, map[rune]struct{}{'a': {}},
map[rune]struct{}{ map[rune]struct{}{
'b': {}, 'c': {}, 'd': {}, 'e': {}, 'f': {}, 'g': {}, 'h': {}, 'f': {}, 'g': {}, 'c': {}, 'd': {}, 'h': {},
}}) }})
// test nested libraries that share deps at different levels of the tree... with override. // test nested libraries that share deps at different levels of the tree... with override.
// same condition as above test: no sub-dependencies of
testLinkCase(t, linkTestCaseInput{ testLinkCase(t, linkTestCaseInput{
map[rune][]rune{ map[rune][]rune{
'a': {'b', 'c', 'd', 'e'}, 'a': {'b', 'c', 'd', 'e'},
'e': {'f', 'g', 'h', 'i', 'm'}, 'e': {'f', 'g', 'h', 'i', 'm'},
'i': {'j', 'k', 'l', 'm'}}, 'i': {'j', 'k', 'l', 'm'},
'l': {'n', 'o', 'p'}},
map[rune]struct{}{ map[rune]struct{}{
'i': {}, 'i': {},
}, },
map[rune]struct{}{ map[rune]struct{}{
'a': {}, 'b': {}, 'c': {}, 'd': {}, 'e': {}, 'f': {}, 'g': {}, 'h': {}, 'j': {}, 'k': {}, 'l': {}, 'm': {}, 'a': {}, 'b': {}, 'c': {}, 'd': {}, 'e': {}, 'f': {}, 'g': {}, 'h': {}, 'm': {},
}}) }})
// TODO: same as the above case but nested one level of dependencies deep (?) // TODO: same as the above case but nested one level of dependencies deep (?)
} }

View File

@ -65,12 +65,14 @@ func (d *DeploymentResult) Accumulate(other *DeploymentResult) {
} }
} }
// depTreeBuilder turns a set of unlinked contracts and their dependent libraries into a collection of trees
// representing the relation of their dependencies.
type depTreeBuilder struct { type depTreeBuilder struct {
overrides map[string]common.Address overrides map[string]common.Address
// map of pattern to unlinked contract bytecode (for libraries or contracts) // map of pattern to unlinked contract bytecode (for libraries or contracts)
contracts map[string]string contracts map[string]string
// map of pattern to subtree represented by contract // map of pattern to subtree represented by contract
subtrees map[string]any subtrees map[string]*depTreeNode
// map of nodes that aren't referenced by other dependencies (these can be libraries too if user is doing lib-only deployment) // map of nodes that aren't referenced by other dependencies (these can be libraries too if user is doing lib-only deployment)
roots map[string]struct{} roots map[string]struct{}
} }
@ -78,12 +80,8 @@ type depTreeBuilder struct {
type depTreeNode struct { type depTreeNode struct {
pattern string pattern string
unlinkedCode string unlinkedCode string
nodes []any nodes []*depTreeNode
} overrideAddr *common.Address
type overrideNode struct {
pattern string
addr common.Address
} }
func (d *depTreeBuilder) buildDepTrees(pattern, contract string) { func (d *depTreeBuilder) buildDepTrees(pattern, contract string) {
@ -91,19 +89,15 @@ func (d *depTreeBuilder) buildDepTrees(pattern, contract string) {
if _, ok := d.subtrees[pattern]; ok { if _, ok := d.subtrees[pattern]; ok {
return return
} }
if addr, ok := d.overrides[pattern]; ok {
node := &overrideNode{
pattern: pattern,
addr: addr,
}
d.subtrees[pattern] = node
} else {
node := &depTreeNode{ node := &depTreeNode{
pattern: pattern, pattern: pattern,
unlinkedCode: contract, unlinkedCode: contract,
} }
// if the node is an override node: add it to the node map but don't recurse on it. if addr, ok := d.overrides[pattern]; ok {
// else if the node is depNode: recurse on it, and add it to the dep map. node.overrideAddr = &addr
}
reMatchSpecificPattern, err := regexp.Compile("__\\$([a-f0-9]+)\\$__") reMatchSpecificPattern, err := regexp.Compile("__\\$([a-f0-9]+)\\$__")
if err != nil { if err != nil {
panic(err) panic(err)
@ -118,23 +112,26 @@ func (d *depTreeBuilder) buildDepTrees(pattern, contract string) {
} }
d.subtrees[pattern] = node d.subtrees[pattern] = node
} }
}
func (d *depTreeBuilder) BuildDepTrees() (roots []*depTreeNode) { func (d *depTreeBuilder) BuildDepTrees() (roots []*depTreeNode) {
for pattern, contract := range d.contracts { // before the trees of dependencies are known, consider that any provided contract could be a root.
for pattern, _ := range d.contracts {
d.roots[pattern] = struct{}{} d.roots[pattern] = struct{}{}
}
// recursively build each part of the dependency subtree by starting at
for pattern, contract := range d.contracts {
d.buildDepTrees(pattern, contract) d.buildDepTrees(pattern, contract)
} }
for pattern, _ := range d.roots { for pattern, _ := range d.roots {
switch node := d.subtrees[pattern].(type) { roots = append(roots, d.subtrees[pattern])
case *depTreeNode:
roots = append(roots, node)
}
} }
return roots return roots
} }
type treeDeployer struct { // depTreeDeployer is responsible for taking a built dependency, deploying-and-linking its components in the proper
// order.
type depTreeDeployer struct {
deployedAddrs map[string]common.Address deployedAddrs map[string]common.Address
deployerTxs map[string]*types.Transaction deployerTxs map[string]*types.Transaction
input map[string][]byte // map of the root contract pattern to the constructor input (if there is any) input map[string][]byte // map of the root contract pattern to the constructor input (if there is any)
@ -142,10 +139,9 @@ type treeDeployer struct {
err error err error
} }
func (d *treeDeployer) linkAndDeploy(n any) { func (d *depTreeDeployer) linkAndDeploy(node *depTreeNode) {
node, ok := n.(*depTreeNode) if node.overrideAddr != nil {
if !ok { // don't recurse on override nodes
// this was an override node
return return
} }
@ -154,16 +150,14 @@ func (d *treeDeployer) linkAndDeploy(n any) {
} }
// link in all node dependencies and produce the deployer bytecode // link in all node dependencies and produce the deployer bytecode
deployerCode := node.unlinkedCode deployerCode := node.unlinkedCode
for _, c := range node.nodes { for _, child := range node.nodes {
switch child := c.(type) { var linkAddr common.Address
case *depTreeNode: if child.overrideAddr != nil {
deployerCode = strings.ReplaceAll(deployerCode, "__$"+child.pattern+"$__", strings.ToLower(d.deployedAddrs[child.pattern].String()[2:])) linkAddr = *child.overrideAddr
case *overrideNode: } else {
deployerCode = strings.ReplaceAll(deployerCode, "__$"+child.pattern+"$__", strings.ToLower(child.addr.String()[2:])) linkAddr = d.deployedAddrs[child.pattern]
default:
panic("invalid node type")
} }
deployerCode = strings.ReplaceAll(deployerCode, "__$"+child.pattern+"$__", strings.ToLower(linkAddr.String()[2:]))
} }
// deploy the contract. // deploy the contract.
@ -176,7 +170,7 @@ func (d *treeDeployer) linkAndDeploy(n any) {
} }
} }
func (d *treeDeployer) Result() (*DeploymentResult, error) { func (d *depTreeDeployer) Result() (*DeploymentResult, error) {
if d.err != nil { if d.err != nil {
return nil, d.err return nil, d.err
} }
@ -202,13 +196,13 @@ func LinkAndDeploy(deployParams DeploymentParams, deploy func(input, deployer []
treeBuilder := depTreeBuilder{ treeBuilder := depTreeBuilder{
overrides: deployParams.Overrides, overrides: deployParams.Overrides,
contracts: unlinkedContracts, contracts: unlinkedContracts,
subtrees: make(map[string]any), subtrees: make(map[string]*depTreeNode),
roots: make(map[string]struct{}), roots: make(map[string]struct{}),
} }
deps := treeBuilder.BuildDepTrees() deps := treeBuilder.BuildDepTrees()
for _, tr := range deps { for _, tr := range deps {
deployer := treeDeployer{ deployer := depTreeDeployer{
deploy: deploy, deploy: deploy,
deployedAddrs: make(map[string]common.Address), deployedAddrs: make(map[string]common.Address),
deployerTxs: make(map[string]*types.Transaction)} deployerTxs: make(map[string]*types.Transaction)}
@ -373,15 +367,3 @@ func Call[T any](instance *ContractInstance, opts *bind.CallOpts, packedInput []
} }
return unpack(packedOutput) return unpack(packedOutput)
} }
/*
func UnpackError(metadata *bind.MetaData, raw []byte) (any, error) {
fmt.Printf("raw is %x\n", raw[0:4])
errType := metadata.Errors[fmt.Sprintf("%x", raw[0:4])]
abi, _ := metadata.GetAbi() // TODO: check error here?
res := reflect.New(errType).Interface()
fmt.Printf("err type name is %s\n", errType.Name())
err := abi.UnpackIntoInterface(&res, errType.Name(), raw)
return res, err
}
*/