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