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 (
|
var (
|
||||||
contracts []ContractDeployParams
|
deployParams DeploymentParams
|
||||||
libs []*bind.MetaData
|
|
||||||
)
|
)
|
||||||
for pattern, bin := range tc.contractCodes {
|
for pattern, bin := range tc.contractCodes {
|
||||||
contracts = append(contracts, ContractDeployParams{
|
deployParams.Contracts = append(deployParams.Contracts, &bind.MetaData{Pattern: pattern, Bin: "0x" + bin})
|
||||||
Meta: &bind.MetaData{Pattern: pattern, Bin: "0x" + bin},
|
|
||||||
Input: nil,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
for pattern, bin := range tc.libCodes {
|
for pattern, bin := range tc.libCodes {
|
||||||
libs = append(libs, &bind.MetaData{
|
deployParams.Contracts = append(deployParams.Contracts, &bind.MetaData{
|
||||||
Bin: "0x" + bin,
|
Bin: "0x" + bin,
|
||||||
Pattern: pattern,
|
Pattern: pattern,
|
||||||
})
|
})
|
||||||
|
@ -140,11 +136,7 @@ func testLinkCase(t *testing.T, tcInput linkTestCaseInput) {
|
||||||
for pattern, override := range tc.overrides {
|
for pattern, override := range tc.overrides {
|
||||||
overridePatterns[pattern] = override
|
overridePatterns[pattern] = override
|
||||||
}
|
}
|
||||||
deployParams := DeploymentParams{
|
deployParams.Overrides = overridePatterns
|
||||||
Contracts: contracts,
|
|
||||||
Libraries: libs,
|
|
||||||
Overrides: overridePatterns,
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := LinkAndDeploy(deployParams, mockDeploy)
|
res, err := LinkAndDeploy(deployParams, mockDeploy)
|
||||||
if err != nil {
|
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))
|
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) {
|
func TestContractLinking(t *testing.T) {
|
||||||
|
@ -263,7 +250,7 @@ func TestContractLinking(t *testing.T) {
|
||||||
'f': {}, 'g': {}, 'c': {}, 'd': {}, '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.
|
||||||
testLinkCase(t, linkTestCaseInput{
|
testLinkCase(t, linkTestCaseInput{
|
||||||
map[rune][]rune{
|
map[rune][]rune{
|
||||||
'a': {'b', 'c', 'd', 'e'},
|
'a': {'b', 'c', 'd', 'e'},
|
||||||
|
|
|
@ -39,12 +39,8 @@ type ContractDeployParams struct {
|
||||||
// set of contracts, their dependency libraries. It takes an optional override
|
// set of contracts, their dependency libraries. It takes an optional override
|
||||||
// list to specify libraries that have already been deployed on-chain.
|
// list to specify libraries that have already been deployed on-chain.
|
||||||
type DeploymentParams struct {
|
type DeploymentParams struct {
|
||||||
// Contracts is the set of contract deployment parameters for contracts
|
Contracts []*bind.MetaData
|
||||||
// that are about to be deployed.
|
Inputs map[string][]byte
|
||||||
Contracts []ContractDeployParams
|
|
||||||
// Libraries is a map of pattern to metadata for library contracts that
|
|
||||||
// are to be deployed.
|
|
||||||
Libraries []*bind.MetaData
|
|
||||||
// Overrides is an optional map of pattern to deployment address.
|
// Overrides is an optional map of pattern to deployment address.
|
||||||
// Contracts/libraries that refer to dependencies in the override
|
// Contracts/libraries that refer to dependencies in the override
|
||||||
// set are linked to the provided address (an already-deployed contract).
|
// 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 {
|
type depTreeBuilder struct {
|
||||||
overrides map[string]common.Address
|
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 {
|
type depTreeNode struct {
|
||||||
pattern string
|
pattern string
|
||||||
|
@ -83,27 +81,58 @@ type depTreeNode struct {
|
||||||
nodes []*depTreeNode
|
nodes []*depTreeNode
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *depTreeBuilder) buildDepTree(pattern string, contractBin string) *depTreeNode {
|
func (d *depTreeBuilder) buildDepTrees(pattern, contract string) {
|
||||||
node := depTreeNode{pattern, contractBin, nil}
|
|
||||||
|
|
||||||
reMatchSpecificPattern, err := regexp.Compile("__\\$([a-f0-9]+)\\$__")
|
reMatchSpecificPattern, err := regexp.Compile("__\\$([a-f0-9]+)\\$__")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
for _, match := range reMatchSpecificPattern.FindAllStringSubmatch(contractBin, -1) {
|
node := &depTreeNode{
|
||||||
pattern := match[1]
|
pattern: pattern,
|
||||||
if _, ok := d.overrides[pattern]; ok {
|
unlinkedCode: contract,
|
||||||
|
}
|
||||||
|
for _, match := range reMatchSpecificPattern.FindAllStringSubmatch(contract, -1) {
|
||||||
|
depPattern := match[1]
|
||||||
|
if _, ok := d.subtrees[depPattern]; ok {
|
||||||
continue
|
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 {
|
type treeDeployer struct {
|
||||||
deployedAddrs map[string]common.Address
|
deployedAddrs map[string]common.Address
|
||||||
deployerTxs map[string]*types.Transaction
|
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)
|
deploy func(input, deployer []byte) (common.Address, *types.Transaction, error)
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
@ -114,11 +143,13 @@ func (d *treeDeployer) linkAndDeploy(node *depTreeNode) {
|
||||||
}
|
}
|
||||||
// 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 _, child := range node.nodes {
|
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.
|
// 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 {
|
if err != nil {
|
||||||
d.err = err
|
d.err = err
|
||||||
} else {
|
} else {
|
||||||
|
@ -141,32 +172,34 @@ func (d *treeDeployer) Result() (*DeploymentResult, error) {
|
||||||
// libraries. If an error occurs, only contracts which were successfully
|
// libraries. If an error occurs, only contracts which were successfully
|
||||||
// deployed are returned in the result.
|
// deployed are returned in the result.
|
||||||
func LinkAndDeploy(deployParams DeploymentParams, deploy func(input, deployer []byte) (common.Address, *types.Transaction, error)) (res *DeploymentResult, err error) {
|
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
|
unlinkedContracts := make(map[string]string)
|
||||||
unlinkedLibs := make(map[string]string)
|
|
||||||
for _, meta := range deployParams.Libraries {
|
|
||||||
unlinkedLibs[meta.Pattern] = meta.Bin
|
|
||||||
}
|
|
||||||
accumRes := &DeploymentResult{
|
accumRes := &DeploymentResult{
|
||||||
Txs: make(map[string]*types.Transaction),
|
Txs: make(map[string]*types.Transaction),
|
||||||
Addrs: make(map[string]common.Address),
|
Addrs: make(map[string]common.Address),
|
||||||
}
|
}
|
||||||
for _, contract := range deployParams.Contracts {
|
for _, meta := range deployParams.Contracts {
|
||||||
if _, ok := deployParams.Overrides[contract.Meta.Pattern]; ok {
|
unlinkedContracts[meta.Pattern] = meta.Bin
|
||||||
continue
|
}
|
||||||
}
|
// TODO: instantiate this using constructor
|
||||||
treeBuilder := depTreeBuilder{deployParams.Overrides, unlinkedLibs}
|
treeBuilder := depTreeBuilder{
|
||||||
tree := treeBuilder.buildDepTree(contract.Meta.Pattern, contract.Meta.Bin)
|
overrides: deployParams.Overrides,
|
||||||
|
contracts: unlinkedContracts,
|
||||||
|
}
|
||||||
|
|
||||||
|
deps := treeBuilder.BuildDepTrees()
|
||||||
|
for _, tr := range deps {
|
||||||
|
// TODO: instantiate deployer with its tree?
|
||||||
deployer := treeDeployer{
|
deployer := treeDeployer{
|
||||||
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),
|
||||||
deployer.linkAndDeploy(tree)
|
input: map[string][]byte{tr.pattern: deployParams.Inputs[tr.pattern]}}
|
||||||
|
deployer.linkAndDeploy(tr)
|
||||||
res, err := deployer.Result()
|
res, err := deployer.Result()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return accumRes, err
|
return accumRes, err
|
||||||
}
|
}
|
||||||
accumRes.Accumulate(res)
|
accumRes.Accumulate(res)
|
||||||
|
|
||||||
}
|
}
|
||||||
return accumRes, nil
|
return accumRes, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,13 +122,8 @@ func TestDeploymentLibraries(t *testing.T) {
|
||||||
t.Fatalf("failed to pack constructor: %v", err)
|
t.Fatalf("failed to pack constructor: %v", err)
|
||||||
}
|
}
|
||||||
deploymentParams := DeploymentParams{
|
deploymentParams := DeploymentParams{
|
||||||
Contracts: []ContractDeployParams{
|
Contracts: append(nested_libraries.C1LibraryDeps, nested_libraries.C1MetaData),
|
||||||
{
|
Inputs: map[string][]byte{nested_libraries.C1MetaData.Pattern: constructorInput},
|
||||||
Meta: nested_libraries.C1MetaData,
|
|
||||||
Input: constructorInput,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Libraries: nested_libraries.C1LibraryDeps,
|
|
||||||
Overrides: nil,
|
Overrides: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +181,7 @@ func TestDeploymentWithOverrides(t *testing.T) {
|
||||||
|
|
||||||
// deploy some library deps
|
// deploy some library deps
|
||||||
deploymentParams := DeploymentParams{
|
deploymentParams := DeploymentParams{
|
||||||
Libraries: nested_libraries.C1LibraryDeps,
|
Contracts: nested_libraries.C1LibraryDeps,
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := LinkAndDeploy(deploymentParams, makeTestDeployer(opts, bindBackend))
|
res, err := LinkAndDeploy(deploymentParams, makeTestDeployer(opts, bindBackend))
|
||||||
|
@ -216,13 +211,8 @@ func TestDeploymentWithOverrides(t *testing.T) {
|
||||||
overrides := res.Addrs
|
overrides := res.Addrs
|
||||||
// deploy the contract
|
// deploy the contract
|
||||||
deploymentParams = DeploymentParams{
|
deploymentParams = DeploymentParams{
|
||||||
Contracts: []ContractDeployParams{
|
Contracts: []*bind.MetaData{nested_libraries.C1MetaData},
|
||||||
{
|
Inputs: map[string][]byte{nested_libraries.C1MetaData.Pattern: constructorInput},
|
||||||
Meta: nested_libraries.C1MetaData,
|
|
||||||
Input: constructorInput,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Libraries: nil,
|
|
||||||
Overrides: overrides,
|
Overrides: overrides,
|
||||||
}
|
}
|
||||||
res, err = LinkAndDeploy(deploymentParams, makeTestDeployer(opts, bindBackend))
|
res, err = LinkAndDeploy(deploymentParams, makeTestDeployer(opts, bindBackend))
|
||||||
|
@ -281,11 +271,7 @@ func TestEvents(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
deploymentParams := DeploymentParams{
|
deploymentParams := DeploymentParams{
|
||||||
Contracts: []ContractDeployParams{
|
Contracts: []*bind.MetaData{events.CMetaData},
|
||||||
{
|
|
||||||
Meta: events.CMetaData,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := LinkAndDeploy(deploymentParams, makeTestDeployer(txAuth, backend))
|
res, err := LinkAndDeploy(deploymentParams, makeTestDeployer(txAuth, backend))
|
||||||
|
|
Loading…
Reference in New Issue