github.com/klaytn/klaytn@v1.12.1/tests/block_test_util.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2015 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from tests/block_test_util.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package tests 22 23 import ( 24 "bytes" 25 "encoding/hex" 26 "encoding/json" 27 "fmt" 28 "math/big" 29 30 "github.com/klaytn/klaytn/blockchain" 31 "github.com/klaytn/klaytn/blockchain/state" 32 "github.com/klaytn/klaytn/blockchain/types" 33 "github.com/klaytn/klaytn/blockchain/vm" 34 "github.com/klaytn/klaytn/common" 35 "github.com/klaytn/klaytn/common/hexutil" 36 "github.com/klaytn/klaytn/common/math" 37 "github.com/klaytn/klaytn/consensus/gxhash" 38 "github.com/klaytn/klaytn/params" 39 "github.com/klaytn/klaytn/rlp" 40 "github.com/klaytn/klaytn/storage/database" 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 blockchain.GenesisAlloc `json:"pre"` 57 Post blockchain.GenesisAlloc `json:"postState"` 58 BestBlock common.UnprefixedHash `json:"lastblockhash"` 59 Network string `json:"networks"` 60 } 61 62 type btBlock struct { 63 BlockHeader *btHeader 64 Rlp string 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 Number *big.Int 72 Hash common.Hash 73 ParentHash common.Hash 74 ReceiptTrie common.Hash 75 StateRoot common.Hash 76 TransactionsTrie common.Hash 77 ExtraData []byte 78 BlockScore *big.Int 79 GasUsed uint64 80 Timestamp *big.Int 81 } 82 83 type btHeaderMarshaling struct { 84 ExtraData hexutil.Bytes 85 Number *math.HexOrDecimal256 86 BlockScore *math.HexOrDecimal256 87 GasUsed math.HexOrDecimal64 88 Timestamp *math.HexOrDecimal256 89 } 90 91 func (t *BlockTest) Run() error { 92 config, ok := Forks[t.json.Network] 93 if !ok { 94 return UnsupportedForkError{t.json.Network} 95 } 96 97 // import pre accounts & construct test genesis block & state root 98 db := database.NewMemoryDBManager() 99 gblock, err := t.genesis(config).Commit(common.Hash{}, db) 100 if err != nil { 101 return err 102 } 103 if gblock.Hash() != t.json.Genesis.Hash { 104 return fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x", gblock.Hash().Bytes()[:6], t.json.Genesis.Hash[:6]) 105 } 106 if gblock.Root() != t.json.Genesis.StateRoot { 107 return fmt.Errorf("genesis block state root does not match test: computed=%x, test=%x", gblock.Root().Bytes()[:6], t.json.Genesis.StateRoot[:6]) 108 } 109 110 // TODO-Klaytn: Replace gxhash with istanbul 111 chain, err := blockchain.NewBlockChain(db, nil, config, gxhash.NewShared(), vm.Config{}) 112 if err != nil { 113 return err 114 } 115 defer chain.Stop() 116 117 validBlocks, err := t.insertBlocks(chain) 118 if err != nil { 119 return err 120 } 121 cmlast := chain.CurrentBlock().Hash() 122 if common.Hash(t.json.BestBlock) != cmlast { 123 return fmt.Errorf("last block hash validation mismatch: want: %x, have: %x", t.json.BestBlock, cmlast) 124 } 125 newDB, err := chain.State() 126 if err != nil { 127 return err 128 } 129 if err = t.validatePostState(newDB); err != nil { 130 return fmt.Errorf("post state validation failed: %v", err) 131 } 132 return t.validateImportedHeaders(chain, validBlocks) 133 } 134 135 func (t *BlockTest) genesis(config *params.ChainConfig) *blockchain.Genesis { 136 return &blockchain.Genesis{ 137 Config: config, 138 Timestamp: t.json.Genesis.Timestamp.Uint64(), 139 ParentHash: t.json.Genesis.ParentHash, 140 ExtraData: t.json.Genesis.ExtraData, 141 GasUsed: t.json.Genesis.GasUsed, 142 BlockScore: t.json.Genesis.BlockScore, 143 Alloc: t.json.Pre, 144 } 145 } 146 147 /* See https://github.com/ethereum/tests/wiki/Blockchain-Tests-II 148 149 Whether a block is valid or not is a bit subtle, it's defined by presence of 150 blockHeader and transactions fields. If they are missing, the block is 151 invalid and we must verify that we do not accept it. 152 153 Since some tests mix valid and invalid blocks we need to check this for every block. 154 155 If a block is invalid it does not necessarily fail the test, if it's invalidness is 156 expected we are expected to ignore it and continue processing and then validate the 157 post state. 158 */ 159 func (t *BlockTest) insertBlocks(blockchain *blockchain.BlockChain) ([]btBlock, error) { 160 validBlocks := make([]btBlock, 0) 161 // insert the test blocks, which will execute all transactions 162 for _, b := range t.json.Blocks { 163 cb, err := b.decode() 164 if err != nil { 165 if b.BlockHeader == nil { 166 continue // OK - block is supposed to be invalid, continue with next block 167 } else { 168 return nil, fmt.Errorf("Block RLP decoding failed when expected to succeed: %v", err) 169 } 170 } 171 // RLP decoding worked, try to insert into chain: 172 blocks := types.Blocks{cb} 173 i, err := blockchain.InsertChain(blocks) 174 if err != nil { 175 if b.BlockHeader == nil { 176 continue // OK - block is supposed to be invalid, continue with next block 177 } else { 178 return nil, fmt.Errorf("Block #%v insertion into chain failed: %v", blocks[i].Number(), err) 179 } 180 } 181 if b.BlockHeader == nil { 182 return nil, fmt.Errorf("Block insertion should have failed") 183 } 184 185 // validate RLP decoding by checking all values against test file JSON 186 if err = validateHeader(b.BlockHeader, cb.Header()); err != nil { 187 return nil, fmt.Errorf("Deserialised block header validation failed: %v", err) 188 } 189 validBlocks = append(validBlocks, b) 190 } 191 return validBlocks, nil 192 } 193 194 func validateHeader(h *btHeader, h2 *types.Header) error { 195 if h.Bloom != h2.Bloom { 196 return fmt.Errorf("Bloom: want: %x have: %x", h.Bloom, h2.Bloom) 197 } 198 if h.Number.Cmp(h2.Number) != 0 { 199 return fmt.Errorf("Number: want: %v have: %v", h.Number, h2.Number) 200 } 201 if h.ParentHash != h2.ParentHash { 202 return fmt.Errorf("Parent hash: want: %x have: %x", h.ParentHash, h2.ParentHash) 203 } 204 if h.ReceiptTrie != h2.ReceiptHash { 205 return fmt.Errorf("Receipt hash: want: %x have: %x", h.ReceiptTrie, h2.ReceiptHash) 206 } 207 if h.TransactionsTrie != h2.TxHash { 208 return fmt.Errorf("Tx hash: want: %x have: %x", h.TransactionsTrie, h2.TxHash) 209 } 210 if h.StateRoot != h2.Root { 211 return fmt.Errorf("State hash: want: %x have: %x", h.StateRoot, h2.Root) 212 } 213 if !bytes.Equal(h.ExtraData, h2.Extra) { 214 return fmt.Errorf("Extra data: want: %x have: %x", h.ExtraData, h2.Extra) 215 } 216 if h.BlockScore.Cmp(h2.BlockScore) != 0 { 217 return fmt.Errorf("BlockScore: want: %v have: %v", h.BlockScore, h2.BlockScore) 218 } 219 if h.GasUsed != h2.GasUsed { 220 return fmt.Errorf("GasUsed: want: %d have: %d", h.GasUsed, h2.GasUsed) 221 } 222 if h.Timestamp.Cmp(h2.Time) != 0 { 223 return fmt.Errorf("Timestamp: want: %v have: %v", h.Timestamp, h2.Time) 224 } 225 return nil 226 } 227 228 func (t *BlockTest) validatePostState(statedb *state.StateDB) error { 229 // validate post state accounts in test file against what we have in state db 230 for addr, acct := range t.json.Post { 231 // address is indirectly verified by the other fields, as it's the db key 232 code2 := statedb.GetCode(addr) 233 balance2 := statedb.GetBalance(addr) 234 nonce2 := statedb.GetNonce(addr) 235 if !bytes.Equal(code2, acct.Code) { 236 return fmt.Errorf("account code mismatch for addr: %s want: %v have: %s", addr, acct.Code, hex.EncodeToString(code2)) 237 } 238 if balance2.Cmp(acct.Balance) != 0 { 239 return fmt.Errorf("account balance mismatch for addr: %s, want: %d, have: %d", addr, acct.Balance, balance2) 240 } 241 if nonce2 != acct.Nonce { 242 return fmt.Errorf("account nonce mismatch for addr: %s want: %d have: %d", addr, acct.Nonce, nonce2) 243 } 244 } 245 return nil 246 } 247 248 func (t *BlockTest) validateImportedHeaders(cm *blockchain.BlockChain, validBlocks []btBlock) error { 249 // to get constant lookup when verifying block headers by hash (some tests have many blocks) 250 bmap := make(map[common.Hash]btBlock, len(t.json.Blocks)) 251 for _, b := range validBlocks { 252 bmap[b.BlockHeader.Hash] = b 253 } 254 // iterate over blocks backwards from HEAD and validate imported 255 // headers vs test file. some tests have reorgs, and we import 256 // block-by-block, so we can only validate imported headers after 257 // all blocks have been processed by BlockChain, as they may not 258 // be part of the longest chain until last block is imported. 259 for b := cm.CurrentBlock(); b != nil && b.NumberU64() != 0; b = cm.GetBlockByHash(b.Header().ParentHash) { 260 if err := validateHeader(bmap[b.Hash()].BlockHeader, b.Header()); err != nil { 261 return fmt.Errorf("Imported block header validation failed: %v", err) 262 } 263 } 264 return nil 265 } 266 267 func (bb *btBlock) decode() (*types.Block, error) { 268 data, err := hexutil.Decode(bb.Rlp) 269 if err != nil { 270 return nil, err 271 } 272 var b types.Block 273 err = rlp.DecodeBytes(data, &b) 274 return &b, err 275 }