package tests import ( "context" "crypto/ecdsa" "fmt" "math/big" "testing" "github.com/ledgerwatch/turbo-geth/accounts/abi/bind" "github.com/ledgerwatch/turbo-geth/accounts/abi/bind/backends" "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/consensus/ethash" "github.com/ledgerwatch/turbo-geth/core" "github.com/ledgerwatch/turbo-geth/core/types" "github.com/ledgerwatch/turbo-geth/core/vm" "github.com/ledgerwatch/turbo-geth/crypto" "github.com/ledgerwatch/turbo-geth/ethdb" "github.com/ledgerwatch/turbo-geth/params" "github.com/ledgerwatch/turbo-geth/tests/contracts" ) func TestInsertIncorrectStateRootDifferentAccounts(t *testing.T) { data := getGenesis() from := data.addresses[0] fromKey := data.keys[0] to := common.Address{1} blockchain, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{ 0: { getBlockTx(from, to, big.NewInt(1000)), fromKey, }, 1: { getBlockTx(from, to, big.NewInt(2000)), fromKey, }, }) if err != nil { t.Fatal(err) } // BLOCK 1 incorrectHeader := blocks[0].Header() incorrectHeader.Root = blocks[1].Header().Root if blocks[0].Header().Root == incorrectHeader.Root { t.Fatal("roots are the same") } incorrectBlock := types.NewBlock(incorrectHeader, blocks[0].Transactions(), blocks[0].Uncles(), receipts[0]) if _, err = blockchain.InsertChain(types.Blocks{incorrectBlock}); err == nil { t.Fatal("should fail") } // insert a correct block blockchain, blocks, _, err = genBlocks(data.genesisSpec, map[int]tx{ 0: { getBlockTx(data.addresses[1], to, big.NewInt(5000)), data.keys[1], }, }) if err != nil { t.Fatal(err) } if _, err = blockchain.InsertChain(blocks); err != nil { t.Fatal(err) } st, _, _ := blockchain.State() if !st.Exist(to) { t.Error("expected account to exist") } if balance := st.GetBalance(from); balance.Cmp(big.NewInt(1000000000)) != 0 { t.Fatalf("got %v, expected %v", balance, big.NewInt(1000000000)) } if balance := st.GetBalance(data.addresses[1]); balance.Cmp(big.NewInt(999995000)) != 0 { t.Fatalf("got %v, expected %v", balance, big.NewInt(5000)) } if balance := st.GetBalance(to); balance.Cmp(big.NewInt(5000)) != 0 { t.Fatalf("got %v, expected %v", balance, big.NewInt(5000)) } } func TestInsertIncorrectStateRootSameAccount(t *testing.T) { data := getGenesis() from := data.addresses[0] fromKey := data.keys[0] to := common.Address{1} blockchain, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{ 0: { getBlockTx(from, to, big.NewInt(1000)), fromKey, }, 1: { getBlockTx(from, to, big.NewInt(2000)), fromKey, }, }) if err != nil { t.Fatal(err) } // BLOCK 1 incorrectHeader := blocks[0].Header() incorrectHeader.Root = blocks[1].Header().Root if blocks[0].Header().Root == incorrectHeader.Root { t.Fatal("roots are the same") } incorrectBlock := types.NewBlock(incorrectHeader, blocks[0].Transactions(), blocks[0].Uncles(), receipts[0]) if _, err = blockchain.InsertChain(types.Blocks{incorrectBlock}); err == nil { t.Fatal("should fail") } // insert a correct block blockchain, blocks, _, err = genBlocks(data.genesisSpec, map[int]tx{ 0: { getBlockTx(from, to, big.NewInt(5000)), fromKey, }, }) if err != nil { t.Fatal(err) } if _, err = blockchain.InsertChain(blocks); err != nil { t.Fatal(err) } st, _, _ := blockchain.State() if !st.Exist(to) { t.Error("expected account to exist") } if balance := st.GetBalance(from); balance.Cmp(big.NewInt(999995000)) != 0 { t.Fatalf("got %v, expected %v", balance, big.NewInt(999995000)) } if balance := st.GetBalance(to); balance.Cmp(big.NewInt(5000)) != 0 { t.Fatalf("got %v, expected %v", balance, big.NewInt(5000)) } } func TestInsertIncorrectStateRootSameAccountSameAmount(t *testing.T) { data := getGenesis() from := data.addresses[0] fromKey := data.keys[0] to := common.Address{1} blockchain, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{ 0: { getBlockTx(from, to, big.NewInt(1000)), fromKey, }, 1: { getBlockTx(from, to, big.NewInt(2000)), fromKey, }, }) if err != nil { t.Fatal(err) } // BLOCK 1 incorrectHeader := blocks[0].Header() incorrectHeader.Root = blocks[1].Header().Root incorrectBlock := types.NewBlock(incorrectHeader, blocks[0].Transactions(), blocks[0].Uncles(), receipts[0]) if _, err = blockchain.InsertChain(types.Blocks{incorrectBlock}); err == nil { t.Fatal("should fail") } // insert a correct block blockchain, blocks, _, err = genBlocks(data.genesisSpec, map[int]tx{ 0: { getBlockTx(from, to, big.NewInt(1000)), fromKey, }, }) if err != nil { t.Fatal(err) } if _, err = blockchain.InsertChain(blocks); err != nil { t.Fatal(err) } st, _, _ := blockchain.State() if !st.Exist(to) { t.Error("expected account to exist") } if balance := st.GetBalance(from); balance.Cmp(big.NewInt(999999000)) != 0 { t.Fatalf("got %v, expected %v", balance, big.NewInt(999999000)) } if balance := st.GetBalance(to); balance.Cmp(big.NewInt(1000)) != 0 { t.Fatalf("got %v, expected %v", balance, big.NewInt(1000)) } } func TestInsertIncorrectStateRootAllFundsRoot(t *testing.T) { data := getGenesis(big.NewInt(3000)) from := data.addresses[0] fromKey := data.keys[0] to := common.Address{1} blockchain, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{ 0: { getBlockTx(from, to, big.NewInt(1000)), fromKey, }, 1: { getBlockTx(from, to, big.NewInt(2000)), fromKey, }, }) if err != nil { t.Fatal(err) } // BLOCK 1 incorrectHeader := blocks[0].Header() incorrectHeader.Root = blocks[1].Header().Root incorrectBlock := types.NewBlock(incorrectHeader, blocks[0].Transactions(), blocks[0].Uncles(), receipts[0]) if _, err = blockchain.InsertChain(types.Blocks{incorrectBlock}); err == nil { t.Fatal("should fail") } // insert a correct block blockchain, blocks, _, err = genBlocks(data.genesisSpec, map[int]tx{ 0: { getBlockTx(from, to, big.NewInt(1000)), fromKey, }, }) if err != nil { t.Fatal(err) } if _, err = blockchain.InsertChain(blocks); err != nil { t.Fatal(err) } st, _, _ := blockchain.State() if !st.Exist(to) { t.Error("expected account to exist") } if balance := st.GetBalance(from); balance.Cmp(big.NewInt(2000)) != 0 { t.Fatalf("got %v, expected %v", balance, big.NewInt(2000)) } if balance := st.GetBalance(to); balance.Cmp(big.NewInt(1000)) != 0 { t.Fatalf("got %v, expected %v", balance, big.NewInt(1000)) } } func TestInsertIncorrectStateRootAllFunds(t *testing.T) { data := getGenesis(big.NewInt(3000)) from := data.addresses[0] fromKey := data.keys[0] to := common.Address{1} blockchain, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{ 0: { getBlockTx(from, to, big.NewInt(3000)), fromKey, }, 1: { getBlockTx(data.addresses[1], to, big.NewInt(2000)), data.keys[1], }, }) if err != nil { t.Fatal(err) } // BLOCK 1 incorrectHeader := blocks[0].Header() incorrectHeader.Root = blocks[1].Header().Root incorrectBlock := types.NewBlock(incorrectHeader, blocks[0].Transactions(), blocks[0].Uncles(), receipts[0]) if _, err = blockchain.InsertChain(types.Blocks{incorrectBlock}); err == nil { t.Fatal("should fail") } // insert a correct block blockchain, blocks, _, err = genBlocks(data.genesisSpec, map[int]tx{ 0: { getBlockTx(from, to, big.NewInt(1000)), fromKey, }, }) if err != nil { t.Fatal(err) } if _, err = blockchain.InsertChain(blocks); err != nil { t.Fatal(err) } st, _, _ := blockchain.State() if !st.Exist(to) { t.Error("expected account to exist") } if balance := st.GetBalance(from); balance.Cmp(big.NewInt(2000)) != 0 { t.Fatalf("got %v, expected %v", balance, big.NewInt(2000)) } if balance := st.GetBalance(to); balance.Cmp(big.NewInt(1000)) != 0 { t.Fatalf("got %v, expected %v", balance, big.NewInt(1000)) } } func TestAccountDeployIncorrectRoot(t *testing.T) { data := getGenesis() from := data.addresses[0] fromKey := data.keys[0] to := common.Address{1} var contractAddress common.Address eipContract := new(contracts.Eip2027) blockchain, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{ 0: { getBlockTx(from, to, big.NewInt(10)), fromKey, }, 1: { getBlockDeployEip2027Tx(data.transactOpts[0], &contractAddress, eipContract), fromKey, }, }) if err != nil { t.Fatal(err) } // BLOCK 1 if _, err = blockchain.InsertChain(types.Blocks{blocks[0]}); err != nil { t.Fatal(err) } st, _, _ := blockchain.State() if !st.Exist(from) { t.Error("expected account to exist") } if st.Exist(contractAddress) { t.Error("expected contractAddress to not exist at the block 0", contractAddress.Hash().String()) } incorrectHeader := blocks[1].Header() incorrectHeader.Root = blocks[0].Header().Root incorrectBlock := types.NewBlock(incorrectHeader, blocks[1].Transactions(), blocks[1].Uncles(), receipts[1]) // BLOCK 2 - INCORRECT if _, err = blockchain.InsertChain(types.Blocks{incorrectBlock}); err == nil { t.Fatal("should fail") } st, _, _ = blockchain.State() if !st.Exist(from) { t.Error("expected account to exist") } if st.Exist(contractAddress) { t.Error("expected contractAddress to not exist at the block 1", contractAddress.Hash().String()) } // BLOCK 2 - CORRECT if _, err = blockchain.InsertChain(types.Blocks{blocks[1]}); err != nil { t.Fatal(err) } st, _, _ = blockchain.State() if !st.Exist(from) { t.Error("expected account to exist") } if !st.Exist(contractAddress) { t.Error("expected contractAddress to not exist at the block 1", contractAddress.Hash().String()) } } func TestAccountCreateIncorrectRoot(t *testing.T) { data := getGenesis() from := data.addresses[0] fromKey := data.keys[0] to := common.Address{1} var contractAddress common.Address eipContract := new(contracts.Eip2027) blockchain, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{ 0: { getBlockTx(from, to, big.NewInt(10)), fromKey, }, 1: { getBlockDeployEip2027Tx(data.transactOpts[0], &contractAddress, eipContract), fromKey, }, 2: { getBlockEip2027Tx(data.transactOpts[0], eipContract.Create, big.NewInt(2)), fromKey, }, }) if err != nil { t.Fatal(err) } // BLOCK 1 if _, err = blockchain.InsertChain(types.Blocks{blocks[0]}); err != nil { t.Fatal(err) } st, _, _ := blockchain.State() if !st.Exist(from) { t.Error("expected account to exist") } if st.Exist(contractAddress) { t.Error("expected contractAddress to not exist at the block 0", contractAddress.Hash().String()) } // BLOCK 2 if _, err = blockchain.InsertChain(types.Blocks{blocks[1]}); err != nil { t.Fatal(err) } st, _, _ = blockchain.State() if !st.Exist(from) { t.Error("expected account to exist") } if !st.Exist(contractAddress) { t.Error("expected contractAddress to exist at the block 2", contractAddress.Hash().String()) } // BLOCK 3 - INCORRECT incorrectHeader := blocks[2].Header() incorrectHeader.Root = blocks[1].Header().Root incorrectBlock := types.NewBlock(incorrectHeader, blocks[2].Transactions(), blocks[2].Uncles(), receipts[2]) if _, err = blockchain.InsertChain(types.Blocks{incorrectBlock}); err == nil { t.Fatal("should fail") } // BLOCK 3 if _, err = blockchain.InsertChain(types.Blocks{blocks[2]}); err != nil { t.Fatal(err) } } func TestAccountUpdateIncorrectRoot(t *testing.T) { data := getGenesis() from := data.addresses[0] fromKey := data.keys[0] to := common.Address{1} var contractAddress common.Address eipContract := new(contracts.Eip2027) blockchain, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{ 0: { getBlockTx(from, to, big.NewInt(10)), fromKey, }, 1: { getBlockDeployEip2027Tx(data.transactOpts[0], &contractAddress, eipContract), fromKey, }, 2: { getBlockEip2027Tx(data.transactOpts[0], eipContract.Create, big.NewInt(2)), fromKey, }, 3: { getBlockEip2027Tx(data.transactOpts[0], eipContract.Update, big.NewInt(0)), fromKey, }, }) if err != nil { t.Fatal(err) } // BLOCK 1 if _, err = blockchain.InsertChain(types.Blocks{blocks[0]}); err != nil { t.Fatal(err) } st, _, _ := blockchain.State() if !st.Exist(from) { t.Error("expected account to exist") } if st.Exist(contractAddress) { t.Error("expected contractAddress to not exist at the block 0", contractAddress.Hash().String()) } // BLOCK 2 if _, err = blockchain.InsertChain(types.Blocks{blocks[1]}); err != nil { t.Fatal(err) } st, _, _ = blockchain.State() if !st.Exist(from) { t.Error("expected account to exist") } if !st.Exist(contractAddress) { t.Error("expected contractAddress to exist at the block 2", contractAddress.Hash().String()) } // BLOCK 3 if _, err = blockchain.InsertChain(types.Blocks{blocks[2]}); err != nil { t.Fatal(err) } // BLOCK 4 - INCORRECT incorrectHeader := blocks[3].Header() incorrectHeader.Root = blocks[1].Header().Root incorrectBlock := types.NewBlock(incorrectHeader, blocks[3].Transactions(), blocks[3].Uncles(), receipts[3]) if _, err = blockchain.InsertChain(types.Blocks{incorrectBlock}); err == nil { t.Fatal("should fail") } // BLOCK 4 if _, err = blockchain.InsertChain(types.Blocks{blocks[3]}); err != nil { t.Fatal(err) } } func TestAccountDeleteIncorrectRoot(t *testing.T) { data := getGenesis() from := data.addresses[0] fromKey := data.keys[0] to := common.Address{1} var contractAddress common.Address eipContract := new(contracts.Eip2027) blockchain, blocks, receipts, err := genBlocks(data.genesisSpec, map[int]tx{ 0: { getBlockTx(from, to, big.NewInt(10)), fromKey, }, 1: { getBlockDeployEip2027Tx(data.transactOpts[0], &contractAddress, eipContract), fromKey, }, 2: { getBlockEip2027Tx(data.transactOpts[0], eipContract.Create, big.NewInt(2)), fromKey, }, 3: { getBlockEip2027Tx(data.transactOpts[0], eipContract.Remove), fromKey, }, }) if err != nil { t.Fatal(err) } // BLOCK 1 if _, err = blockchain.InsertChain(types.Blocks{blocks[0]}); err != nil { t.Fatal(err) } st, _, _ := blockchain.State() if !st.Exist(from) { t.Error("expected account to exist") } if st.Exist(contractAddress) { t.Error("expected contractAddress to not exist at the block 0", contractAddress.Hash().String()) } // BLOCK 2 if _, err = blockchain.InsertChain(types.Blocks{blocks[1]}); err != nil { t.Fatal(err) } st, _, _ = blockchain.State() if !st.Exist(from) { t.Error("expected account to exist") } if !st.Exist(contractAddress) { t.Error("expected contractAddress to exist at the block 1", contractAddress.Hash().String()) } // BLOCK 3 if _, err = blockchain.InsertChain(types.Blocks{blocks[2]}); err != nil { t.Fatal(err) } // BLOCK 4 - INCORRECT incorrectHeader := blocks[3].Header() incorrectHeader.Root = blocks[1].Header().Root incorrectBlock := types.NewBlock(incorrectHeader, blocks[3].Transactions(), blocks[3].Uncles(), receipts[3]) if _, err = blockchain.InsertChain(types.Blocks{incorrectBlock}); err == nil { t.Fatal("should fail") } // BLOCK 4 if _, err = blockchain.InsertChain(types.Blocks{blocks[3]}); err != nil { t.Fatal(err) } } type initialData struct { keys []*ecdsa.PrivateKey addresses []common.Address transactOpts []*bind.TransactOpts genesisSpec *core.Genesis } func getGenesis(funds ...*big.Int) initialData { accountFunds := big.NewInt(1000000000) if len(funds) > 0 { accountFunds = funds[0] } keys := make([]*ecdsa.PrivateKey, 3) keys[0], _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") keys[1], _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") keys[2], _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") addresses := make([]common.Address, 0, len(keys)) transactOpts := make([]*bind.TransactOpts, 0, len(keys)) allocs := core.GenesisAlloc{} for _, key := range keys { addr := crypto.PubkeyToAddress(key.PublicKey) addresses = append(addresses, addr) transactOpts = append(transactOpts, bind.NewKeyedTransactor(key)) allocs[addr] = core.GenesisAccount{Balance: accountFunds} } return initialData{ keys: keys, addresses: addresses, transactOpts: transactOpts, genesisSpec: &core.Genesis{ Config: ¶ms.ChainConfig{ ChainID: big.NewInt(1), HomesteadBlock: new(big.Int), EIP155Block: new(big.Int), EIP158Block: big.NewInt(1), ConstantinopleBlock: big.NewInt(1), }, Alloc: allocs, }, } } type tx struct { txFn blockTx key *ecdsa.PrivateKey } func genBlocks(gspec *core.Genesis, txs map[int]tx) (*core.BlockChain, []*types.Block, []types.Receipts, error) { engine := ethash.NewFaker() db := ethdb.NewMemDatabase() genesis := gspec.MustCommit(db) genesisDb := db.MemCopy() blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil) if err != nil { return nil, nil, nil, err } blockchain.EnableReceipts(true) contractBackend := backends.NewSimulatedBackendWithConfig(gspec.Alloc, gspec.Config, gspec.GasLimit) ctx := blockchain.WithContext(context.Background(), big.NewInt(genesis.Number().Int64()+1)) var blockNumber int blocks, receipts := core.GenerateChain(ctx, gspec.Config, genesis, engine, genesisDb, len(txs), func(i int, block *core.BlockGen) { var tx *types.Transaction var isContractCall bool blockNumber = i signer := types.HomesteadSigner{} ctx = gspec.Config.WithEIPsFlags(ctx, block.Number()) if txToSend, ok := txs[i]; ok { tx, isContractCall = txToSend.txFn(block, contractBackend) tx, err = types.SignTx(tx, signer, txToSend.key) if err != nil { return } } if tx != nil { if !isContractCall { err = contractBackend.SendTransaction(ctx, tx) if err != nil { return } } block.AddTx(tx) } contractBackend.Commit() }) if err != nil { err = fmt.Errorf("block %d, error %w", blockNumber, err) } return blockchain, blocks, receipts, err } type blockTx func(_ *core.BlockGen, backend bind.ContractBackend) (*types.Transaction, bool) func getBlockTx(from common.Address, to common.Address, amount *big.Int) blockTx { return func(block *core.BlockGen, _ bind.ContractBackend) (*types.Transaction, bool) { return types.NewTransaction(block.TxNonce(from), to, amount, 21000, new(big.Int), nil), false } } func getBlockDeployEip2027Tx(transactOpts *bind.TransactOpts, contractAddress *common.Address, eipContract *contracts.Eip2027) blockTx { return func(_ *core.BlockGen, backend bind.ContractBackend) (*types.Transaction, bool) { contractAddressRes, tx, eipContractRes, err := contracts.DeployEip2027(transactOpts, backend) if err != nil { panic(err) } *contractAddress = contractAddressRes *eipContract = *eipContractRes return tx, true } } func getBlockEip2027Tx(transactOpts *bind.TransactOpts, contractCall interface{}, newBalance ...*big.Int) blockTx { return func(_ *core.BlockGen, backend bind.ContractBackend) (*types.Transaction, bool) { var ( tx *types.Transaction err error ) switch fn := contractCall.(type) { case func(opts *bind.TransactOpts) (*types.Transaction, error): tx, err = fn(transactOpts) case func(opts *bind.TransactOpts, newBalance *big.Int) (*types.Transaction, error): if len(newBalance) != 1 { panic("*big.Int type new balance is expected") } tx, err = fn(transactOpts, newBalance[0]) default: panic("non expected function type") } if err != nil { panic(err) } return tx, true } }