-
Notifications
You must be signed in to change notification settings - Fork 20.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
move contract dep tree, link-and-deploy to bind package. This is so t…
…hat bind can build the dependency tree for each library to populate their exported deps.
- Loading branch information
Showing
6 changed files
with
291 additions
and
282 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
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,242 @@ | ||
package bind | ||
|
||
import ( | ||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/ethereum/go-ethereum/core/types" | ||
"regexp" | ||
"strings" | ||
) | ||
|
||
// ContractDeployParams represents state needed to deploy a contract: | ||
// the metdata and constructor input (which can be nil if no input is specified). | ||
type ContractDeployParams struct { | ||
Meta *MetaData | ||
// Input is the ABI-encoded constructor input for the contract deployment. | ||
Input []byte | ||
} | ||
|
||
// DeploymentParams represents parameters needed to deploy a | ||
// set of contracts, their dependency libraries. It takes an optional override | ||
// list to specify libraries that have already been deployed on-chain. | ||
type DeploymentParams struct { | ||
Contracts []*MetaData | ||
Inputs map[string][]byte | ||
// Overrides is an optional map of pattern to deployment address. | ||
// Contracts/libraries that refer to dependencies in the override | ||
// set are linked to the provided address (an already-deployed contract). | ||
Overrides map[string]common.Address | ||
} | ||
|
||
// DeploymentResult contains the relevant information from the deployment of | ||
// multiple contracts: their deployment txs and addresses. | ||
type DeploymentResult struct { | ||
// map of contract library pattern -> deploy transaction | ||
Txs map[string]*types.Transaction | ||
// map of contract library pattern -> deployed address | ||
Addrs map[string]common.Address | ||
} | ||
|
||
func (d *DeploymentResult) Accumulate(other *DeploymentResult) { | ||
for pattern, tx := range other.Txs { | ||
d.Txs[pattern] = tx | ||
} | ||
for pattern, addr := range other.Addrs { | ||
d.Addrs[pattern] = addr | ||
} | ||
} | ||
|
||
// 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]*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{} | ||
} | ||
|
||
// depTreeNode represents a node (contract) in a dependency tree. it contains its unlinked code, and references to any | ||
// library contracts that it requires. If it is specified as an override, it contains the address where it has already | ||
// been deployed at. | ||
type depTreeNode struct { | ||
pattern string | ||
unlinkedCode string | ||
children []*depTreeNode | ||
overrideAddr *common.Address | ||
} | ||
|
||
// returns the subtree as a map of pattern -> unlinked contract bytecode. it excludes the code of the top-level | ||
// node. | ||
func (n *depTreeNode) Flatten() (res map[string]string) { | ||
res = map[string]string{n.pattern: n.unlinkedCode} | ||
for _, child := range n.children { | ||
subtree := child.Flatten() | ||
|
||
for k, v := range subtree { | ||
res[k] = v | ||
} | ||
} | ||
return res | ||
} | ||
|
||
func (d *depTreeBuilder) buildDepTrees(pattern, contract string) { | ||
// if the node is in the subtree set already, it has already been fully recursed/built so we can bail out. | ||
if _, ok := d.subtrees[pattern]; ok { | ||
return | ||
} | ||
node := &depTreeNode{ | ||
pattern: pattern, | ||
unlinkedCode: contract, | ||
} | ||
if addr, ok := d.overrides[pattern]; ok { | ||
node.overrideAddr = &addr | ||
} | ||
// iterate each referenced library in the unlinked code, recurse and built its subtree. | ||
reMatchSpecificPattern, err := regexp.Compile(`__\$([a-f0-9]+)\$__`) | ||
if err != nil { | ||
panic(err) | ||
} | ||
for _, match := range reMatchSpecificPattern.FindAllStringSubmatch(contract, -1) { | ||
depPattern := match[1] | ||
d.buildDepTrees(depPattern, d.contracts[depPattern]) | ||
node.children = append(node.children, d.subtrees[depPattern]) | ||
|
||
// this library can't be a root dependency if it is referenced by other contracts. | ||
delete(d.roots, depPattern) | ||
} | ||
d.subtrees[pattern] = node | ||
} | ||
|
||
// BuildDepTrees will compute a set of dependency trees from a set of unlinked contracts. The root of each tree | ||
// corresponds to a contract/library that is not referenced as a dependency anywhere else. Children of each node are | ||
// its library dependencies. | ||
func (d *depTreeBuilder) BuildDepTrees() (roots []*depTreeNode, nonRoots []*depTreeNode) { | ||
// 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.contracts { | ||
if _, ok := d.roots[pattern]; ok { | ||
roots = append(roots, d.subtrees[pattern]) | ||
} else { | ||
nonRoots = append(nonRoots, d.subtrees[pattern]) | ||
} | ||
} | ||
return roots, nonRoots | ||
} | ||
|
||
func newDepTreeBuilder(overrides map[string]common.Address, contracts map[string]string) *depTreeBuilder { | ||
return &depTreeBuilder{ | ||
overrides: overrides, | ||
contracts: contracts, | ||
subtrees: make(map[string]*depTreeNode), | ||
roots: make(map[string]struct{}), | ||
} | ||
} | ||
|
||
type deployFn func(input, deployer []byte) (common.Address, *types.Transaction, error) | ||
|
||
// depTreeDeployer is responsible for taking a dependency, deploying-and-linking its components in the proper | ||
// order. A depTreeDeployer cannot be used after calling LinkAndDeploy other than to retrieve the deployment result. | ||
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) | ||
deploy deployFn | ||
err error | ||
} | ||
|
||
// linkAndDeploy recursively deploys a contract/library: starting by linking/deploying its dependencies. | ||
// The deployment result (deploy addresses/txs or an error) is stored in the depTreeDeployer object. | ||
func (d *depTreeDeployer) linkAndDeploy(node *depTreeNode) { | ||
// short-circuit further deployment of contracts if a previous deployment encountered an error. | ||
if d.err != nil { | ||
return | ||
} | ||
|
||
// don't deploy contracts specified as overrides. don't deploy their dependencies. | ||
if node.overrideAddr != nil { | ||
return | ||
} | ||
|
||
// if this contract/library depends on other libraries deploy them (and their dependencies) first | ||
for _, childNode := range node.children { | ||
d.linkAndDeploy(childNode) | ||
} | ||
// if we just deployed any prerequisite contracts, link their deployed addresses into the bytecode to produce | ||
// a deployer bytecode for this contract. | ||
deployerCode := node.unlinkedCode | ||
for _, child := range node.children { | ||
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:])) | ||
} | ||
|
||
// Finally, deploy the contract. | ||
addr, tx, err := d.deploy(d.input[node.pattern], common.Hex2Bytes(deployerCode)) | ||
if err != nil { | ||
d.err = err | ||
} else { | ||
d.deployedAddrs[node.pattern] = addr | ||
d.deployerTxs[node.pattern] = tx | ||
} | ||
} | ||
|
||
// result returns a result for this deployment, or an error if it failed. | ||
func (d *depTreeDeployer) result() (*DeploymentResult, error) { | ||
if d.err != nil { | ||
return nil, d.err | ||
} | ||
return &DeploymentResult{ | ||
Txs: d.deployerTxs, | ||
Addrs: d.deployedAddrs, | ||
}, nil | ||
} | ||
|
||
func newDepTreeDeployer(deploy deployFn) *depTreeDeployer { | ||
return &depTreeDeployer{ | ||
deploy: deploy, | ||
deployedAddrs: make(map[string]common.Address), | ||
deployerTxs: make(map[string]*types.Transaction)} | ||
} | ||
|
||
// LinkAndDeploy deploys a specified set of contracts and their dependent | ||
// libraries. If an error occurs, only contracts which were successfully | ||
// deployed are returned in the result. | ||
func LinkAndDeploy(deployParams DeploymentParams, deploy deployFn) (res *DeploymentResult, err error) { | ||
unlinkedContracts := make(map[string]string) | ||
accumRes := &DeploymentResult{ | ||
Txs: make(map[string]*types.Transaction), | ||
Addrs: make(map[string]common.Address), | ||
} | ||
for _, meta := range deployParams.Contracts { | ||
unlinkedContracts[meta.Pattern] = meta.Bin[2:] | ||
} | ||
treeBuilder := newDepTreeBuilder(deployParams.Overrides, unlinkedContracts) | ||
deps, _ := treeBuilder.BuildDepTrees() | ||
|
||
for _, tr := range deps { | ||
deployer := newDepTreeDeployer(deploy) | ||
if deployParams.Inputs != nil { | ||
deployer.input = map[string][]byte{tr.pattern: deployParams.Inputs[tr.pattern]} | ||
} | ||
deployer.linkAndDeploy(tr) | ||
res, err := deployer.result() | ||
if err != nil { | ||
return accumRes, err | ||
} | ||
accumRes.Accumulate(res) | ||
} | ||
return accumRes, nil | ||
} |
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
Oops, something went wrong.