gitlab.com/flarenetwork/coreth@v0.1.1/chain/multicoin_test.go (about)

     1  // (c) 2019-2020, Ava Labs, Inc.
     2  //
     3  // This file is a derived work, based on the go-ethereum library whose original
     4  // notices appear below.
     5  //
     6  // It is distributed under a license compatible with the licensing terms of the
     7  // original code from which it is derived.
     8  //
     9  // Much love to the original authors for their work.
    10  // **********
    11  // (c) 2019-2020, Ava Labs, Inc. All rights reserved.
    12  // See the file LICENSE for licensing terms.
    13  
    14  // NOTE from Ted: to compile from solidity source using the code, make sure
    15  // your solc<0.8.0, as geth 1.9.21 does not support the JSON output from
    16  // solc>=0.8.0:
    17  // See:
    18  // - https://github.com/ethereum/go-ethereum/issues/22041
    19  // - https://github.com/ethereum/go-ethereum/pull/22092
    20  
    21  package chain
    22  
    23  import (
    24  	"crypto/rand"
    25  	"encoding/json"
    26  	"fmt"
    27  	"math/big"
    28  	"strings"
    29  	"testing"
    30  
    31  	"github.com/ethereum/go-ethereum/accounts/abi"
    32  	"github.com/ethereum/go-ethereum/common"
    33  	"github.com/ethereum/go-ethereum/crypto"
    34  	"github.com/ethereum/go-ethereum/log"
    35  	"gitlab.com/flarenetwork/coreth/accounts/keystore"
    36  	"gitlab.com/flarenetwork/coreth/consensus/dummy"
    37  	"gitlab.com/flarenetwork/coreth/core"
    38  	"gitlab.com/flarenetwork/coreth/core/rawdb"
    39  	"gitlab.com/flarenetwork/coreth/core/types"
    40  	"gitlab.com/flarenetwork/coreth/core/vm"
    41  	"gitlab.com/flarenetwork/coreth/eth"
    42  	"gitlab.com/flarenetwork/coreth/eth/ethconfig"
    43  	"gitlab.com/flarenetwork/coreth/node"
    44  )
    45  
    46  // TestMulticoin tests multicoin low-level state management and regular
    47  // transaction/smart contract transfer.
    48  func TestMulticoin(t *testing.T) {
    49  	// configure the chain
    50  	config := ethconfig.NewDefaultConfig()
    51  
    52  	// configure the genesis block
    53  	genesisJSON := `{"config":{"chainId":1,"homesteadBlock":0,"daoForkBlock":0,"daoForkSupport":true,"eip150Block":0,"eip150Hash":"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0","eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"petersburgBlock":0,"istanbulBlock":0,"muirGlacierBlock":0},"nonce":"0x0","timestamp":"0x0","extraData":"0x00","gasLimit":"0x5f5e100","difficulty":"0x0","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"751a0b96e1042bee789452ecb20253fba40dbe85":{"balance":"0x1000000000000000", "mcbalance": {"0x0000000000000000000000000000000000000000000000000000000000000000": 1000000000000000000}}, "0100000000000000000000000000000000000000": {"code": "0x73000000000000000000000000000000000000000030146080604052600436106100405760003560e01c80631e01043914610045578063b6510bb314610087575b600080fd5b6100716004803603602081101561005b57600080fd5b81019080803590602001909291905050506100f6565b6040518082815260200191505060405180910390f35b81801561009357600080fd5b506100f4600480360360808110156100aa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019092919080359060200190929190505050610119565b005b60003073ffffffffffffffffffffffffffffffffffffffff168290cd9050919050565b8373ffffffffffffffffffffffffffffffffffffffff1681836108fc8690811502906040516000604051808303818888878c8acf95505050505050158015610165573d6000803e3d6000fd5b505050505056fea26469706673582212204ca02a58b31e59814fcb487b2bdc205149e01e9f695f02f5e73ae40c4f027c1e64736f6c634300060a0033", "balance": "0x0", "mcbalance": {}}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}`
    54  	mcAbiJSON := `[{"inputs":[{"internalType":"uint256","name":"coinid","type":"uint256"}],"name":"getBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"coinid","type":"uint256"},{"internalType":"uint256","name":"amount2","type":"uint256"}],"name":"transfer","outputs":[],"stateMutability":"nonpayable","type":"function"}]`
    55  	genesisKey := "0xabd71b35d559563fea757f0f5edbde286fb8c043105b15abb7cd57189306d7d1"
    56  
    57  	bobKey, _ := keystore.NewKey(rand.Reader)
    58  	genesisBlock := new(core.Genesis)
    59  	if err := json.Unmarshal([]byte(genesisJSON), genesisBlock); err != nil {
    60  		t.Fatal(err)
    61  	}
    62  	hk, _ := crypto.HexToECDSA(genesisKey[2:])
    63  	genKey := keystore.NewKeyFromECDSA(hk)
    64  
    65  	config.Genesis = genesisBlock
    66  
    67  	// NOTE: use precompiled `mc_test.sol` for portability, do not remove the
    68  	// following code (for debug purpose)
    69  	//
    70  	//// compile the smart contract
    71  	//gopath := os.Getenv("GOPATH")
    72  	//if gopath == "" {
    73  	//	gopath = build.Default.GOPATH
    74  	//}
    75  	//counterSrc, err := filepath.Abs(gopath + "/src/github.com/ava-labs/coreth/examples/multicoin/mc_test.sol")
    76  	//if err != nil {
    77  	// 	t.Fatal(err)
    78  	// }
    79  	//contracts, err := compiler.CompileSolidity("", counterSrc)
    80  	//if err != nil {
    81  	// 	t.Fatal(err)
    82  	// }
    83  	//contract, _ := contracts[fmt.Sprintf("%s:%s", counterSrc, "MCTest")]
    84  	// abiStr, err := json.Marshal(contract.Info.AbiDefinition)
    85  	// contractAbi, err := abi.JSON(strings.NewReader(string(abiStr)))
    86  	// if err != nil {
    87  	// 	t.Fatal(err)
    88  	// }
    89  	// code := common.Hex2Bytes(contract.Code[2:])
    90  
    91  	// see `mc_test.sol`
    92  	contract := "608060405234801561001057600080fd5b50610426806100206000396000f3fe60806040526004361061002d5760003560e01c8063a41fe49f14610039578063ba7b37d41461008857610034565b3661003457005b600080fd5b34801561004557600080fd5b506100866004803603606081101561005c57600080fd5b810190808035906020019092919080359060200190929190803590602001909291905050506100c3565b005b34801561009457600080fd5b506100c1600480360360208110156100ab57600080fd5b810190808035906020019092919050505061025a565b005b600073010000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1633858585604051602401808573ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018381526020018281526020019450505050506040516020818303038152906040527fb6510bb3000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040518082805190602001908083835b602083106101df57805182526020820191506020810190506020830392506101bc565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114610241576040519150601f19603f3d011682016040523d82523d6000602084013e610246565b606091505b505090508061025457600080fd5b50505050565b60008073010000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1683604051602401808281526020019150506040516020818303038152906040527f1e010439000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040518082805190602001908083835b602083106103495780518252602082019150602081019050602083039250610326565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146103ab576040519150601f19603f3d011682016040523d82523d6000602084013e6103b0565b606091505b5091509150816103bf57600080fd5b8080602001905160208110156103d457600080fd5b810190808051906020019092919050505060008190555050505056fea26469706673582212207931f8bf71bbaeaffac554cafb419604155328b1466fae52488964ccba082f5464736f6c63430007060033"
    93  	contractAbi, err := abi.JSON(strings.NewReader(`[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"coinid","type":"uint256"}],"name":"updateBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"coinid","type":"uint256"},{"internalType":"uint256","name":"amount2","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]`))
    94  	if err != nil {
    95  		t.Fatal(err)
    96  	}
    97  	code := common.Hex2Bytes(contract)
    98  
    99  	var (
   100  		chain *ETHChain
   101  	)
   102  	chain, err = NewETHChain(
   103  		&config,
   104  		&node.Config{},
   105  		rawdb.NewMemoryDatabase(),
   106  		eth.DefaultSettings,
   107  		new(dummy.ConsensusCallbacks),
   108  		common.Hash{},
   109  	)
   110  	if err != nil {
   111  		t.Fatal(err)
   112  	}
   113  
   114  	newTxPoolHeadChan := make(chan core.NewTxPoolHeadEvent, 1)
   115  	log.Info(chain.GetGenesisBlock().Hash().Hex())
   116  
   117  	mcAbi, err := abi.JSON(strings.NewReader(mcAbiJSON))
   118  	if err != nil {
   119  		t.Fatal(err)
   120  	}
   121  
   122  	// start the chain
   123  	chain.GetTxPool().SubscribeNewHeadEvent(newTxPoolHeadChan)
   124  	txSubmitCh := chain.GetTxSubmitCh()
   125  	chain.Start()
   126  
   127  	nonce := uint64(0)
   128  	tx := types.NewContractCreation(nonce, big.NewInt(0), uint64(gasLimit), gasPrice, code)
   129  	signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), genKey.PrivateKey)
   130  	if err != nil {
   131  		t.Fatal(err)
   132  	}
   133  	for _, err := range chain.AddRemoteTxs([]*types.Transaction{signedTx}) {
   134  		if err != nil {
   135  			t.Fatal(err)
   136  		}
   137  	}
   138  	<-txSubmitCh
   139  	nonce++
   140  	block, err := chain.GenerateBlock()
   141  	if err != nil {
   142  		t.Fatal(err)
   143  	}
   144  	insertAndAccept(t, chain, block)
   145  
   146  	<-newTxPoolHeadChan
   147  	log.Info("Generated block with new counter contract creation", "blkNumber", block.NumberU64())
   148  
   149  	if txs := block.Transactions(); len(txs) != 1 {
   150  		t.Fatalf("Expected new block to contain 1 transaction, but found %d", len(txs))
   151  	}
   152  	receipts := chain.GetReceiptsByHash(block.Hash())
   153  	if len(receipts) != 1 {
   154  		t.Fatalf("Expected length of receipts to be 1, but found %d", len(receipts))
   155  	}
   156  	contractAddr := receipts[0].ContractAddress
   157  
   158  	// give Bob some initial balance
   159  	tx = types.NewTransaction(nonce, bobKey.Address, big.NewInt(300000000000000000), uint64(gasLimit), gasPrice, nil)
   160  	signedTx, err = types.SignTx(tx, types.NewEIP155Signer(chainID), genKey.PrivateKey)
   161  	if err != nil {
   162  		t.Fatal(err)
   163  	}
   164  	chain.AddRemoteTxs([]*types.Transaction{signedTx})
   165  	nonce++
   166  	<-txSubmitCh
   167  	block, err = chain.GenerateBlock()
   168  	if err != nil {
   169  		t.Fatal(err)
   170  	}
   171  	insertAndAccept(t, chain, block)
   172  
   173  	// Await block generation
   174  	<-newTxPoolHeadChan
   175  	if txs := block.Transactions(); len(txs) != 1 {
   176  		t.Fatalf("Expected new block to contain 1 transaction, but found %d", len(txs))
   177  	}
   178  
   179  	bobTransferInput, err := mcAbi.Pack("transfer", bobKey.Address, big.NewInt(0), big.NewInt(0), big.NewInt(100000000000000000))
   180  	if err != nil {
   181  		t.Fatal(err)
   182  	}
   183  	contractTransferInput, err := mcAbi.Pack("transfer", contractAddr, big.NewInt(0), big.NewInt(0), big.NewInt(100000000000000000))
   184  	if err != nil {
   185  		t.Fatal(err)
   186  	}
   187  
   188  	// send 5 * 100000000000000000 to Bob
   189  	// send 5 * 100000000000000000 to the contract
   190  	for i := 0; i < 5; i++ {
   191  		// transfer some coin0 balance to Bob
   192  		tx1 := types.NewTransaction(nonce, vm.BuiltinAddr, big.NewInt(0), uint64(gasLimit), gasPrice, bobTransferInput)
   193  		signedTx1, err := types.SignTx(tx1, types.NewEIP155Signer(chainID), genKey.PrivateKey)
   194  		if err != nil {
   195  			t.Fatal(err)
   196  		}
   197  		nonce++
   198  
   199  		// transfer some coin0 balance to the contract
   200  		tx2 := types.NewTransaction(nonce, vm.BuiltinAddr, big.NewInt(0), uint64(gasLimit), gasPrice, contractTransferInput)
   201  		signedTx2, err := types.SignTx(tx2, types.NewEIP155Signer(chainID), genKey.PrivateKey)
   202  		if err != nil {
   203  			t.Fatal(err)
   204  		}
   205  		nonce++
   206  
   207  		for _, err := range chain.AddRemoteTxs([]*types.Transaction{signedTx1, signedTx2}) {
   208  			if err != nil {
   209  				t.Fatal(err)
   210  			}
   211  		}
   212  
   213  		<-txSubmitCh
   214  		block, err := chain.GenerateBlock()
   215  		if err != nil {
   216  			t.Fatal(err)
   217  		}
   218  		insertAndAccept(t, chain, block)
   219  
   220  		<-newTxPoolHeadChan
   221  		if txs := block.Transactions(); len(txs) != 2 {
   222  			t.Fatalf("Expected block to contain 2 transactions, but found %d", len(txs))
   223  		}
   224  	}
   225  
   226  	// test contract methods
   227  	// withdraw 10000000000000000 from contract to Bob
   228  	input, err := contractAbi.Pack("withdraw", big.NewInt(0), big.NewInt(0), big.NewInt(10000000000000000))
   229  	if err != nil {
   230  		t.Fatal(err)
   231  	}
   232  	withdrawTx := types.NewTransaction(nonce, contractAddr, big.NewInt(0), uint64(gasLimit), gasPrice, input)
   233  	signedWithdrawTx, err := types.SignTx(withdrawTx, types.NewEIP155Signer(chainID), genKey.PrivateKey)
   234  	if err != nil {
   235  		t.Fatal(err)
   236  	}
   237  	nonce++
   238  
   239  	input, err = contractAbi.Pack("updateBalance", big.NewInt(0))
   240  	if err != nil {
   241  		t.Fatal(err)
   242  	}
   243  	updateBalanceTx := types.NewTransaction(nonce, contractAddr, big.NewInt(0), uint64(gasLimit), gasPrice, input)
   244  	signedUpdateBalanceTx, err := types.SignTx(updateBalanceTx, types.NewEIP155Signer(chainID), genKey.PrivateKey)
   245  	if err != nil {
   246  		t.Fatal(err)
   247  	}
   248  	chain.AddRemoteTxs([]*types.Transaction{signedWithdrawTx, signedUpdateBalanceTx})
   249  
   250  	<-txSubmitCh
   251  	block, err = chain.GenerateBlock()
   252  	if err != nil {
   253  		t.Fatal(err)
   254  	}
   255  	insertAndAccept(t, chain, block)
   256  
   257  	<-newTxPoolHeadChan
   258  
   259  	if txs := block.Transactions(); len(txs) != 2 {
   260  		t.Fatalf("Expected new block to contain 2 transaction, but found %d", len(txs))
   261  	}
   262  
   263  	coin0 := common.HexToHash("0x0")
   264  	state, err := chain.CurrentState()
   265  	if err != nil {
   266  		t.Fatal(err)
   267  	}
   268  
   269  	genMCBalance := state.GetBalanceMultiCoin(genKey.Address, coin0)
   270  	bobMCBalance := state.GetBalanceMultiCoin(bobKey.Address, coin0)
   271  	contractMCBalance := state.GetBalanceMultiCoin(contractAddr, coin0)
   272  
   273  	log.Info(fmt.Sprintf("genesis balance = %s", state.GetBalance(genKey.Address)))
   274  	log.Info(fmt.Sprintf("genesis mcbalance(0) = %s", genMCBalance))
   275  	log.Info(fmt.Sprintf("bob's balance = %s", state.GetBalance(bobKey.Address)))
   276  	log.Info(fmt.Sprintf("bob's mcbalance(0) = %s", bobMCBalance))
   277  	log.Info(fmt.Sprintf("contract mcbalance(0) = %s", contractMCBalance))
   278  
   279  	if genMCBalance.Cmp(big.NewInt(10000000000000000)) != 0 {
   280  		t.Fatal("incorrect genesis MC balance")
   281  	}
   282  	if bobMCBalance.Cmp(big.NewInt(500000000000000000)) != 0 {
   283  		t.Fatal("incorrect bob's MC balance")
   284  	}
   285  	if contractMCBalance.Cmp(big.NewInt(490000000000000000)) != 0 {
   286  		t.Fatal("incorrect contract's MC balance")
   287  	}
   288  }