fix test cases
This commit is contained in:
parent
81289d7e26
commit
463c3b6dd4
|
@ -71,10 +71,10 @@ func makeLinkTestCase(input map[rune][]rune, overrides map[rune]common.Address)
|
|||
}
|
||||
}
|
||||
|
||||
func testLinkCase(t *testing.T, input map[rune][]rune, overrides map[rune]struct{}) {
|
||||
func testLinkCase(t *testing.T, input map[rune][]rune, overrides map[rune]struct{}, expectDeployed map[rune]struct{}) {
|
||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||
var testAddrNonce uint64
|
||||
alreadyDeployed := make(map[common.Address]struct{})
|
||||
overridesAddrs := make(map[common.Address]struct{})
|
||||
|
||||
// generate deterministic addresses for the override set.
|
||||
rand.Seed(42)
|
||||
|
@ -83,7 +83,7 @@ func testLinkCase(t *testing.T, input map[rune][]rune, overrides map[rune]struct
|
|||
var addr common.Address
|
||||
rand.Read(addr[:])
|
||||
overrideAddrs[contract] = addr
|
||||
alreadyDeployed[addr] = struct{}{}
|
||||
overridesAddrs[addr] = struct{}{}
|
||||
}
|
||||
|
||||
tc := makeLinkTestCase(input, overrideAddrs)
|
||||
|
@ -96,9 +96,6 @@ func testLinkCase(t *testing.T, input map[rune][]rune, overrides map[rune]struct
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: include in link test case: set of contracts that we expect to be deployed at the end.
|
||||
// generate this in makeLinkTestCase
|
||||
// ^ overrides are not included in this case.
|
||||
mockDeploy := func(input []byte, deployer []byte) (common.Address, *types.Transaction, error) {
|
||||
contractAddr := crypto.CreateAddress(testAddr, testAddrNonce)
|
||||
testAddrNonce++
|
||||
|
@ -107,11 +104,11 @@ func testLinkCase(t *testing.T, input map[rune][]rune, overrides map[rune]struct
|
|||
for i := 0; i < len(deployer)/20; i += 20 {
|
||||
var dep common.Address
|
||||
dep.SetBytes(deployer[i : i+20])
|
||||
if _, ok := alreadyDeployed[dep]; !ok {
|
||||
if _, ok := overridesAddrs[dep]; !ok {
|
||||
t.Fatalf("reference to dependent contract that has not yet been deployed: %x\n", dep)
|
||||
}
|
||||
}
|
||||
alreadyDeployed[contractAddr] = struct{}{}
|
||||
overridesAddrs[contractAddr] = struct{}{}
|
||||
// we don't care about the txs themselves for the sake of the linking tests. so we can return nil for them in the mock deployer
|
||||
return contractAddr, nil, nil
|
||||
}
|
||||
|
@ -148,13 +145,14 @@ func testLinkCase(t *testing.T, input map[rune][]rune, overrides map[rune]struct
|
|||
t.Fatalf("got error from LinkAndDeploy: %v\n", err)
|
||||
}
|
||||
|
||||
// TODO: assert that the result consists of the input contracts minus the overrides.
|
||||
|
||||
if len(res.Addrs) != len(allContracts)-len(overrides) {
|
||||
for val, _ := range allContracts {
|
||||
fmt.Println(string(val))
|
||||
if len(res.Addrs) != len(expectDeployed) {
|
||||
t.Fatalf("got %d deployed contracts. expected %d.\n", len(res.Addrs), len(expectDeployed))
|
||||
}
|
||||
for contract, _ := range expectDeployed {
|
||||
pattern := crypto.Keccak256Hash([]byte(string(contract))).String()[2:36]
|
||||
if _, ok := res.Addrs[pattern]; !ok {
|
||||
t.Fatalf("expected contract %s was not deployed\n", string(contract))
|
||||
}
|
||||
t.Fatalf("expected %d contracts to be deployed. got %d\n", len(allContracts)-len(overrides), len(res.Addrs))
|
||||
}
|
||||
|
||||
// note that the link-and-deploy functionality assumes that the combined-abi is well-formed.
|
||||
|
@ -164,19 +162,29 @@ func testLinkCase(t *testing.T, input map[rune][]rune, overrides map[rune]struct
|
|||
}
|
||||
|
||||
func TestContractLinking(t *testing.T) {
|
||||
testLinkCase(t, map[rune][]rune{
|
||||
'a': {'b', 'c', 'd', 'e'},
|
||||
'e': {'f', 'g', 'h', 'i'}},
|
||||
map[rune]struct{}{})
|
||||
|
||||
testLinkCase(t, map[rune][]rune{
|
||||
'a': {'b', 'c', 'd', 'e'}},
|
||||
map[rune]struct{}{})
|
||||
map[rune]struct{}{},
|
||||
map[rune]struct{}{
|
||||
'a': {}, 'b': {}, 'c': {}, 'd': {}, 'e': {},
|
||||
})
|
||||
|
||||
testLinkCase(t, map[rune][]rune{
|
||||
'a': {'b', 'c', 'd', 'e'},
|
||||
'e': {'f', 'g', 'h', 'i'}},
|
||||
map[rune]struct{}{},
|
||||
map[rune]struct{}{
|
||||
'a': {}, 'b': {}, 'c': {}, 'd': {}, 'e': {}, 'f': {}, 'g': {}, 'h': {}, 'i': {},
|
||||
})
|
||||
|
||||
// test single contract only without deps
|
||||
testLinkCase(t, map[rune][]rune{
|
||||
'a': {}},
|
||||
map[rune]struct{}{})
|
||||
map[rune]struct{}{},
|
||||
map[rune]struct{}{
|
||||
'a': {},
|
||||
})
|
||||
|
||||
// test that libraries at different levels of the tree can share deps,
|
||||
// and that these shared deps will only be deployed once.
|
||||
|
@ -184,27 +192,60 @@ func TestContractLinking(t *testing.T) {
|
|||
'a': {'b', 'c', 'd', 'e'},
|
||||
'e': {'f', 'g', 'h', 'i', 'm'},
|
||||
'i': {'j', 'k', 'l', 'm'}},
|
||||
map[rune]struct{}{})
|
||||
map[rune]struct{}{},
|
||||
map[rune]struct{}{
|
||||
'a': {}, 'b': {}, 'c': {}, 'd': {}, 'e': {}, 'f': {}, 'g': {}, 'h': {}, 'i': {}, 'j': {}, 'k': {}, 'l': {}, 'm': {},
|
||||
})
|
||||
|
||||
// test two contracts can be deployed which don't share deps
|
||||
testLinkCase(t, map[rune][]rune{
|
||||
'a': {'b', 'c', 'd', 'e'},
|
||||
'f': {'g', 'h', 'i', 'j'}},
|
||||
map[rune]struct{}{})
|
||||
map[rune]struct{}{},
|
||||
map[rune]struct{}{
|
||||
'a': {}, 'b': {}, 'c': {}, 'd': {}, 'e': {}, 'f': {}, 'g': {}, 'h': {}, 'i': {}, 'j': {},
|
||||
})
|
||||
|
||||
// test two contracts can be deployed which share deps
|
||||
testLinkCase(t, map[rune][]rune{
|
||||
'a': {'b', 'c', 'd', 'e'},
|
||||
'f': {'g', 'c', 'd', 'j'}},
|
||||
map[rune]struct{}{})
|
||||
'f': {'g', 'c', 'd', 'h'}},
|
||||
map[rune]struct{}{},
|
||||
map[rune]struct{}{
|
||||
'a': {}, 'b': {}, 'c': {}, 'd': {}, 'e': {}, 'f': {}, 'g': {}, 'h': {},
|
||||
})
|
||||
|
||||
// test that one contract with overrides for all lib deps
|
||||
// test one contract with overrides for all lib deps
|
||||
testLinkCase(t, map[rune][]rune{
|
||||
'a': {'b', 'c', 'd', 'e'}},
|
||||
map[rune]struct{}{'b': {}, 'c': {}, 'd': {}, 'e': {}})
|
||||
map[rune]struct{}{'b': {}, 'c': {}, 'd': {}, 'e': {}},
|
||||
map[rune]struct{}{
|
||||
'a': {},
|
||||
})
|
||||
|
||||
// test one contract with overrides for some lib deps
|
||||
testLinkCase(t, map[rune][]rune{
|
||||
'a': {'b', 'c'}},
|
||||
map[rune]struct{}{'b': {}, 'c': {}},
|
||||
map[rune]struct{}{
|
||||
'a': {},
|
||||
})
|
||||
|
||||
// test deployment of a contract with overrides
|
||||
testLinkCase(t, map[rune][]rune{
|
||||
'a': {}},
|
||||
map[rune]struct{}{'a': {}})
|
||||
map[rune]struct{}{'a': {}},
|
||||
map[rune]struct{}{})
|
||||
|
||||
// two contracts share some dependencies. one contract is marked as an override. all dependencies for the non-override
|
||||
// contract will be deployed
|
||||
testLinkCase(t, map[rune][]rune{
|
||||
'a': {'b', 'c', 'd', 'e'},
|
||||
'f': {'g', 'c', 'd', 'h'}},
|
||||
map[rune]struct{}{'a': {}},
|
||||
map[rune]struct{}{
|
||||
'f': {}, 'g': {}, 'c': {}, 'd': {}, 'h': {},
|
||||
})
|
||||
|
||||
// TODO: same as the above case but nested one level of dependencies deep (?)
|
||||
}
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
package v2
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
|
@ -29,101 +27,6 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// deployContract deploys a hex-encoded contract with the given constructor
|
||||
// input. It returns the deployment transaction, address on success.
|
||||
func deployContract(constructor []byte, contract string, deploy func(input, deployer []byte) (common.Address, *types.Transaction, error)) (deploymentAddr common.Address, deploymentTx *types.Transaction, err error) {
|
||||
contractBinBytes, err := hex.DecodeString(contract[2:])
|
||||
if err != nil {
|
||||
return common.Address{}, nil, fmt.Errorf("contract bytecode is not a hex string: %s", contractBinBytes[2:])
|
||||
}
|
||||
addr, tx, err := deploy(constructor, contractBinBytes)
|
||||
if err != nil {
|
||||
return common.Address{}, nil, fmt.Errorf("failed to deploy contract: %v", err)
|
||||
}
|
||||
return addr, tx, nil
|
||||
}
|
||||
|
||||
// deployLibs iterates the set contracts (map of pattern to hex-encoded
|
||||
// contract deployer code). Each contract is deployed, and the
|
||||
// resulting addresses/deployment-txs are returned on success.
|
||||
func deployLibs(contracts map[string]string, deploy func(input, deployer []byte) (common.Address, *types.Transaction, error)) (deploymentTxs map[common.Address]*types.Transaction, deployAddrs map[string]common.Address, err error) {
|
||||
deploymentTxs = make(map[common.Address]*types.Transaction)
|
||||
deployAddrs = make(map[string]common.Address)
|
||||
|
||||
for pattern, contractBin := range contracts {
|
||||
contractDeployer, err := hex.DecodeString(contractBin[2:])
|
||||
if err != nil {
|
||||
return deploymentTxs, deployAddrs, fmt.Errorf("contract bytecode is not a hex string: %s", contractBin[2:])
|
||||
}
|
||||
addr, tx, err := deploy([]byte{}, contractDeployer)
|
||||
if err != nil {
|
||||
return deploymentTxs, deployAddrs, fmt.Errorf("failed to deploy contract: %v", err)
|
||||
}
|
||||
deploymentTxs[addr] = tx
|
||||
deployAddrs[pattern] = addr
|
||||
}
|
||||
|
||||
return deploymentTxs, deployAddrs, nil
|
||||
}
|
||||
|
||||
// linkContract takes an unlinked contract deployer hex-encoded code, a map of
|
||||
// already-deployed library dependencies, replaces references to deployed library
|
||||
// dependencies in the contract code, and returns the contract deployment bytecode on
|
||||
// success.
|
||||
func linkContract(contract string, linkedLibs map[string]common.Address) (deployableContract string, err error) {
|
||||
reMatchSpecificPattern, err := regexp.Compile("__\\$([a-f0-9]+)\\$__")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// link in any library the contract depends on
|
||||
for _, match := range reMatchSpecificPattern.FindAllStringSubmatch(contract, -1) {
|
||||
matchingPattern := match[1]
|
||||
addr := linkedLibs[matchingPattern]
|
||||
contract = strings.ReplaceAll(contract, "__$"+matchingPattern+"$__", addr.String()[2:])
|
||||
}
|
||||
return contract, nil
|
||||
}
|
||||
|
||||
// linkLibs iterates the set of dependencies that have yet to be
|
||||
// linked/deployed (pending), replacing references to library dependencies
|
||||
// (i.e. mutating pending) if those dependencies are fully linked/deployed
|
||||
// (in 'linked').
|
||||
//
|
||||
// contracts that have become fully linked in the current invocation are
|
||||
// returned.
|
||||
func linkLibs(pending map[string]string, linked map[string]common.Address) (newPending map[string]string, deployableDeps map[string]string) {
|
||||
newPending = make(map[string]string)
|
||||
reMatchSpecificPattern, err := regexp.Compile("__\\$([a-f0-9]+)\\$__")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
reMatchAnyPattern, err := regexp.Compile("__\\$.*\\$__")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
deployableDeps = make(map[string]string)
|
||||
|
||||
for pattern, dep := range pending {
|
||||
newPending[pattern] = dep
|
||||
// link references to dependent libraries that have been deployed
|
||||
for _, match := range reMatchSpecificPattern.FindAllStringSubmatch(newPending[pattern], -1) {
|
||||
matchingPattern := match[1]
|
||||
addr, ok := linked[matchingPattern]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
newPending[pattern] = strings.ReplaceAll(newPending[pattern], "__$"+matchingPattern+"$__", addr.String()[2:])
|
||||
}
|
||||
// if the library code became fully linked, move it from pending->linked.
|
||||
if !reMatchAnyPattern.MatchString(newPending[pattern]) {
|
||||
deployableDeps[pattern] = newPending[pattern]
|
||||
delete(newPending, pattern)
|
||||
}
|
||||
}
|
||||
return newPending, deployableDeps
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
@ -172,6 +75,7 @@ type ContractDeployer interface {
|
|||
|
||||
type depTreeBuilder struct {
|
||||
overrides map[string]common.Address
|
||||
libs map[string]string
|
||||
}
|
||||
type depTreeNode struct {
|
||||
pattern string
|
||||
|
@ -179,8 +83,8 @@ type depTreeNode struct {
|
|||
nodes []*depTreeNode
|
||||
}
|
||||
|
||||
func (d *depTreeBuilder) buildDepTree(contractBin string, libs map[string]string) *depTreeNode {
|
||||
node := depTreeNode{contractBin, contractBin, nil}
|
||||
func (d *depTreeBuilder) buildDepTree(pattern string, contractBin string) *depTreeNode {
|
||||
node := depTreeNode{pattern, contractBin, nil}
|
||||
|
||||
reMatchSpecificPattern, err := regexp.Compile("__\\$([a-f0-9]+)\\$__")
|
||||
if err != nil {
|
||||
|
@ -191,7 +95,7 @@ func (d *depTreeBuilder) buildDepTree(contractBin string, libs map[string]string
|
|||
if _, ok := d.overrides[pattern]; ok {
|
||||
continue
|
||||
}
|
||||
node.nodes = append(node.nodes, d.buildDepTree(libs[pattern], libs))
|
||||
node.nodes = append(node.nodes, d.buildDepTree(pattern, d.libs[pattern]))
|
||||
}
|
||||
return &node
|
||||
}
|
||||
|
@ -247,9 +151,15 @@ func LinkAndDeploy(deployParams DeploymentParams, deploy func(input, deployer []
|
|||
Addrs: make(map[string]common.Address),
|
||||
}
|
||||
for _, contract := range deployParams.Contracts {
|
||||
treeBuilder := depTreeBuilder{deployParams.Overrides}
|
||||
tree := treeBuilder.buildDepTree(contract.Meta.Bin, unlinkedLibs)
|
||||
deployer := treeDeployer{deploy: deploy}
|
||||
if _, ok := deployParams.Overrides[contract.Meta.Pattern]; ok {
|
||||
continue
|
||||
}
|
||||
treeBuilder := depTreeBuilder{deployParams.Overrides, unlinkedLibs}
|
||||
tree := treeBuilder.buildDepTree(contract.Meta.Pattern, contract.Meta.Bin)
|
||||
deployer := treeDeployer{
|
||||
deploy: deploy,
|
||||
deployedAddrs: make(map[string]common.Address),
|
||||
deployerTxs: make(map[string]*types.Transaction)}
|
||||
deployer.linkAndDeploy(tree)
|
||||
res, err := deployer.Result()
|
||||
if err != nil {
|
||||
|
|
Loading…
Reference in New Issue