mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-31 00:53:46 +00:00
staging changes for linking tests. rework LinkAndDeploy to decouple logic of contract deployment, by passing a callback that does it.
This commit is contained in:
parent
ba68e747b5
commit
c8b17ca9c4
3 changed files with 125 additions and 16 deletions
98
accounts/abi/bind/contract_linking_test.go
Normal file
98
accounts/abi/bind/contract_linking_test.go
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
package bind
|
||||
|
||||
import (
|
||||
v2 "github.com/ethereum/go-ethereum/accounts/abi/bind/v2"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type linkTestCase struct {
|
||||
// map of a library to the order in which its dependencies appear in the EVM bytecode.
|
||||
codes map[string]string
|
||||
}
|
||||
|
||||
func makeLinkTestCase(input map[rune][]rune) *linkTestCase {
|
||||
codes := make(map[string]string)
|
||||
inputMap := make(map[rune]map[rune]struct{})
|
||||
|
||||
for contract, deps := range input {
|
||||
inputMap[contract] = make(map[rune]struct{})
|
||||
for _, dep := range deps {
|
||||
codes[string(contract)] = codes[string(contract)] + string(dep)
|
||||
inputMap[contract][dep] = struct{}{}
|
||||
}
|
||||
}
|
||||
return &linkTestCase{
|
||||
generateUnlinkedContracts(codes),
|
||||
}
|
||||
}
|
||||
|
||||
func generateUnlinkedContracts(inputCodes map[string]string) map[string]string {
|
||||
// map of solidity library pattern to unlinked code
|
||||
codes := make(map[string]string)
|
||||
|
||||
for name, code := range inputCodes {
|
||||
var prelinkCode []string
|
||||
for _, char := range code {
|
||||
prelinkCode = append(prelinkCode, crypto.Keccak256Hash([]byte(string(char))).String()[2:36])
|
||||
}
|
||||
pattern := crypto.Keccak256Hash([]byte(string(name))).String()[2:36]
|
||||
codes[pattern] = strings.Join(prelinkCode, "")
|
||||
}
|
||||
return codes
|
||||
}
|
||||
|
||||
var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
|
||||
func testLinkCase(t *testing.T, input map[rune][]rune, overrides map[rune]common.Address) {
|
||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||
var testAddrNonce uint64
|
||||
|
||||
tc := makeLinkTestCase(input)
|
||||
alreadyDeployed := make(map[common.Address]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++
|
||||
|
||||
// assert that this contract only references libs that are known to be deployed or in the override set
|
||||
for i := 0; i < len(deployer)/20; i += 20 {
|
||||
var dep common.Address
|
||||
dep.SetBytes(deployer[i : i+20])
|
||||
if _, ok := alreadyDeployed[dep]; !ok {
|
||||
t.Fatalf("reference to dependent contract that has not yet been deployed.")
|
||||
}
|
||||
}
|
||||
alreadyDeployed[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
|
||||
}
|
||||
doTest := func() {
|
||||
// convert the raw test case into working form
|
||||
|
||||
deployParams := v2.DeploymentParams{
|
||||
Contracts: nil,
|
||||
Libraries: nil,
|
||||
Overrides: nil,
|
||||
}
|
||||
res, err := v2.LinkAndDeploy(deployParams, mockDeploy)
|
||||
if err != nil {
|
||||
t.Fatalf("got error from LinkAndDeploy: %v\n", err)
|
||||
}
|
||||
|
||||
// assert that res contains everything we expected to be deployed
|
||||
}
|
||||
}
|
||||
|
||||
func TestContractLinking(t *testing.T) {
|
||||
|
||||
//test-case specific values (TODO: move these, mockDeploy, doTest into their own routines).
|
||||
|
||||
// input: a cycle of dependencies.
|
||||
// expected: [{set of deps deployed first}, {set of deps deployed second}, ..., {contract(s) deployed}]
|
||||
}
|
||||
|
|
@ -31,32 +31,31 @@ import (
|
|||
|
||||
// deployContract deploys a hex-encoded contract with the given constructor
|
||||
// input. It returns the deployment transaction, address on success.
|
||||
func deployContract(backend bind.ContractBackend, auth *bind.TransactOpts, constructor []byte, contract string) (deploymentTx *types.Transaction, deploymentAddr common.Address, err error) {
|
||||
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 nil, common.Address{}, fmt.Errorf("contract bytecode is not a hex string: %s", contractBinBytes[2:])
|
||||
return common.Address{}, nil, fmt.Errorf("contract bytecode is not a hex string: %s", contractBinBytes[2:])
|
||||
}
|
||||
addr, tx, _, err := bind.DeployContractRaw(auth, contractBinBytes, backend, constructor)
|
||||
addr, tx, err := deploy(constructor, contractBinBytes)
|
||||
if err != nil {
|
||||
return nil, common.Address{}, fmt.Errorf("failed to deploy contract: %v", err)
|
||||
return common.Address{}, nil, fmt.Errorf("failed to deploy contract: %v", err)
|
||||
}
|
||||
return tx, addr, nil
|
||||
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(backend bind.ContractBackend, auth *bind.TransactOpts, contracts map[string]string) (deploymentTxs map[common.Address]*types.Transaction, deployAddrs map[string]common.Address, err error) {
|
||||
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 {
|
||||
contractBinBytes, err := hex.DecodeString(contractBin[2:])
|
||||
contractDeployer, err := hex.DecodeString(contractBin[2:])
|
||||
if err != nil {
|
||||
return deploymentTxs, deployAddrs, fmt.Errorf("contract bytecode is not a hex string: %s", contractBin[2:])
|
||||
}
|
||||
// TODO: can pass nil for constructor?
|
||||
addr, tx, _, err := bind.DeployContractRaw(auth, contractBinBytes, backend, []byte{})
|
||||
addr, tx, err := deploy([]byte{}, contractDeployer)
|
||||
if err != nil {
|
||||
return deploymentTxs, deployAddrs, fmt.Errorf("failed to deploy contract: %v", err)
|
||||
}
|
||||
|
|
@ -155,10 +154,14 @@ type DeploymentResult struct {
|
|||
Addrs map[string]common.Address
|
||||
}
|
||||
|
||||
type ContractDeployer interface {
|
||||
DeployContract(input []byte, deployer []byte) (common.Address, *types.Transaction, error)
|
||||
}
|
||||
|
||||
// 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(auth *bind.TransactOpts, backend bind.ContractBackend, deployParams DeploymentParams) (res *DeploymentResult, err error) {
|
||||
func LinkAndDeploy(deployParams DeploymentParams, deploy func(input, deployer []byte) (common.Address, *types.Transaction, error)) (res *DeploymentResult, err error) {
|
||||
libMetas := deployParams.Libraries
|
||||
overrides := deployParams.Overrides
|
||||
|
||||
|
|
@ -188,7 +191,7 @@ func LinkAndDeploy(auth *bind.TransactOpts, backend bind.ContractBackend, deploy
|
|||
if len(deployableDeps) == 0 {
|
||||
break
|
||||
}
|
||||
deployTxs, deployAddrs, err := deployLibs(backend, auth, deployableDeps)
|
||||
deployTxs, deployAddrs, err := deployLibs(deployableDeps, deploy)
|
||||
for pattern, addr := range deployAddrs {
|
||||
deployed[pattern] = addr
|
||||
res.Addrs[pattern] = addr
|
||||
|
|
@ -205,7 +208,7 @@ func LinkAndDeploy(auth *bind.TransactOpts, backend bind.ContractBackend, deploy
|
|||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
contractTx, contractAddr, err := deployContract(backend, auth, contractParams.Input, linkedContract)
|
||||
contractAddr, contractTx, err := deployContract(contractParams.Input, linkedContract, deploy)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,6 +96,13 @@ func testSetup() (*bind.TransactOpts, *backends.SimulatedBackend, error) {
|
|||
return opts, &bindBackend, nil
|
||||
}
|
||||
|
||||
func makeTestDeployer(auth *bind.TransactOpts, backend bind.ContractBackend) func(input, deployer []byte) (common.Address, *types.Transaction, error) {
|
||||
return func(input, deployer []byte) (common.Address, *types.Transaction, error) {
|
||||
addr, tx, _, err := bind.DeployContractRaw(auth, deployer, backend, input)
|
||||
return addr, tx, err
|
||||
}
|
||||
}
|
||||
|
||||
// test that deploying a contract with library dependencies works,
|
||||
// verifying by calling method on the deployed contract.
|
||||
func TestDeploymentLibraries(t *testing.T) {
|
||||
|
|
@ -124,7 +131,8 @@ func TestDeploymentLibraries(t *testing.T) {
|
|||
Libraries: nested_libraries.C1LibraryDeps,
|
||||
Overrides: nil,
|
||||
}
|
||||
res, err := LinkAndDeploy(opts, bindBackend, deploymentParams)
|
||||
|
||||
res, err := LinkAndDeploy(deploymentParams, makeTestDeployer(opts, bindBackend))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %+v\n", err)
|
||||
}
|
||||
|
|
@ -181,7 +189,7 @@ func TestDeploymentWithOverrides(t *testing.T) {
|
|||
Libraries: nested_libraries.C1LibraryDeps,
|
||||
}
|
||||
|
||||
res, err := LinkAndDeploy(opts, bindBackend, deploymentParams)
|
||||
res, err := LinkAndDeploy(deploymentParams, makeTestDeployer(opts, bindBackend))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %+v\n", err)
|
||||
}
|
||||
|
|
@ -217,7 +225,7 @@ func TestDeploymentWithOverrides(t *testing.T) {
|
|||
Libraries: nil,
|
||||
Overrides: overrides,
|
||||
}
|
||||
res, err = LinkAndDeploy(opts, bindBackend, deploymentParams)
|
||||
res, err = LinkAndDeploy(deploymentParams, makeTestDeployer(opts, bindBackend))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %+v\n", err)
|
||||
}
|
||||
|
|
@ -280,7 +288,7 @@ func TestEvents(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
res, err := LinkAndDeploy(txAuth, backend, deploymentParams)
|
||||
res, err := LinkAndDeploy(deploymentParams, makeTestDeployer(txAuth, backend))
|
||||
if err != nil {
|
||||
t.Fatalf("error deploying contract for testing: %v", err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue