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