|
| 1 | +// Copyright 2020 The go-ethereum Authors |
| 2 | +// This file is part of the go-ethereum library. |
| 3 | +// |
| 4 | +// The go-ethereum library is free software: you can redistribute it and/or modify |
| 5 | +// it under the terms of the GNU Lesser General Public License as published by |
| 6 | +// the Free Software Foundation, either version 3 of the License, or |
| 7 | +// (at your option) any later version. |
| 8 | +// |
| 9 | +// The go-ethereum library is distributed in the hope that it will be useful, |
| 10 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | +// GNU Lesser General Public License for more details. |
| 13 | +// |
| 14 | +// You should have received a copy of the GNU Lesser General Public License |
| 15 | +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. |
| 16 | + |
| 17 | +package core |
| 18 | + |
| 19 | +import ( |
| 20 | + "math/big" |
| 21 | + "testing" |
| 22 | + |
| 23 | + "github.com/ethereum/go-ethereum/common" |
| 24 | + "github.com/ethereum/go-ethereum/consensus" |
| 25 | + "github.com/ethereum/go-ethereum/consensus/ethash" |
| 26 | + "github.com/ethereum/go-ethereum/core/rawdb" |
| 27 | + "github.com/ethereum/go-ethereum/core/types" |
| 28 | + "github.com/ethereum/go-ethereum/core/vm" |
| 29 | + "github.com/ethereum/go-ethereum/crypto" |
| 30 | + "github.com/ethereum/go-ethereum/params" |
| 31 | + "github.com/ethereum/go-ethereum/trie" |
| 32 | + "golang.org/x/crypto/sha3" |
| 33 | +) |
| 34 | + |
| 35 | +// TestStateProcessorErrors tests the output from the 'core' errors |
| 36 | +// as defined in core/error.go. These errors are generated when the |
| 37 | +// blockchain imports bad blocks, meaning blocks which have valid headers but |
| 38 | +// contain invalid transactions |
| 39 | +func TestStateProcessorErrors(t *testing.T) { |
| 40 | + var ( |
| 41 | + signer = types.HomesteadSigner{} |
| 42 | + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") |
| 43 | + db = rawdb.NewMemoryDatabase() |
| 44 | + gspec = &Genesis{ |
| 45 | + Config: params.TestChainConfig, |
| 46 | + } |
| 47 | + genesis = gspec.MustCommit(db) |
| 48 | + blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) |
| 49 | + ) |
| 50 | + defer blockchain.Stop() |
| 51 | + var makeTx = func(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *types.Transaction { |
| 52 | + tx, _ := types.SignTx(types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data), signer, testKey) |
| 53 | + return tx |
| 54 | + } |
| 55 | + for i, tt := range []struct { |
| 56 | + txs []*types.Transaction |
| 57 | + want string |
| 58 | + }{ |
| 59 | + { |
| 60 | + txs: []*types.Transaction{ |
| 61 | + makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, nil, nil), |
| 62 | + makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, nil, nil), |
| 63 | + }, |
| 64 | + want: "could not apply tx 1 [0x36bfa6d14f1cd35a1be8cc2322982a595fabc0e799f09c1de3bad7bd5b1f7626]: nonce too low: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tx: 0 state: 1", |
| 65 | + }, |
| 66 | + { |
| 67 | + txs: []*types.Transaction{ |
| 68 | + makeTx(100, common.Address{}, big.NewInt(0), params.TxGas, nil, nil), |
| 69 | + }, |
| 70 | + want: "could not apply tx 0 [0x51cd272d41ef6011d8138e18bf4043797aca9b713c7d39a97563f9bbe6bdbe6f]: nonce too high: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tx: 100 state: 0", |
| 71 | + }, |
| 72 | + { |
| 73 | + txs: []*types.Transaction{ |
| 74 | + makeTx(0, common.Address{}, big.NewInt(0), 21000000, nil, nil), |
| 75 | + }, |
| 76 | + want: "could not apply tx 0 [0x54c58b530824b0bb84b7a98183f08913b5d74e1cebc368515ef3c65edf8eb56a]: gas limit reached", |
| 77 | + }, |
| 78 | + { |
| 79 | + txs: []*types.Transaction{ |
| 80 | + makeTx(0, common.Address{}, big.NewInt(1), params.TxGas, nil, nil), |
| 81 | + }, |
| 82 | + want: "could not apply tx 0 [0x3094b17498940d92b13baccf356ce8bfd6f221e926abc903d642fa1466c5b50e]: insufficient funds for transfer: address 0x71562b71999873DB5b286dF957af199Ec94617F7", |
| 83 | + }, |
| 84 | + { |
| 85 | + txs: []*types.Transaction{ |
| 86 | + makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(0xffffff), nil), |
| 87 | + }, |
| 88 | + want: "could not apply tx 0 [0xaa3f7d86802b1f364576d9071bf231e31d61b392d306831ac9cf706ff5371ce0]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 0 want 352321515000", |
| 89 | + }, |
| 90 | + { |
| 91 | + txs: []*types.Transaction{ |
| 92 | + makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, nil, nil), |
| 93 | + makeTx(1, common.Address{}, big.NewInt(0), params.TxGas, nil, nil), |
| 94 | + makeTx(2, common.Address{}, big.NewInt(0), params.TxGas, nil, nil), |
| 95 | + makeTx(3, common.Address{}, big.NewInt(0), params.TxGas-1000, big.NewInt(0), nil), |
| 96 | + }, |
| 97 | + want: "could not apply tx 3 [0x836fab5882205362680e49b311a20646de03b630920f18ec6ee3b111a2cf6835]: intrinsic gas too low: have 20000, want 21000", |
| 98 | + }, |
| 99 | + // The last 'core' error is ErrGasUintOverflow: "gas uint64 overflow", but in order to |
| 100 | + // trigger that one, we'd have to allocate a _huge_ chunk of data, such that the |
| 101 | + // multiplication len(data) +gas_per_byte overflows uint64. Not testable at the moment |
| 102 | + } { |
| 103 | + block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs) |
| 104 | + _, err := blockchain.InsertChain(types.Blocks{block}) |
| 105 | + if err == nil { |
| 106 | + t.Fatal("block imported without errors") |
| 107 | + } |
| 108 | + if have, want := err.Error(), tt.want; have != want { |
| 109 | + t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want) |
| 110 | + } |
| 111 | + } |
| 112 | +} |
| 113 | + |
| 114 | +// GenerateBadBlock constructs a "block" which contains the transactions. The transactions are not expected to be |
| 115 | +// valid, and no proper post-state can be made. But from the perspective of the blockchain, the block is sufficiently |
| 116 | +// valid to be considered for import: |
| 117 | +// - valid pow (fake), ancestry, difficulty, gaslimit etc |
| 118 | +func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Transactions) *types.Block { |
| 119 | + header := &types.Header{ |
| 120 | + ParentHash: parent.Hash(), |
| 121 | + Coinbase: parent.Coinbase(), |
| 122 | + Difficulty: engine.CalcDifficulty(&fakeChainReader{params.TestChainConfig}, parent.Time()+10, &types.Header{ |
| 123 | + Number: parent.Number(), |
| 124 | + Time: parent.Time(), |
| 125 | + Difficulty: parent.Difficulty(), |
| 126 | + UncleHash: parent.UncleHash(), |
| 127 | + }), |
| 128 | + GasLimit: CalcGasLimit(parent, parent.GasLimit(), parent.GasLimit()), |
| 129 | + Number: new(big.Int).Add(parent.Number(), common.Big1), |
| 130 | + Time: parent.Time() + 10, |
| 131 | + UncleHash: types.EmptyUncleHash, |
| 132 | + } |
| 133 | + var receipts []*types.Receipt |
| 134 | + |
| 135 | + // The post-state result doesn't need to be correct (this is a bad block), but we do need something there |
| 136 | + // Preferably something unique. So let's use a combo of blocknum + txhash |
| 137 | + hasher := sha3.NewLegacyKeccak256() |
| 138 | + hasher.Write(header.Number.Bytes()) |
| 139 | + var cumulativeGas uint64 |
| 140 | + for _, tx := range txs { |
| 141 | + txh := tx.Hash() |
| 142 | + hasher.Write(txh[:]) |
| 143 | + receipt := types.NewReceipt(nil, false, cumulativeGas+tx.Gas()) |
| 144 | + receipt.TxHash = tx.Hash() |
| 145 | + receipt.GasUsed = tx.Gas() |
| 146 | + receipts = append(receipts, receipt) |
| 147 | + cumulativeGas += tx.Gas() |
| 148 | + } |
| 149 | + header.Root = common.BytesToHash(hasher.Sum(nil)) |
| 150 | + // Assemble and return the final block for sealing |
| 151 | + return types.NewBlock(header, txs, nil, receipts, new(trie.Trie)) |
| 152 | +} |
0 commit comments