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