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)
|
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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 (?)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
Loading…
Reference in New Issue