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