github.com/phillinzzz/newBsc@v1.1.6/core/blockchain_diff_test.go (about)

     1  // Copyright 2020 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  // Tests that abnormal program termination (i.e.crash) and restart doesn't leave
    18  // the database in some strange state with gaps in the chain, nor with block data
    19  // dangling in the future.
    20  
    21  package core
    22  
    23  import (
    24  	"math/big"
    25  	"testing"
    26  	"time"
    27  
    28  	"golang.org/x/crypto/sha3"
    29  
    30  	"github.com/phillinzzz/newBsc/common"
    31  	"github.com/phillinzzz/newBsc/consensus/ethash"
    32  	"github.com/phillinzzz/newBsc/core/rawdb"
    33  	"github.com/phillinzzz/newBsc/core/state/snapshot"
    34  	"github.com/phillinzzz/newBsc/core/types"
    35  	"github.com/phillinzzz/newBsc/core/vm"
    36  	"github.com/phillinzzz/newBsc/crypto"
    37  	"github.com/phillinzzz/newBsc/ethdb"
    38  	"github.com/phillinzzz/newBsc/ethdb/memorydb"
    39  	"github.com/phillinzzz/newBsc/params"
    40  	"github.com/phillinzzz/newBsc/rlp"
    41  )
    42  
    43  var (
    44  	// testKey is a private key to use for funding a tester account.
    45  	testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
    46  	// testAddr is the Ethereum address of the tester account.
    47  	testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
    48  	// testBlocks is the test parameters array for specific blocks.
    49  	testBlocks = []testBlockParam{
    50  		{
    51  			// This txs params also used to default block.
    52  			blockNr: 11,
    53  			txs: []testTransactionParam{
    54  				{
    55  					to:       common.Address{0x01},
    56  					value:    big.NewInt(1),
    57  					gasPrice: big.NewInt(1),
    58  					data:     nil,
    59  				},
    60  			},
    61  		},
    62  		{
    63  			blockNr: 12,
    64  			txs: []testTransactionParam{
    65  				{
    66  					to:       common.Address{0x01},
    67  					value:    big.NewInt(1),
    68  					gasPrice: big.NewInt(1),
    69  					data:     nil,
    70  				},
    71  				{
    72  					to:       common.Address{0x02},
    73  					value:    big.NewInt(2),
    74  					gasPrice: big.NewInt(2),
    75  					data:     nil,
    76  				},
    77  			},
    78  		},
    79  		{
    80  			blockNr: 13,
    81  			txs: []testTransactionParam{
    82  				{
    83  					to:       common.Address{0x01},
    84  					value:    big.NewInt(1),
    85  					gasPrice: big.NewInt(1),
    86  					data:     nil,
    87  				},
    88  				{
    89  					to:       common.Address{0x02},
    90  					value:    big.NewInt(2),
    91  					gasPrice: big.NewInt(2),
    92  					data:     nil,
    93  				},
    94  				{
    95  					to:       common.Address{0x03},
    96  					value:    big.NewInt(3),
    97  					gasPrice: big.NewInt(3),
    98  					data:     nil,
    99  				},
   100  			},
   101  		},
   102  		{
   103  			blockNr: 14,
   104  			txs:     []testTransactionParam{},
   105  		},
   106  	}
   107  )
   108  
   109  type testTransactionParam struct {
   110  	to       common.Address
   111  	value    *big.Int
   112  	gasPrice *big.Int
   113  	data     []byte
   114  }
   115  
   116  type testBlockParam struct {
   117  	blockNr int
   118  	txs     []testTransactionParam
   119  }
   120  
   121  // testBackend is a mock implementation of the live Ethereum message handler. Its
   122  // purpose is to allow testing the request/reply workflows and wire serialization
   123  // in the `eth` protocol without actually doing any data processing.
   124  type testBackend struct {
   125  	db    ethdb.Database
   126  	chain *BlockChain
   127  }
   128  
   129  // newTestBackend creates an empty chain and wraps it into a mock backend.
   130  func newTestBackend(blocks int, light bool) *testBackend {
   131  	return newTestBackendWithGenerator(blocks, light)
   132  }
   133  
   134  // newTestBackend creates a chain with a number of explicitly defined blocks and
   135  // wraps it into a mock backend.
   136  func newTestBackendWithGenerator(blocks int, lightProcess bool) *testBackend {
   137  	signer := types.HomesteadSigner{}
   138  	// Create a database pre-initialize with a genesis block
   139  	db := rawdb.NewMemoryDatabase()
   140  	db.SetDiffStore(memorydb.New())
   141  	(&Genesis{
   142  		Config: params.TestChainConfig,
   143  		Alloc:  GenesisAlloc{testAddr: {Balance: big.NewInt(100000000000000000)}},
   144  	}).MustCommit(db)
   145  
   146  	chain, _ := NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, EnablePersistDiff(860000))
   147  	generator := func(i int, block *BlockGen) {
   148  		// The chain maker doesn't have access to a chain, so the difficulty will be
   149  		// lets unset (nil). Set it here to the correct value.
   150  		block.SetCoinbase(testAddr)
   151  
   152  		for idx, testBlock := range testBlocks {
   153  			// Specific block setting, the index in this generator has 1 diff from specified blockNr.
   154  			if i+1 == testBlock.blockNr {
   155  				for _, testTransaction := range testBlock.txs {
   156  					tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), testTransaction.to,
   157  						testTransaction.value, params.TxGas, testTransaction.gasPrice, testTransaction.data), signer, testKey)
   158  					if err != nil {
   159  						panic(err)
   160  					}
   161  					block.AddTxWithChain(chain, tx)
   162  				}
   163  				break
   164  			}
   165  
   166  			// Default block setting.
   167  			if idx == len(testBlocks)-1 {
   168  				// We want to simulate an empty middle block, having the same state as the
   169  				// first one. The last is needs a state change again to force a reorg.
   170  				for _, testTransaction := range testBlocks[0].txs {
   171  					tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), testTransaction.to,
   172  						testTransaction.value, params.TxGas, testTransaction.gasPrice, testTransaction.data), signer, testKey)
   173  					if err != nil {
   174  						panic(err)
   175  					}
   176  					block.AddTxWithChain(chain, tx)
   177  				}
   178  			}
   179  		}
   180  
   181  	}
   182  	bs, _ := GenerateChain(params.TestChainConfig, chain.Genesis(), ethash.NewFaker(), db, blocks, generator)
   183  	if _, err := chain.InsertChain(bs); err != nil {
   184  		panic(err)
   185  	}
   186  	if lightProcess {
   187  		EnableLightProcessor(chain)
   188  	}
   189  
   190  	return &testBackend{
   191  		db:    db,
   192  		chain: chain,
   193  	}
   194  }
   195  
   196  // close tears down the transaction pool and chain behind the mock backend.
   197  func (b *testBackend) close() {
   198  	b.chain.Stop()
   199  }
   200  
   201  func (b *testBackend) Chain() *BlockChain { return b.chain }
   202  
   203  func rawDataToDiffLayer(data rlp.RawValue) (*types.DiffLayer, error) {
   204  	var diff types.DiffLayer
   205  	hasher := sha3.NewLegacyKeccak256()
   206  	err := rlp.DecodeBytes(data, &diff)
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  	hasher.Write(data)
   211  	var diffHash common.Hash
   212  	hasher.Sum(diffHash[:0])
   213  	diff.DiffHash = diffHash
   214  	hasher.Reset()
   215  	return &diff, nil
   216  }
   217  
   218  func TestProcessDiffLayer(t *testing.T) {
   219  	blockNum := 128
   220  	fullBackend := newTestBackend(blockNum, false)
   221  	falseDiff := 5
   222  	defer fullBackend.close()
   223  
   224  	lightBackend := newTestBackend(0, true)
   225  	defer lightBackend.close()
   226  	for i := 1; i <= blockNum-falseDiff; i++ {
   227  		block := fullBackend.chain.GetBlockByNumber(uint64(i))
   228  		if block == nil {
   229  			t.Fatal("block should not be nil")
   230  		}
   231  		blockHash := block.Hash()
   232  		rawDiff := fullBackend.chain.GetDiffLayerRLP(blockHash)
   233  		if len(rawDiff) != 0 {
   234  			diff, err := rawDataToDiffLayer(rawDiff)
   235  			if err != nil {
   236  				t.Errorf("failed to decode rawdata %v", err)
   237  			}
   238  			if diff == nil {
   239  				continue
   240  			}
   241  			lightBackend.Chain().HandleDiffLayer(diff, "testpid", true)
   242  		}
   243  		_, err := lightBackend.chain.insertChain([]*types.Block{block}, true)
   244  		if err != nil {
   245  			t.Errorf("failed to insert block %v", err)
   246  		}
   247  	}
   248  	currentBlock := lightBackend.chain.CurrentBlock()
   249  	nextBlock := fullBackend.chain.GetBlockByNumber(currentBlock.NumberU64() + 1)
   250  	rawDiff := fullBackend.chain.GetDiffLayerRLP(nextBlock.Hash())
   251  	diff, _ := rawDataToDiffLayer(rawDiff)
   252  	latestAccount, _ := snapshot.FullAccount(diff.Accounts[0].Blob)
   253  	latestAccount.Balance = big.NewInt(0)
   254  	bz, _ := rlp.EncodeToBytes(&latestAccount)
   255  	diff.Accounts[0].Blob = bz
   256  
   257  	lightBackend.Chain().HandleDiffLayer(diff, "testpid", true)
   258  
   259  	_, err := lightBackend.chain.insertChain([]*types.Block{nextBlock}, true)
   260  	if err != nil {
   261  		t.Errorf("failed to process block %v", err)
   262  	}
   263  
   264  	// the diff cache should be cleared
   265  	if len(lightBackend.chain.diffPeersToDiffHashes) != 0 {
   266  		t.Errorf("the size of diffPeersToDiffHashes should be 0, but get %d", len(lightBackend.chain.diffPeersToDiffHashes))
   267  	}
   268  	if len(lightBackend.chain.diffHashToPeers) != 0 {
   269  		t.Errorf("the size of diffHashToPeers should be 0, but get %d", len(lightBackend.chain.diffHashToPeers))
   270  	}
   271  	if len(lightBackend.chain.diffHashToBlockHash) != 0 {
   272  		t.Errorf("the size of diffHashToBlockHash should be 0, but get %d", len(lightBackend.chain.diffHashToBlockHash))
   273  	}
   274  	if len(lightBackend.chain.blockHashToDiffLayers) != 0 {
   275  		t.Errorf("the size of blockHashToDiffLayers should be 0, but get %d", len(lightBackend.chain.blockHashToDiffLayers))
   276  	}
   277  }
   278  
   279  func TestFreezeDiffLayer(t *testing.T) {
   280  	blockNum := 1024
   281  	fullBackend := newTestBackend(blockNum, true)
   282  	defer fullBackend.close()
   283  	// Minus one empty block.
   284  	if fullBackend.chain.diffQueue.Size() != blockNum-1 {
   285  		t.Errorf("size of diff queue is wrong, expected: %d, get: %d", blockNum, fullBackend.chain.diffQueue.Size())
   286  	}
   287  	time.Sleep(diffLayerFreezerRecheckInterval + 1*time.Second)
   288  	if fullBackend.chain.diffQueue.Size() != int(fullBackend.chain.triesInMemory) {
   289  		t.Errorf("size of diff queue is wrong, expected: %d, get: %d", blockNum, fullBackend.chain.diffQueue.Size())
   290  	}
   291  
   292  	block := fullBackend.chain.GetBlockByNumber(uint64(blockNum / 2))
   293  	diffStore := fullBackend.chain.db.DiffStore()
   294  	rawData := rawdb.ReadDiffLayerRLP(diffStore, block.Hash())
   295  	if len(rawData) == 0 {
   296  		t.Error("do not find diff layer in db")
   297  	}
   298  }
   299  
   300  func TestPruneDiffLayer(t *testing.T) {
   301  	blockNum := 1024
   302  	fullBackend := newTestBackend(blockNum, true)
   303  	defer fullBackend.close()
   304  
   305  	anotherFullBackend := newTestBackend(2*blockNum, true)
   306  	defer anotherFullBackend.close()
   307  
   308  	for num := uint64(1); num < uint64(blockNum); num++ {
   309  		header := fullBackend.chain.GetHeaderByNumber(num)
   310  		rawDiff := fullBackend.chain.GetDiffLayerRLP(header.Hash())
   311  		if len(rawDiff) != 0 {
   312  			diff, _ := rawDataToDiffLayer(rawDiff)
   313  			fullBackend.Chain().HandleDiffLayer(diff, "testpid1", true)
   314  			fullBackend.Chain().HandleDiffLayer(diff, "testpid2", true)
   315  		}
   316  	}
   317  	fullBackend.chain.pruneDiffLayer()
   318  	if len(fullBackend.chain.diffNumToBlockHashes) != maxDiffForkDist {
   319  		t.Error("unexpected size of diffNumToBlockHashes")
   320  	}
   321  	if len(fullBackend.chain.diffPeersToDiffHashes) != 2 {
   322  		t.Error("unexpected size of diffPeersToDiffHashes")
   323  	}
   324  	if len(fullBackend.chain.blockHashToDiffLayers) != maxDiffForkDist {
   325  		t.Error("unexpected size of diffNumToBlockHashes")
   326  	}
   327  	if len(fullBackend.chain.diffHashToBlockHash) != maxDiffForkDist {
   328  		t.Error("unexpected size of diffHashToBlockHash")
   329  	}
   330  	if len(fullBackend.chain.diffHashToPeers) != maxDiffForkDist {
   331  		t.Error("unexpected size of diffHashToPeers")
   332  	}
   333  
   334  	blocks := make([]*types.Block, 0, blockNum)
   335  	for i := blockNum + 1; i <= 2*blockNum; i++ {
   336  		b := anotherFullBackend.chain.GetBlockByNumber(uint64(i))
   337  		blocks = append(blocks, b)
   338  	}
   339  	fullBackend.chain.insertChain(blocks, true)
   340  	fullBackend.chain.pruneDiffLayer()
   341  	if len(fullBackend.chain.diffNumToBlockHashes) != 0 {
   342  		t.Error("unexpected size of diffNumToBlockHashes")
   343  	}
   344  	if len(fullBackend.chain.diffPeersToDiffHashes) != 0 {
   345  		t.Error("unexpected size of diffPeersToDiffHashes")
   346  	}
   347  	if len(fullBackend.chain.blockHashToDiffLayers) != 0 {
   348  		t.Error("unexpected size of diffNumToBlockHashes")
   349  	}
   350  	if len(fullBackend.chain.diffHashToBlockHash) != 0 {
   351  		t.Error("unexpected size of diffHashToBlockHash")
   352  	}
   353  	if len(fullBackend.chain.diffHashToPeers) != 0 {
   354  		t.Error("unexpected size of diffHashToPeers")
   355  	}
   356  
   357  }
   358  
   359  func TestGetDiffAccounts(t *testing.T) {
   360  	blockNum := 128
   361  	fullBackend := newTestBackend(blockNum, false)
   362  	defer fullBackend.close()
   363  
   364  	for _, testBlock := range testBlocks {
   365  		block := fullBackend.chain.GetBlockByNumber(uint64(testBlock.blockNr))
   366  		if block == nil {
   367  			t.Fatal("block should not be nil")
   368  		}
   369  		blockHash := block.Hash()
   370  		accounts, err := fullBackend.chain.GetDiffAccounts(blockHash)
   371  		if err != nil {
   372  			t.Errorf("get diff accounts eror for block number (%d): %v", testBlock.blockNr, err)
   373  		}
   374  
   375  		for idx, account := range accounts {
   376  			if testAddr == account {
   377  				break
   378  			}
   379  
   380  			if idx == len(accounts)-1 {
   381  				t.Errorf("the diff accounts does't include addr: %v", testAddr)
   382  			}
   383  		}
   384  
   385  		for _, transaction := range testBlock.txs {
   386  			for idx, account := range accounts {
   387  				if transaction.to == account {
   388  					break
   389  				}
   390  
   391  				if idx == len(accounts)-1 {
   392  					t.Errorf("the diff accounts does't include addr: %v", transaction.to)
   393  				}
   394  			}
   395  		}
   396  	}
   397  }