github.com/MaynardMiner/ethereumprogpow@v1.8.23/tests/block_test_util.go (about) 1 // Copyright 2015 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 tests implements execution of Ethereum JSON tests. 18 package tests 19 20 import ( 21 "bytes" 22 "encoding/hex" 23 "encoding/json" 24 "fmt" 25 "math/big" 26 27 "github.com/ethereumprogpow/ethereumprogpow/common" 28 "github.com/ethereumprogpow/ethereumprogpow/common/hexutil" 29 "github.com/ethereumprogpow/ethereumprogpow/common/math" 30 "github.com/ethereumprogpow/ethereumprogpow/consensus" 31 "github.com/ethereumprogpow/ethereumprogpow/consensus/ethash" 32 "github.com/ethereumprogpow/ethereumprogpow/core" 33 "github.com/ethereumprogpow/ethereumprogpow/core/state" 34 "github.com/ethereumprogpow/ethereumprogpow/core/types" 35 "github.com/ethereumprogpow/ethereumprogpow/core/vm" 36 "github.com/ethereumprogpow/ethereumprogpow/ethdb" 37 "github.com/ethereumprogpow/ethereumprogpow/params" 38 "github.com/ethereumprogpow/ethereumprogpow/rlp" 39 ) 40 41 // A BlockTest checks handling of entire blocks. 42 type BlockTest struct { 43 json btJSON 44 } 45 46 // UnmarshalJSON implements json.Unmarshaler interface. 47 func (t *BlockTest) UnmarshalJSON(in []byte) error { 48 return json.Unmarshal(in, &t.json) 49 } 50 51 type btJSON struct { 52 Blocks []btBlock `json:"blocks"` 53 Genesis btHeader `json:"genesisBlockHeader"` 54 Pre core.GenesisAlloc `json:"pre"` 55 Post core.GenesisAlloc `json:"postState"` 56 BestBlock common.UnprefixedHash `json:"lastblockhash"` 57 Network string `json:"network"` 58 SealEngine string `json:"sealEngine"` 59 } 60 61 type btBlock struct { 62 BlockHeader *btHeader 63 Rlp string 64 UncleHeaders []*btHeader 65 } 66 67 //go:generate gencodec -type btHeader -field-override btHeaderMarshaling -out gen_btheader.go 68 69 type btHeader struct { 70 Bloom types.Bloom 71 Coinbase common.Address 72 MixHash common.Hash 73 Nonce types.BlockNonce 74 Number *big.Int 75 Hash common.Hash 76 ParentHash common.Hash 77 ReceiptTrie common.Hash 78 StateRoot common.Hash 79 TransactionsTrie common.Hash 80 UncleHash common.Hash 81 ExtraData []byte 82 Difficulty *big.Int 83 GasLimit uint64 84 GasUsed uint64 85 Timestamp *big.Int 86 } 87 88 type btHeaderMarshaling struct { 89 ExtraData hexutil.Bytes 90 Number *math.HexOrDecimal256 91 Difficulty *math.HexOrDecimal256 92 GasLimit math.HexOrDecimal64 93 GasUsed math.HexOrDecimal64 94 Timestamp *math.HexOrDecimal256 95 } 96 97 func (t *BlockTest) Run() error { 98 config, ok := Forks[t.json.Network] 99 if !ok { 100 return UnsupportedForkError{t.json.Network} 101 } 102 103 // import pre accounts & construct test genesis block & state root 104 db := ethdb.NewMemDatabase() 105 gblock, err := t.genesis(config).Commit(db) 106 if err != nil { 107 return err 108 } 109 if gblock.Hash() != t.json.Genesis.Hash { 110 return fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x", gblock.Hash().Bytes()[:6], t.json.Genesis.Hash[:6]) 111 } 112 if gblock.Root() != t.json.Genesis.StateRoot { 113 return fmt.Errorf("genesis block state root does not match test: computed=%x, test=%x", gblock.Root().Bytes()[:6], t.json.Genesis.StateRoot[:6]) 114 } 115 var engine consensus.Engine 116 if t.json.SealEngine == "NoProof" { 117 engine = ethash.NewFaker() 118 } else { 119 engine = ethash.NewShared(nil) 120 } 121 chain, err := core.NewBlockChain(db, &core.CacheConfig{TrieCleanLimit: 0}, config, engine, vm.Config{}, nil) 122 if err != nil { 123 return err 124 } 125 defer chain.Stop() 126 127 validBlocks, err := t.insertBlocks(chain) 128 if err != nil { 129 return err 130 } 131 cmlast := chain.CurrentBlock().Hash() 132 if common.Hash(t.json.BestBlock) != cmlast { 133 return fmt.Errorf("last block hash validation mismatch: want: %x, have: %x", t.json.BestBlock, cmlast) 134 } 135 newDB, err := chain.State() 136 if err != nil { 137 return err 138 } 139 if err = t.validatePostState(newDB); err != nil { 140 return fmt.Errorf("post state validation failed: %v", err) 141 } 142 return t.validateImportedHeaders(chain, validBlocks) 143 } 144 145 func (t *BlockTest) genesis(config *params.ChainConfig) *core.Genesis { 146 return &core.Genesis{ 147 Config: config, 148 Nonce: t.json.Genesis.Nonce.Uint64(), 149 Timestamp: t.json.Genesis.Timestamp.Uint64(), 150 ParentHash: t.json.Genesis.ParentHash, 151 ExtraData: t.json.Genesis.ExtraData, 152 GasLimit: t.json.Genesis.GasLimit, 153 GasUsed: t.json.Genesis.GasUsed, 154 Difficulty: t.json.Genesis.Difficulty, 155 Mixhash: t.json.Genesis.MixHash, 156 Coinbase: t.json.Genesis.Coinbase, 157 Alloc: t.json.Pre, 158 } 159 } 160 161 /* See https://github.com/ethereum/tests/wiki/Blockchain-Tests-II 162 163 Whether a block is valid or not is a bit subtle, it's defined by presence of 164 blockHeader, transactions and uncleHeaders fields. If they are missing, the block is 165 invalid and we must verify that we do not accept it. 166 167 Since some tests mix valid and invalid blocks we need to check this for every block. 168 169 If a block is invalid it does not necessarily fail the test, if it's invalidness is 170 expected we are expected to ignore it and continue processing and then validate the 171 post state. 172 */ 173 func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error) { 174 validBlocks := make([]btBlock, 0) 175 // insert the test blocks, which will execute all transactions 176 for _, b := range t.json.Blocks { 177 cb, err := b.decode() 178 if err != nil { 179 if b.BlockHeader == nil { 180 continue // OK - block is supposed to be invalid, continue with next block 181 } else { 182 return nil, fmt.Errorf("Block RLP decoding failed when expected to succeed: %v", err) 183 } 184 } 185 // RLP decoding worked, try to insert into chain: 186 blocks := types.Blocks{cb} 187 i, err := blockchain.InsertChain(blocks) 188 if err != nil { 189 if b.BlockHeader == nil { 190 continue // OK - block is supposed to be invalid, continue with next block 191 } else { 192 return nil, fmt.Errorf("Block #%v insertion into chain failed: %v", blocks[i].Number(), err) 193 } 194 } 195 if b.BlockHeader == nil { 196 return nil, fmt.Errorf("Block insertion should have failed") 197 } 198 199 // validate RLP decoding by checking all values against test file JSON 200 if err = validateHeader(b.BlockHeader, cb.Header()); err != nil { 201 return nil, fmt.Errorf("Deserialised block header validation failed: %v", err) 202 } 203 validBlocks = append(validBlocks, b) 204 } 205 return validBlocks, nil 206 } 207 208 func validateHeader(h *btHeader, h2 *types.Header) error { 209 if h.Bloom != h2.Bloom { 210 return fmt.Errorf("Bloom: want: %x have: %x", h.Bloom, h2.Bloom) 211 } 212 if h.Coinbase != h2.Coinbase { 213 return fmt.Errorf("Coinbase: want: %x have: %x", h.Coinbase, h2.Coinbase) 214 } 215 if h.MixHash != h2.MixDigest { 216 return fmt.Errorf("MixHash: want: %x have: %x", h.MixHash, h2.MixDigest) 217 } 218 if h.Nonce != h2.Nonce { 219 return fmt.Errorf("Nonce: want: %x have: %x", h.Nonce, h2.Nonce) 220 } 221 if h.Number.Cmp(h2.Number) != 0 { 222 return fmt.Errorf("Number: want: %v have: %v", h.Number, h2.Number) 223 } 224 if h.ParentHash != h2.ParentHash { 225 return fmt.Errorf("Parent hash: want: %x have: %x", h.ParentHash, h2.ParentHash) 226 } 227 if h.ReceiptTrie != h2.ReceiptHash { 228 return fmt.Errorf("Receipt hash: want: %x have: %x", h.ReceiptTrie, h2.ReceiptHash) 229 } 230 if h.TransactionsTrie != h2.TxHash { 231 return fmt.Errorf("Tx hash: want: %x have: %x", h.TransactionsTrie, h2.TxHash) 232 } 233 if h.StateRoot != h2.Root { 234 return fmt.Errorf("State hash: want: %x have: %x", h.StateRoot, h2.Root) 235 } 236 if h.UncleHash != h2.UncleHash { 237 return fmt.Errorf("Uncle hash: want: %x have: %x", h.UncleHash, h2.UncleHash) 238 } 239 if !bytes.Equal(h.ExtraData, h2.Extra) { 240 return fmt.Errorf("Extra data: want: %x have: %x", h.ExtraData, h2.Extra) 241 } 242 if h.Difficulty.Cmp(h2.Difficulty) != 0 { 243 return fmt.Errorf("Difficulty: want: %v have: %v", h.Difficulty, h2.Difficulty) 244 } 245 if h.GasLimit != h2.GasLimit { 246 return fmt.Errorf("GasLimit: want: %d have: %d", h.GasLimit, h2.GasLimit) 247 } 248 if h.GasUsed != h2.GasUsed { 249 return fmt.Errorf("GasUsed: want: %d have: %d", h.GasUsed, h2.GasUsed) 250 } 251 if h.Timestamp.Cmp(h2.Time) != 0 { 252 return fmt.Errorf("Timestamp: want: %v have: %v", h.Timestamp, h2.Time) 253 } 254 return nil 255 } 256 257 func (t *BlockTest) validatePostState(statedb *state.StateDB) error { 258 // validate post state accounts in test file against what we have in state db 259 for addr, acct := range t.json.Post { 260 // address is indirectly verified by the other fields, as it's the db key 261 code2 := statedb.GetCode(addr) 262 balance2 := statedb.GetBalance(addr) 263 nonce2 := statedb.GetNonce(addr) 264 if !bytes.Equal(code2, acct.Code) { 265 return fmt.Errorf("account code mismatch for addr: %s want: %v have: %s", addr, acct.Code, hex.EncodeToString(code2)) 266 } 267 if balance2.Cmp(acct.Balance) != 0 { 268 return fmt.Errorf("account balance mismatch for addr: %s, want: %d, have: %d", addr, acct.Balance, balance2) 269 } 270 if nonce2 != acct.Nonce { 271 return fmt.Errorf("account nonce mismatch for addr: %s want: %d have: %d", addr, acct.Nonce, nonce2) 272 } 273 } 274 return nil 275 } 276 277 func (t *BlockTest) validateImportedHeaders(cm *core.BlockChain, validBlocks []btBlock) error { 278 // to get constant lookup when verifying block headers by hash (some tests have many blocks) 279 bmap := make(map[common.Hash]btBlock, len(t.json.Blocks)) 280 for _, b := range validBlocks { 281 bmap[b.BlockHeader.Hash] = b 282 } 283 // iterate over blocks backwards from HEAD and validate imported 284 // headers vs test file. some tests have reorgs, and we import 285 // block-by-block, so we can only validate imported headers after 286 // all blocks have been processed by BlockChain, as they may not 287 // be part of the longest chain until last block is imported. 288 for b := cm.CurrentBlock(); b != nil && b.NumberU64() != 0; b = cm.GetBlockByHash(b.Header().ParentHash) { 289 if err := validateHeader(bmap[b.Hash()].BlockHeader, b.Header()); err != nil { 290 return fmt.Errorf("Imported block header validation failed: %v", err) 291 } 292 } 293 return nil 294 } 295 296 func (bb *btBlock) decode() (*types.Block, error) { 297 data, err := hexutil.Decode(bb.Rlp) 298 if err != nil { 299 return nil, err 300 } 301 var b types.Block 302 err = rlp.DecodeBytes(data, &b) 303 return &b, err 304 }