move contract dep tree, link-and-deploy to bind package. This is so that bind can build the dependency tree for each library to populate their exported deps.
This commit is contained in:
parent
7000583654
commit
2421cc3ed5
|
@ -404,51 +404,6 @@ func bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
contracts[types[i]].Library = ok
|
contracts[types[i]].Library = ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute the full set of libraries that each contract depends on.
|
|
||||||
for _, contract := range contracts {
|
|
||||||
if contract.Library {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// recursively traverse the library dependency graph
|
|
||||||
// of the contract, flattening it into a set.
|
|
||||||
//
|
|
||||||
// For abigenv2, we do not generate contract deploy
|
|
||||||
// methods (which in v1 recursively deploy their
|
|
||||||
// library dependencies). So, the entire set of
|
|
||||||
// library dependencies is required, and we will
|
|
||||||
// determine the order to deploy and link them at
|
|
||||||
// runtime.
|
|
||||||
var findDeps func(contract *tmplContract) map[string]struct{}
|
|
||||||
findDeps = func(contract *tmplContract) map[string]struct{} {
|
|
||||||
// 1) match all libraries that this contract depends on
|
|
||||||
re, err := regexp.Compile(`__\$([a-f0-9]+)\$__`)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
libBin := contracts[contract.Type].InputBin
|
|
||||||
matches := re.FindAllStringSubmatch(libBin, -1)
|
|
||||||
result := make(map[string]struct{})
|
|
||||||
|
|
||||||
// 2) recurse, gathering nested library dependencies
|
|
||||||
for _, match := range matches {
|
|
||||||
pattern := match[1]
|
|
||||||
result[pattern] = struct{}{}
|
|
||||||
depContract := contracts[libs[pattern]]
|
|
||||||
for subPattern, _ := range findDeps(depContract) {
|
|
||||||
result[subPattern] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
// take the set of library patterns, convert it to a map of type -> pattern
|
|
||||||
deps := findDeps(contract)
|
|
||||||
contract.AllLibraries = make(map[string]string)
|
|
||||||
for contractPattern, _ := range deps {
|
|
||||||
contractType := libs[contractPattern]
|
|
||||||
contract.AllLibraries[contractType] = contractPattern
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// map of contract name -> pattern
|
// map of contract name -> pattern
|
||||||
invertedLibs := make(map[string]string)
|
invertedLibs := make(map[string]string)
|
||||||
// assume that this is invertible/onto because I assume library names are unique now
|
// assume that this is invertible/onto because I assume library names are unique now
|
||||||
|
@ -457,6 +412,25 @@ func bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
invertedLibs[name] = pattern
|
invertedLibs[name] = pattern
|
||||||
}
|
}
|
||||||
|
|
||||||
|
contractsBins := make(map[string]string)
|
||||||
|
for typ, contract := range contracts {
|
||||||
|
pattern := invertedLibs[typ]
|
||||||
|
contractsBins[pattern] = contract.InputBin
|
||||||
|
}
|
||||||
|
builder := newDepTreeBuilder(nil, contractsBins)
|
||||||
|
roots, deps := builder.BuildDepTrees()
|
||||||
|
allNodes := append(roots, deps...)
|
||||||
|
for _, dep := range allNodes {
|
||||||
|
contractType := libs[dep.pattern]
|
||||||
|
for subDepPattern, _ := range dep.Flatten() {
|
||||||
|
if subDepPattern == dep.pattern {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
subDepType := libs[subDepPattern]
|
||||||
|
contracts[contractType].AllLibraries[subDepType] = subDepPattern
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Generate the contract template data content and render it
|
// Generate the contract template data content and render it
|
||||||
data := &tmplData{
|
data := &tmplData{
|
||||||
Package: pkg,
|
Package: pkg,
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -13,13 +13,12 @@
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
package v2
|
package bind
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
@ -87,6 +86,8 @@ func makeLinkTestCase(input map[rune][]rune, overrides map[rune]common.Address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
|
||||||
type linkTestCaseInput struct {
|
type linkTestCaseInput struct {
|
||||||
input map[rune][]rune
|
input map[rune][]rune
|
||||||
overrides map[rune]struct{}
|
overrides map[rune]struct{}
|
||||||
|
@ -141,10 +142,10 @@ func testLinkCase(t *testing.T, tcInput linkTestCaseInput) {
|
||||||
deployParams DeploymentParams
|
deployParams DeploymentParams
|
||||||
)
|
)
|
||||||
for pattern, bin := range tc.contractCodes {
|
for pattern, bin := range tc.contractCodes {
|
||||||
deployParams.Contracts = append(deployParams.Contracts, &bind.MetaData{Pattern: pattern, Bin: "0x" + bin})
|
deployParams.Contracts = append(deployParams.Contracts, &MetaData{Pattern: pattern, Bin: "0x" + bin})
|
||||||
}
|
}
|
||||||
for pattern, bin := range tc.libCodes {
|
for pattern, bin := range tc.libCodes {
|
||||||
deployParams.Contracts = append(deployParams.Contracts, &bind.MetaData{
|
deployParams.Contracts = append(deployParams.Contracts, &MetaData{
|
||||||
Bin: "0x" + bin,
|
Bin: "0x" + bin,
|
||||||
Pattern: pattern,
|
Pattern: pattern,
|
||||||
})
|
})
|
|
@ -176,7 +176,9 @@ func (_L1 *L1) UnpackDo(data []byte) (*big.Int, error) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var L2LibraryDeps = []*bind.MetaData{}
|
var L2LibraryDeps = []*bind.MetaData{
|
||||||
|
L1MetaData,
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: convert this type to value type after everything works.
|
// TODO: convert this type to value type after everything works.
|
||||||
// L2MetaData contains all meta data concerning the L2 contract.
|
// L2MetaData contains all meta data concerning the L2 contract.
|
||||||
|
@ -224,7 +226,9 @@ func (_L2 *L2) UnpackDo(data []byte) (*big.Int, error) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var L2bLibraryDeps = []*bind.MetaData{}
|
var L2bLibraryDeps = []*bind.MetaData{
|
||||||
|
L1MetaData,
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: convert this type to value type after everything works.
|
// TODO: convert this type to value type after everything works.
|
||||||
// L2bMetaData contains all meta data concerning the L2b contract.
|
// L2bMetaData contains all meta data concerning the L2b contract.
|
||||||
|
@ -320,7 +324,11 @@ func (_L3 *L3) UnpackDo(data []byte) (*big.Int, error) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var L4LibraryDeps = []*bind.MetaData{}
|
var L4LibraryDeps = []*bind.MetaData{
|
||||||
|
L1MetaData,
|
||||||
|
L2MetaData,
|
||||||
|
L3MetaData,
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: convert this type to value type after everything works.
|
// TODO: convert this type to value type after everything works.
|
||||||
// L4MetaData contains all meta data concerning the L4 contract.
|
// L4MetaData contains all meta data concerning the L4 contract.
|
||||||
|
@ -368,7 +376,10 @@ func (_L4 *L4) UnpackDo(data []byte) (*big.Int, error) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var L4bLibraryDeps = []*bind.MetaData{}
|
var L4bLibraryDeps = []*bind.MetaData{
|
||||||
|
L1MetaData,
|
||||||
|
L2bMetaData,
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: convert this type to value type after everything works.
|
// TODO: convert this type to value type after everything works.
|
||||||
// L4bMetaData contains all meta data concerning the L4b contract.
|
// L4bMetaData contains all meta data concerning the L4b contract.
|
||||||
|
|
|
@ -17,9 +17,6 @@
|
||||||
package v2
|
package v2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
"github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
@ -28,222 +25,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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 *bind.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 []*bind.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
|
|
||||||
nodes []*depTreeNode
|
|
||||||
overrideAddr *common.Address
|
|
||||||
}
|
|
||||||
|
|
||||||
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.nodes = append(node.nodes, 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) {
|
|
||||||
// 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 {
|
|
||||||
roots = append(roots, d.subtrees[pattern])
|
|
||||||
}
|
|
||||||
return roots
|
|
||||||
}
|
|
||||||
|
|
||||||
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.nodes {
|
|
||||||
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.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:]))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: this will be generated as part of the bindings, contain the ABI (or metadata object?) and errors
|
// TODO: this will be generated as part of the bindings, contain the ABI (or metadata object?) and errors
|
||||||
type ContractInstance struct {
|
type ContractInstance struct {
|
||||||
Address common.Address
|
Address common.Address
|
||||||
|
|
|
@ -121,13 +121,13 @@ func TestDeploymentLibraries(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to pack constructor: %v", err)
|
t.Fatalf("failed to pack constructor: %v", err)
|
||||||
}
|
}
|
||||||
deploymentParams := DeploymentParams{
|
deploymentParams := bind.DeploymentParams{
|
||||||
Contracts: append(nested_libraries.C1LibraryDeps, nested_libraries.C1MetaData),
|
Contracts: append(nested_libraries.C1LibraryDeps, nested_libraries.C1MetaData),
|
||||||
Inputs: map[string][]byte{nested_libraries.C1MetaData.Pattern: constructorInput},
|
Inputs: map[string][]byte{nested_libraries.C1MetaData.Pattern: constructorInput},
|
||||||
Overrides: nil,
|
Overrides: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := LinkAndDeploy(deploymentParams, makeTestDeployer(opts, bindBackend))
|
res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(opts, bindBackend))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %+v\n", err)
|
t.Fatalf("err: %+v\n", err)
|
||||||
}
|
}
|
||||||
|
@ -180,11 +180,11 @@ func TestDeploymentWithOverrides(t *testing.T) {
|
||||||
defer bindBackend.Backend.Close()
|
defer bindBackend.Backend.Close()
|
||||||
|
|
||||||
// deploy some library deps
|
// deploy some library deps
|
||||||
deploymentParams := DeploymentParams{
|
deploymentParams := bind.DeploymentParams{
|
||||||
Contracts: nested_libraries.C1LibraryDeps,
|
Contracts: nested_libraries.C1LibraryDeps,
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := LinkAndDeploy(deploymentParams, makeTestDeployer(opts, bindBackend))
|
res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(opts, bindBackend))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %+v\n", err)
|
t.Fatalf("err: %+v\n", err)
|
||||||
}
|
}
|
||||||
|
@ -210,12 +210,12 @@ func TestDeploymentWithOverrides(t *testing.T) {
|
||||||
}
|
}
|
||||||
overrides := res.Addrs
|
overrides := res.Addrs
|
||||||
// deploy the contract
|
// deploy the contract
|
||||||
deploymentParams = DeploymentParams{
|
deploymentParams = bind.DeploymentParams{
|
||||||
Contracts: []*bind.MetaData{nested_libraries.C1MetaData},
|
Contracts: []*bind.MetaData{nested_libraries.C1MetaData},
|
||||||
Inputs: map[string][]byte{nested_libraries.C1MetaData.Pattern: constructorInput},
|
Inputs: map[string][]byte{nested_libraries.C1MetaData.Pattern: constructorInput},
|
||||||
Overrides: overrides,
|
Overrides: overrides,
|
||||||
}
|
}
|
||||||
res, err = LinkAndDeploy(deploymentParams, makeTestDeployer(opts, bindBackend))
|
res, err = bind.LinkAndDeploy(deploymentParams, makeTestDeployer(opts, bindBackend))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %+v\n", err)
|
t.Fatalf("err: %+v\n", err)
|
||||||
}
|
}
|
||||||
|
@ -271,11 +271,11 @@ func TestEvents(t *testing.T) {
|
||||||
t.Fatalf("error setting up testing env: %v", err)
|
t.Fatalf("error setting up testing env: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
deploymentParams := DeploymentParams{
|
deploymentParams := bind.DeploymentParams{
|
||||||
Contracts: []*bind.MetaData{events.CMetaData},
|
Contracts: []*bind.MetaData{events.CMetaData},
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := LinkAndDeploy(deploymentParams, makeTestDeployer(txAuth, backend))
|
res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(txAuth, backend))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error deploying contract for testing: %v", err)
|
t.Fatalf("error deploying contract for testing: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -387,11 +387,11 @@ func TestErrors(t *testing.T) {
|
||||||
t.Fatalf("error setting up testing env: %v", err)
|
t.Fatalf("error setting up testing env: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
deploymentParams := DeploymentParams{
|
deploymentParams := bind.DeploymentParams{
|
||||||
Contracts: []*bind.MetaData{solc_errors.CMetaData},
|
Contracts: []*bind.MetaData{solc_errors.CMetaData},
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := LinkAndDeploy(deploymentParams, makeTestDeployer(txAuth, backend))
|
res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(txAuth, backend))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error deploying contract for testing: %v", err)
|
t.Fatalf("error deploying contract for testing: %v", err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue