github.com/klaytn/klaytn@v1.12.1/blockchain/block_validator_test.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 core/block_validator_test.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package blockchain 22 23 import ( 24 "math/big" 25 "runtime" 26 "testing" 27 "time" 28 29 "github.com/klaytn/klaytn/blockchain/types" 30 "github.com/klaytn/klaytn/blockchain/vm" 31 "github.com/klaytn/klaytn/common" 32 "github.com/klaytn/klaytn/consensus/gxhash" 33 "github.com/klaytn/klaytn/crypto" 34 "github.com/klaytn/klaytn/params" 35 "github.com/klaytn/klaytn/storage/database" 36 "github.com/stretchr/testify/assert" 37 ) 38 39 // Tests that simple header verification works, for both good and bad blocks. 40 func TestHeaderVerification(t *testing.T) { 41 // Create a simple chain to verify 42 var ( 43 testdb = database.NewMemoryDBManager() 44 gspec = &Genesis{Config: params.TestChainConfig} 45 genesis = gspec.MustCommit(testdb) 46 blocks, _ = GenerateChain(params.TestChainConfig, genesis, gxhash.NewFaker(), testdb, 8, nil) 47 ) 48 headers := make([]*types.Header, len(blocks)) 49 for i, block := range blocks { 50 headers[i] = block.Header() 51 } 52 // Run the header checker for blocks one-by-one, checking for both valid and invalid nonces 53 chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, gxhash.NewFaker(), vm.Config{}) 54 defer chain.Stop() 55 56 for i := 0; i < len(blocks); i++ { 57 for j, valid := range []bool{true, false} { 58 var results <-chan error 59 60 if valid { 61 engine := gxhash.NewFaker() 62 _, results = engine.VerifyHeaders(chain, []*types.Header{headers[i]}, []bool{true}) 63 } else { 64 engine := gxhash.NewFakeFailer(headers[i].Number.Uint64()) 65 _, results = engine.VerifyHeaders(chain, []*types.Header{headers[i]}, []bool{true}) 66 } 67 // Wait for the verification result 68 select { 69 case result := <-results: 70 if (result == nil) != valid { 71 t.Errorf("test %d.%d: validity mismatch: have %v, want %v", i, j, result, valid) 72 } 73 case <-time.After(time.Second): 74 t.Fatalf("test %d.%d: verification timeout", i, j) 75 } 76 // Make sure no more data is returned 77 select { 78 case result := <-results: 79 t.Fatalf("test %d.%d: unexpected result returned: %v", i, j, result) 80 case <-time.After(25 * time.Millisecond): 81 } 82 } 83 chain.InsertChain(blocks[i : i+1]) 84 } 85 } 86 87 func TestVerifyBlockBody(t *testing.T) { 88 testcases := []struct { 89 baseFee *big.Int 90 txData types.TxInternalData 91 err bool 92 }{ 93 { 94 big.NewInt(100), 95 &types.TxInternalDataLegacy{ 96 GasLimit: 1, 97 Price: big.NewInt(200), 98 Payload: []byte("abcdef"), 99 }, 100 false, 101 }, 102 103 { 104 big.NewInt(100), 105 &types.TxInternalDataEthereumDynamicFee{ 106 ChainID: big.NewInt(1), 107 GasLimit: 123457, 108 GasFeeCap: big.NewInt(10), 109 GasTipCap: big.NewInt(10), 110 Payload: []byte("abcdef"), 111 }, 112 true, 113 }, 114 { 115 big.NewInt(100), 116 &types.TxInternalDataEthereumDynamicFee{ 117 ChainID: big.NewInt(1), 118 GasLimit: 123457, 119 GasFeeCap: big.NewInt(100), 120 GasTipCap: big.NewInt(10), 121 Payload: []byte("abcdef"), 122 }, 123 false, 124 }, 125 { 126 big.NewInt(100), 127 &types.TxInternalDataEthereumDynamicFee{ 128 ChainID: big.NewInt(1), 129 GasLimit: 123457, 130 GasFeeCap: big.NewInt(200), 131 GasTipCap: big.NewInt(10), 132 }, 133 false, 134 }, 135 } 136 137 // Create a simple chain to verify 138 var ( 139 testdb = database.NewMemoryDBManager() 140 gspec = &Genesis{Config: params.TestChainConfig} 141 genesis = gspec.MustCommit(testdb) 142 ) 143 144 // We don't need istanbul instance here because validateBody is in BlockValidator instance 145 GenerateChain(params.TestChainConfig, genesis, gxhash.NewFaker(), testdb, 8, nil) 146 chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, gxhash.NewFaker(), vm.Config{}) 147 defer chain.Stop() 148 149 // Generate a batch of accounts to start with 150 privKey, _ := crypto.GenerateKey() 151 signer := types.LatestSignerForChainID(big.NewInt(1)) 152 var block *types.Block 153 for _, testcase := range testcases { 154 // Generate a block header 155 header := &types.Header{ 156 ParentHash: chain.hc.currentHeaderHash, 157 Number: common.Big1, 158 GasUsed: 0, 159 Extra: []byte{}, 160 BaseFee: testcase.baseFee, 161 } 162 163 // Generate a block with tx 164 tx := types.NewTx(testcase.txData) 165 tx.Sign(signer, privKey) 166 block = types.NewBlock(header, append(types.Transactions{}, tx), nil) 167 168 err := chain.validator.ValidateBody(block) 169 if errExist := err != nil; errExist != testcase.err { 170 assert.Error(t, err) 171 } 172 } 173 } 174 175 // Tests that concurrent header verification works, for both good and bad blocks. 176 func TestHeaderConcurrentVerification2(t *testing.T) { testHeaderConcurrentVerification(t, 2) } 177 func TestHeaderConcurrentVerification8(t *testing.T) { testHeaderConcurrentVerification(t, 8) } 178 func TestHeaderConcurrentVerification32(t *testing.T) { testHeaderConcurrentVerification(t, 32) } 179 180 func testHeaderConcurrentVerification(t *testing.T, threads int) { 181 // Create a simple chain to verify 182 var ( 183 testdb = database.NewMemoryDBManager() 184 gspec = &Genesis{Config: params.TestChainConfig} 185 genesis = gspec.MustCommit(testdb) 186 blocks, _ = GenerateChain(params.TestChainConfig, genesis, gxhash.NewFaker(), testdb, 8, nil) 187 ) 188 headers := make([]*types.Header, len(blocks)) 189 seals := make([]bool, len(blocks)) 190 191 for i, block := range blocks { 192 headers[i] = block.Header() 193 seals[i] = true 194 } 195 // Set the number of threads to verify on 196 old := runtime.GOMAXPROCS(threads) 197 defer runtime.GOMAXPROCS(old) 198 199 // Run the header checker for the entire block chain at once both for a valid and 200 // also an invalid chain (enough if one arbitrary block is invalid). 201 for i, valid := range []bool{true, false} { 202 var results <-chan error 203 204 if valid { 205 chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, gxhash.NewFaker(), vm.Config{}) 206 _, results = chain.engine.VerifyHeaders(chain, headers, seals) 207 chain.Stop() 208 } else { 209 chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, gxhash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}) 210 _, results = chain.engine.VerifyHeaders(chain, headers, seals) 211 chain.Stop() 212 } 213 // Wait for all the verification results 214 checks := make(map[int]error) 215 for j := 0; j < len(blocks); j++ { 216 select { 217 case result := <-results: 218 checks[j] = result 219 220 case <-time.After(time.Second): 221 t.Fatalf("test %d.%d: verification timeout", i, j) 222 } 223 } 224 // Check nonce check validity 225 for j := 0; j < len(blocks); j++ { 226 want := valid || (j < len(blocks)-2) // We chose the last-but-one nonce in the chain to fail 227 if (checks[j] == nil) != want { 228 t.Errorf("test %d.%d: validity mismatch: have %v, want %v", i, j, checks[j], want) 229 } 230 if !want { 231 // A few blocks after the first error may pass verification due to concurrent 232 // workers. We don't care about those in this test, just that the correct block 233 // errors out. 234 break 235 } 236 } 237 // Make sure no more data is returned 238 select { 239 case result := <-results: 240 t.Fatalf("test %d: unexpected result returned: %v", i, result) 241 case <-time.After(25 * time.Millisecond): 242 } 243 } 244 } 245 246 // Tests that aborting a header validation indeed prevents further checks from being 247 // run, as well as checks that no left-over goroutines are leaked. 248 func TestHeaderConcurrentAbortion2(t *testing.T) { testHeaderConcurrentAbortion(t, 2) } 249 func TestHeaderConcurrentAbortion8(t *testing.T) { testHeaderConcurrentAbortion(t, 8) } 250 func TestHeaderConcurrentAbortion32(t *testing.T) { testHeaderConcurrentAbortion(t, 32) } 251 252 func testHeaderConcurrentAbortion(t *testing.T, threads int) { 253 // Create a simple chain to verify 254 var ( 255 testdb = database.NewMemoryDBManager() 256 gspec = &Genesis{Config: params.TestChainConfig} 257 genesis = gspec.MustCommit(testdb) 258 blocks, _ = GenerateChain(params.TestChainConfig, genesis, gxhash.NewFaker(), testdb, 1024, nil) 259 ) 260 headers := make([]*types.Header, len(blocks)) 261 seals := make([]bool, len(blocks)) 262 263 for i, block := range blocks { 264 headers[i] = block.Header() 265 seals[i] = true 266 } 267 // Set the number of threads to verify on 268 old := runtime.GOMAXPROCS(threads) 269 defer runtime.GOMAXPROCS(old) 270 271 // Start the verifications and immediately abort 272 chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, gxhash.NewFakeDelayer(time.Millisecond), vm.Config{}) 273 defer chain.Stop() 274 275 abort, results := chain.engine.VerifyHeaders(chain, headers, seals) 276 close(abort) 277 278 // Deplete the results channel 279 verified := 0 280 for depleted := false; !depleted; { 281 select { 282 case result := <-results: 283 if result != nil { 284 t.Errorf("header %d: validation failed: %v", verified, result) 285 } 286 verified++ 287 case <-time.After(50 * time.Millisecond): 288 depleted = true 289 } 290 } 291 // Check that abortion was honored by not processing too many POWs 292 if verified > 2*threads { 293 t.Errorf("verification count too large: have %d, want below %d", verified, 2*threads) 294 } 295 }