rewrite contract link/deploy again
This commit is contained in:
parent
e4c01633a4
commit
60fc16dea8
|
@ -120,17 +120,13 @@ func testLinkCase(t *testing.T, tcInput linkTestCaseInput) {
|
|||
}
|
||||
|
||||
var (
|
||||
contracts []ContractDeployParams
|
||||
libs []*bind.MetaData
|
||||
deployParams DeploymentParams
|
||||
)
|
||||
for pattern, bin := range tc.contractCodes {
|
||||
contracts = append(contracts, ContractDeployParams{
|
||||
Meta: &bind.MetaData{Pattern: pattern, Bin: "0x" + bin},
|
||||
Input: nil,
|
||||
})
|
||||
deployParams.Contracts = append(deployParams.Contracts, &bind.MetaData{Pattern: pattern, Bin: "0x" + bin})
|
||||
}
|
||||
for pattern, bin := range tc.libCodes {
|
||||
libs = append(libs, &bind.MetaData{
|
||||
deployParams.Contracts = append(deployParams.Contracts, &bind.MetaData{
|
||||
Bin: "0x" + bin,
|
||||
Pattern: pattern,
|
||||
})
|
||||
|
@ -140,11 +136,7 @@ func testLinkCase(t *testing.T, tcInput linkTestCaseInput) {
|
|||
for pattern, override := range tc.overrides {
|
||||
overridePatterns[pattern] = override
|
||||
}
|
||||
deployParams := DeploymentParams{
|
||||
Contracts: contracts,
|
||||
Libraries: libs,
|
||||
Overrides: overridePatterns,
|
||||
}
|
||||
deployParams.Overrides = overridePatterns
|
||||
|
||||
res, err := LinkAndDeploy(deployParams, mockDeploy)
|
||||
if err != nil {
|
||||
|
@ -160,11 +152,6 @@ func testLinkCase(t *testing.T, tcInput linkTestCaseInput) {
|
|||
t.Fatalf("expected contract %s was not deployed\n", string(contract))
|
||||
}
|
||||
}
|
||||
|
||||
// note that the link-and-deploy functionality assumes that the combined-abi is well-formed.
|
||||
|
||||
// test-case ideas:
|
||||
// * libraries that are disjount from the rest of dep graph (they don't get deployed)
|
||||
}
|
||||
|
||||
func TestContractLinking(t *testing.T) {
|
||||
|
@ -263,7 +250,7 @@ func TestContractLinking(t *testing.T) {
|
|||
'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.
|
||||
testLinkCase(t, linkTestCaseInput{
|
||||
map[rune][]rune{
|
||||
'a': {'b', 'c', 'd', 'e'},
|
||||
|
|
|
@ -39,12 +39,8 @@ type ContractDeployParams struct {
|
|||
// 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 is the set of contract deployment parameters for contracts
|
||||
// that are about to be deployed.
|
||||
Contracts []ContractDeployParams
|
||||
// Libraries is a map of pattern to metadata for library contracts that
|
||||
// are to be deployed.
|
||||
Libraries []*bind.MetaData
|
||||
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).
|
||||
|
@ -69,13 +65,15 @@ func (d *DeploymentResult) Accumulate(other *DeploymentResult) {
|
|||
}
|
||||
}
|
||||
|
||||
type ContractDeployer interface {
|
||||
DeployContract(input []byte, deployer []byte) (common.Address, *types.Transaction, error)
|
||||
}
|
||||
|
||||
type depTreeBuilder struct {
|
||||
overrides map[string]common.Address
|
||||
libs map[string]string
|
||||
// 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{}
|
||||
}
|
||||
type depTreeNode struct {
|
||||
pattern string
|
||||
|
@ -83,27 +81,58 @@ type depTreeNode struct {
|
|||
nodes []*depTreeNode
|
||||
}
|
||||
|
||||
func (d *depTreeBuilder) buildDepTree(pattern string, contractBin string) *depTreeNode {
|
||||
node := depTreeNode{pattern, contractBin, nil}
|
||||
|
||||
func (d *depTreeBuilder) buildDepTrees(pattern, contract string) {
|
||||
reMatchSpecificPattern, err := regexp.Compile("__\\$([a-f0-9]+)\\$__")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, match := range reMatchSpecificPattern.FindAllStringSubmatch(contractBin, -1) {
|
||||
pattern := match[1]
|
||||
if _, ok := d.overrides[pattern]; ok {
|
||||
node := &depTreeNode{
|
||||
pattern: pattern,
|
||||
unlinkedCode: contract,
|
||||
}
|
||||
for _, match := range reMatchSpecificPattern.FindAllStringSubmatch(contract, -1) {
|
||||
depPattern := match[1]
|
||||
if _, ok := d.subtrees[depPattern]; ok {
|
||||
continue
|
||||
}
|
||||
node.nodes = append(node.nodes, d.buildDepTree(pattern, d.libs[pattern]))
|
||||
delete(d.roots, depPattern)
|
||||
d.buildDepTrees(depPattern, d.contracts[depPattern])
|
||||
node.nodes = append(node.nodes, d.subtrees[depPattern])
|
||||
}
|
||||
return &node
|
||||
d.subtrees[pattern] = node
|
||||
}
|
||||
|
||||
func (d *depTreeBuilder) BuildDepTrees() (roots []*depTreeNode) {
|
||||
for pattern, _ := range d.contracts {
|
||||
d.roots[pattern] = struct{}{}
|
||||
}
|
||||
for pattern, contract := range d.contracts {
|
||||
if _, ok := d.subtrees[pattern]; ok {
|
||||
continue
|
||||
}
|
||||
reMatchSpecificPattern, err := regexp.Compile("__\\$([a-f0-9]+)\\$__")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, match := range reMatchSpecificPattern.FindAllStringSubmatch(contract, -1) {
|
||||
depPattern := match[1]
|
||||
delete(d.roots, depPattern)
|
||||
if _, ok := d.subtrees[depPattern]; ok {
|
||||
continue
|
||||
}
|
||||
d.buildDepTrees(depPattern, d.contracts[depPattern])
|
||||
}
|
||||
}
|
||||
for pattern, _ := range d.roots {
|
||||
roots = append(roots, d.subtrees[pattern])
|
||||
}
|
||||
return roots
|
||||
}
|
||||
|
||||
type treeDeployer struct {
|
||||
deployedAddrs map[string]common.Address
|
||||
deployerTxs map[string]*types.Transaction
|
||||
inputs map[string][]byte
|
||||
input map[string][]byte // map of the root contract pattern to the constructor input (if there is any)
|
||||
deploy func(input, deployer []byte) (common.Address, *types.Transaction, error)
|
||||
err error
|
||||
}
|
||||
|
@ -114,11 +143,13 @@ func (d *treeDeployer) linkAndDeploy(node *depTreeNode) {
|
|||
}
|
||||
// link in all node dependencies and produce the deployer bytecode
|
||||
deployerCode := node.unlinkedCode
|
||||
|
||||
for _, child := range node.nodes {
|
||||
deployerCode = strings.ReplaceAll(deployerCode, child.pattern, d.deployedAddrs[child.pattern].String()[2:])
|
||||
deployerCode = strings.ReplaceAll(deployerCode, "__$"+child.pattern+"$__", strings.ToLower(d.deployedAddrs[child.pattern].String()[2:]))
|
||||
}
|
||||
|
||||
// deploy the contract.
|
||||
addr, tx, err := d.deploy(d.inputs[node.pattern], common.Hex2Bytes(deployerCode))
|
||||
addr, tx, err := d.deploy(d.input[node.pattern], common.Hex2Bytes(deployerCode))
|
||||
if err != nil {
|
||||
d.err = err
|
||||
} else {
|
||||
|
@ -141,32 +172,34 @@ func (d *treeDeployer) Result() (*DeploymentResult, error) {
|
|||
// libraries. If an error occurs, only contracts which were successfully
|
||||
// deployed are returned in the result.
|
||||
func LinkAndDeploy(deployParams DeploymentParams, deploy func(input, deployer []byte) (common.Address, *types.Transaction, error)) (res *DeploymentResult, err error) {
|
||||
// re-express libraries as a map of pattern -> pre-link binary
|
||||
unlinkedLibs := make(map[string]string)
|
||||
for _, meta := range deployParams.Libraries {
|
||||
unlinkedLibs[meta.Pattern] = meta.Bin
|
||||
}
|
||||
unlinkedContracts := make(map[string]string)
|
||||
accumRes := &DeploymentResult{
|
||||
Txs: make(map[string]*types.Transaction),
|
||||
Addrs: make(map[string]common.Address),
|
||||
}
|
||||
for _, contract := range deployParams.Contracts {
|
||||
if _, ok := deployParams.Overrides[contract.Meta.Pattern]; ok {
|
||||
continue
|
||||
}
|
||||
treeBuilder := depTreeBuilder{deployParams.Overrides, unlinkedLibs}
|
||||
tree := treeBuilder.buildDepTree(contract.Meta.Pattern, contract.Meta.Bin)
|
||||
for _, meta := range deployParams.Contracts {
|
||||
unlinkedContracts[meta.Pattern] = meta.Bin
|
||||
}
|
||||
// TODO: instantiate this using constructor
|
||||
treeBuilder := depTreeBuilder{
|
||||
overrides: deployParams.Overrides,
|
||||
contracts: unlinkedContracts,
|
||||
}
|
||||
|
||||
deps := treeBuilder.BuildDepTrees()
|
||||
for _, tr := range deps {
|
||||
// TODO: instantiate deployer with its tree?
|
||||
deployer := treeDeployer{
|
||||
deploy: deploy,
|
||||
deployedAddrs: make(map[string]common.Address),
|
||||
deployerTxs: make(map[string]*types.Transaction)}
|
||||
deployer.linkAndDeploy(tree)
|
||||
deployerTxs: make(map[string]*types.Transaction),
|
||||
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
|
||||
}
|
||||
|
|
|
@ -122,13 +122,8 @@ func TestDeploymentLibraries(t *testing.T) {
|
|||
t.Fatalf("failed to pack constructor: %v", err)
|
||||
}
|
||||
deploymentParams := DeploymentParams{
|
||||
Contracts: []ContractDeployParams{
|
||||
{
|
||||
Meta: nested_libraries.C1MetaData,
|
||||
Input: constructorInput,
|
||||
},
|
||||
},
|
||||
Libraries: nested_libraries.C1LibraryDeps,
|
||||
Contracts: append(nested_libraries.C1LibraryDeps, nested_libraries.C1MetaData),
|
||||
Inputs: map[string][]byte{nested_libraries.C1MetaData.Pattern: constructorInput},
|
||||
Overrides: nil,
|
||||
}
|
||||
|
||||
|
@ -186,7 +181,7 @@ func TestDeploymentWithOverrides(t *testing.T) {
|
|||
|
||||
// deploy some library deps
|
||||
deploymentParams := DeploymentParams{
|
||||
Libraries: nested_libraries.C1LibraryDeps,
|
||||
Contracts: nested_libraries.C1LibraryDeps,
|
||||
}
|
||||
|
||||
res, err := LinkAndDeploy(deploymentParams, makeTestDeployer(opts, bindBackend))
|
||||
|
@ -216,13 +211,8 @@ func TestDeploymentWithOverrides(t *testing.T) {
|
|||
overrides := res.Addrs
|
||||
// deploy the contract
|
||||
deploymentParams = DeploymentParams{
|
||||
Contracts: []ContractDeployParams{
|
||||
{
|
||||
Meta: nested_libraries.C1MetaData,
|
||||
Input: constructorInput,
|
||||
},
|
||||
},
|
||||
Libraries: nil,
|
||||
Contracts: []*bind.MetaData{nested_libraries.C1MetaData},
|
||||
Inputs: map[string][]byte{nested_libraries.C1MetaData.Pattern: constructorInput},
|
||||
Overrides: overrides,
|
||||
}
|
||||
res, err = LinkAndDeploy(deploymentParams, makeTestDeployer(opts, bindBackend))
|
||||
|
@ -281,11 +271,7 @@ func TestEvents(t *testing.T) {
|
|||
}
|
||||
|
||||
deploymentParams := DeploymentParams{
|
||||
Contracts: []ContractDeployParams{
|
||||
{
|
||||
Meta: events.CMetaData,
|
||||
},
|
||||
},
|
||||
Contracts: []*bind.MetaData{events.CMetaData},
|
||||
}
|
||||
|
||||
res, err := LinkAndDeploy(deploymentParams, makeTestDeployer(txAuth, backend))
|
||||
|
|
Loading…
Reference in New Issue