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:
parent
736d587990
commit
47192a758c
|
@ -194,6 +194,8 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
|
|||
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) {
|
||||
return c.call(opts, input)
|
||||
}
|
||||
|
|
|
@ -311,10 +311,10 @@ func bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
|||
events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
|
||||
}
|
||||
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.
|
||||
|
||||
// Normalize the event for capital cases and non-anonymous outputs
|
||||
// Normalize the error for capital cases and non-anonymous outputs
|
||||
normalized := original
|
||||
|
||||
// Ensure there is no duplicated identifier
|
||||
|
|
|
@ -241,28 +241,29 @@ func TestContractLinking(t *testing.T) {
|
|||
map[rune]struct{}{'a': {}},
|
||||
map[rune]struct{}{}})
|
||||
|
||||
// two contracts share some dependencies. one contract is marked as an override. only the override contract
|
||||
// is not deployed. its dependencies are all deployed (even the ones not used by f), and the ones shared with f
|
||||
// are not redeployed
|
||||
// two contracts ('a' and 'f') share some dependencies. contract 'a' is marked as an override. expect that any of
|
||||
// its depdencies that aren't shared with 'f' are not deployed.
|
||||
testLinkCase(t, linkTestCaseInput{map[rune][]rune{
|
||||
'a': {'b', 'c', 'd', 'e'},
|
||||
'f': {'g', 'c', 'd', 'h'}},
|
||||
map[rune]struct{}{'a': {}},
|
||||
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.
|
||||
// same condition as above test: no sub-dependencies of
|
||||
testLinkCase(t, linkTestCaseInput{
|
||||
map[rune][]rune{
|
||||
'a': {'b', 'c', 'd', 'e'},
|
||||
'e': {'f', 'g', 'h', 'i', 'm'},
|
||||
'i': {'j', 'k', 'l', 'm'}},
|
||||
'i': {'j', 'k', 'l', 'm'},
|
||||
'l': {'n', 'o', 'p'}},
|
||||
map[rune]struct{}{
|
||||
'i': {},
|
||||
},
|
||||
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 (?)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
overrides map[string]common.Address
|
||||
// map of pattern to unlinked contract bytecode (for libraries or contracts)
|
||||
contracts map[string]string
|
||||
// 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)
|
||||
roots map[string]struct{}
|
||||
}
|
||||
|
@ -78,12 +80,8 @@ type depTreeBuilder struct {
|
|||
type depTreeNode struct {
|
||||
pattern string
|
||||
unlinkedCode string
|
||||
nodes []any
|
||||
}
|
||||
|
||||
type overrideNode struct {
|
||||
pattern string
|
||||
addr common.Address
|
||||
nodes []*depTreeNode
|
||||
overrideAddr *common.Address
|
||||
}
|
||||
|
||||
func (d *depTreeBuilder) buildDepTrees(pattern, contract string) {
|
||||
|
@ -91,19 +89,15 @@ func (d *depTreeBuilder) buildDepTrees(pattern, contract string) {
|
|||
if _, ok := d.subtrees[pattern]; ok {
|
||||
return
|
||||
}
|
||||
if addr, ok := d.overrides[pattern]; ok {
|
||||
node := &overrideNode{
|
||||
pattern: pattern,
|
||||
addr: addr,
|
||||
}
|
||||
d.subtrees[pattern] = node
|
||||
} else {
|
||||
|
||||
node := &depTreeNode{
|
||||
pattern: pattern,
|
||||
unlinkedCode: contract,
|
||||
}
|
||||
// if the node is an override node: add it to the node map but don't recurse on it.
|
||||
// else if the node is depNode: recurse on it, and add it to the dep map.
|
||||
if addr, ok := d.overrides[pattern]; ok {
|
||||
node.overrideAddr = &addr
|
||||
}
|
||||
|
||||
reMatchSpecificPattern, err := regexp.Compile("__\\$([a-f0-9]+)\\$__")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -118,23 +112,26 @@ func (d *depTreeBuilder) buildDepTrees(pattern, contract string) {
|
|||
}
|
||||
d.subtrees[pattern] = node
|
||||
}
|
||||
}
|
||||
|
||||
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{}{}
|
||||
}
|
||||
|
||||
// recursively build each part of the dependency subtree by starting at
|
||||
for pattern, contract := range d.contracts {
|
||||
d.buildDepTrees(pattern, contract)
|
||||
}
|
||||
for pattern, _ := range d.roots {
|
||||
switch node := d.subtrees[pattern].(type) {
|
||||
case *depTreeNode:
|
||||
roots = append(roots, node)
|
||||
}
|
||||
roots = append(roots, d.subtrees[pattern])
|
||||
}
|
||||
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
|
||||
deployerTxs map[string]*types.Transaction
|
||||
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
|
||||
}
|
||||
|
||||
func (d *treeDeployer) linkAndDeploy(n any) {
|
||||
node, ok := n.(*depTreeNode)
|
||||
if !ok {
|
||||
// this was an override node
|
||||
func (d *depTreeDeployer) linkAndDeploy(node *depTreeNode) {
|
||||
if node.overrideAddr != nil {
|
||||
// don't recurse on override nodes
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -154,16 +150,14 @@ func (d *treeDeployer) linkAndDeploy(n any) {
|
|||
}
|
||||
// link in all node dependencies and produce the deployer bytecode
|
||||
deployerCode := node.unlinkedCode
|
||||
for _, c := range node.nodes {
|
||||
switch child := c.(type) {
|
||||
case *depTreeNode:
|
||||
deployerCode = strings.ReplaceAll(deployerCode, "__$"+child.pattern+"$__", strings.ToLower(d.deployedAddrs[child.pattern].String()[2:]))
|
||||
case *overrideNode:
|
||||
deployerCode = strings.ReplaceAll(deployerCode, "__$"+child.pattern+"$__", strings.ToLower(child.addr.String()[2:]))
|
||||
default:
|
||||
panic("invalid node type")
|
||||
for _, child := range node.nodes {
|
||||
var linkAddr common.Address
|
||||
if child.overrideAddr != nil {
|
||||
linkAddr = *child.overrideAddr
|
||||
} else {
|
||||
linkAddr = d.deployedAddrs[child.pattern]
|
||||
}
|
||||
|
||||
deployerCode = strings.ReplaceAll(deployerCode, "__$"+child.pattern+"$__", strings.ToLower(linkAddr.String()[2:]))
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return nil, d.err
|
||||
}
|
||||
|
@ -202,13 +196,13 @@ func LinkAndDeploy(deployParams DeploymentParams, deploy func(input, deployer []
|
|||
treeBuilder := depTreeBuilder{
|
||||
overrides: deployParams.Overrides,
|
||||
contracts: unlinkedContracts,
|
||||
subtrees: make(map[string]any),
|
||||
subtrees: make(map[string]*depTreeNode),
|
||||
roots: make(map[string]struct{}),
|
||||
}
|
||||
|
||||
deps := treeBuilder.BuildDepTrees()
|
||||
for _, tr := range deps {
|
||||
deployer := treeDeployer{
|
||||
deployer := depTreeDeployer{
|
||||
deploy: deploy,
|
||||
deployedAddrs: make(map[string]common.Address),
|
||||
deployerTxs: make(map[string]*types.Transaction)}
|
||||
|
@ -373,15 +367,3 @@ func Call[T any](instance *ContractInstance, opts *bind.CallOpts, packedInput []
|
|||
}
|
||||
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
|
||||
}
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue