blockchain_cache_provider_test.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. package core
  2. import (
  3. "math/big"
  4. "testing"
  5. "time"
  6. "github.com/ethereum/go-ethereum/common"
  7. "github.com/ethereum/go-ethereum/consensus/ethash"
  8. "github.com/ethereum/go-ethereum/core/rawdb"
  9. "github.com/ethereum/go-ethereum/core/types"
  10. "github.com/ethereum/go-ethereum/core/vm"
  11. "github.com/ethereum/go-ethereum/params"
  12. "github.com/ethereum/go-ethereum/private"
  13. "github.com/golang/mock/gomock"
  14. "github.com/stretchr/testify/assert"
  15. )
  16. var (
  17. incrementByOne = common.BytesToEncryptedPayloadHash([]byte("incContract"))
  18. )
  19. func buildCacheProviderTestChain(n int, config *params.ChainConfig, quorumChainConfig *QuorumChainConfig) ([]*types.Block, map[common.Hash]*types.Block, *BlockChain) {
  20. testdb := rawdb.NewMemoryDatabase()
  21. genesis := GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000))
  22. // The generated chain deploys one Accumulator contracts which is incremented every block
  23. blocks, _ := GenerateChain(config, genesis, ethash.NewFaker(), testdb, n, func(i int, block *BlockGen) {
  24. block.SetCoinbase(common.Address{0})
  25. signer := types.QuorumPrivateTxSigner{}
  26. var tx *types.Transaction
  27. var err error
  28. if i == 0 {
  29. tx, err = types.SignTx(types.NewContractCreation(block.TxNonce(testAddress), big.NewInt(0), testGas, nil, deployContract.Bytes()), signer, testKey)
  30. } else {
  31. tx, err = types.SignTx(types.NewTransaction(block.TxNonce(testAddress), Contract1AddressAfterDeployment, big.NewInt(0), testGas, nil, incrementByOne.Bytes()), signer, testKey)
  32. }
  33. if err != nil {
  34. panic(err)
  35. }
  36. block.AddTx(tx)
  37. })
  38. hashes := make([]common.Hash, n+1)
  39. hashes[len(hashes)-1] = genesis.Hash()
  40. blockm := make(map[common.Hash]*types.Block, n+1)
  41. blockm[genesis.Hash()] = genesis
  42. for i, b := range blocks {
  43. hashes[len(hashes)-i-2] = b.Hash()
  44. blockm[b.Hash()] = b
  45. }
  46. // recreate the DB so that we don't have the public state written already by the block generation logic
  47. testdb = rawdb.NewMemoryDatabase()
  48. genesis = GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000))
  49. // disable snapshots
  50. testingCacheConfig := &CacheConfig{
  51. TrieCleanLimit: 256,
  52. TrieDirtyLimit: 256,
  53. TrieTimeLimit: 5 * time.Minute,
  54. // TODO - figure out why is the snapshot causing panics when enabled during the test
  55. SnapshotLimit: 0,
  56. SnapshotWait: true,
  57. }
  58. blockchain, err := NewBlockChain(testdb, testingCacheConfig, config, ethash.NewFaker(), vm.Config{}, nil, nil, quorumChainConfig)
  59. if err != nil {
  60. return nil, nil, nil
  61. }
  62. return blocks, blockm, blockchain
  63. }
  64. func buildMockPTM(mockCtrl *gomock.Controller) private.PrivateTransactionManager {
  65. mockptm := private.NewMockPrivateTransactionManager(mockCtrl)
  66. deployAccumulatorContractConstructor, _ := AccumulatorParsedABI.Pack("", big.NewInt(1))
  67. deployAccumulatorContract := append(common.FromHex(AccumulatorBin), deployAccumulatorContractConstructor...)
  68. incrementAccumulatorContract, _ := AccumulatorParsedABI.Pack("inc", big.NewInt(1))
  69. mockptm.EXPECT().Receive(deployContract).Return("", []string{"AAA"}, deployAccumulatorContract, nil, nil).AnyTimes()
  70. mockptm.EXPECT().Receive(incrementByOne).Return("", []string{"AAA"}, incrementAccumulatorContract, nil, nil).AnyTimes()
  71. mockptm.EXPECT().Receive(common.EncryptedPayloadHash{}).Return("", []string{}, common.EncryptedPayloadHash{}.Bytes(), nil, nil).AnyTimes()
  72. return mockptm
  73. }
  74. func TestSegregatedCacheProvider(t *testing.T) {
  75. mockCtrl := gomock.NewController(t)
  76. defer mockCtrl.Finish()
  77. mockptm := buildMockPTM(mockCtrl)
  78. saved := private.P
  79. defer func() {
  80. private.P = saved
  81. }()
  82. private.P = mockptm
  83. blocks, _, blockchain := buildCacheProviderTestChain(11, params.QuorumTestChainConfig, nil)
  84. count, err := blockchain.InsertChain(blocks)
  85. assert.Nil(t, err)
  86. assert.Equal(t, len(blocks), count)
  87. lastBlock := blocks[len(blocks)-1]
  88. statedbLast, privateStateRepoLast, _ := blockchain.StateAt(lastBlock.Root())
  89. assert.Equal(t, uint64(len(blocks)), statedbLast.GetNonce(testAddress))
  90. privateState, _ := privateStateRepoLast.DefaultState()
  91. accPrivateStateStateLast := privateState.GetState(Contract1AddressAfterDeployment, common.Hash{})
  92. assert.Equal(t, common.BytesToHash(big.NewInt(int64(len(blocks))).Bytes()), accPrivateStateStateLast)
  93. // retrieve the state at block height 1
  94. block1 := blocks[1]
  95. statedbB1, privateStateRepoB1, _ := blockchain.StateAt(block1.Root())
  96. assert.Equal(t, uint64(2), statedbB1.GetNonce(testAddress))
  97. privateState, _ = privateStateRepoB1.DefaultState()
  98. privateStateRoot := privateState.IntermediateRoot(false)
  99. accPrivateStateStateB1 := privateState.GetState(Contract1AddressAfterDeployment, common.Hash{})
  100. assert.Equal(t, common.BytesToHash([]byte{2}), accPrivateStateStateB1)
  101. // check that the private state root has already been written to the DB
  102. contains, err := blockchain.db.Has(privateStateRoot.Bytes())
  103. assert.Nil(t, err)
  104. assert.True(t, contains)
  105. }
  106. func TestUnifiedCacheProvider(t *testing.T) {
  107. mockCtrl := gomock.NewController(t)
  108. defer mockCtrl.Finish()
  109. mockptm := buildMockPTM(mockCtrl)
  110. saved := private.P
  111. defer func() {
  112. private.P = saved
  113. }()
  114. private.P = mockptm
  115. blocks, _, blockchain := buildCacheProviderTestChain(130, params.QuorumTestChainConfig, &QuorumChainConfig{privateTrieCacheEnabled: true})
  116. count, err := blockchain.InsertChain(blocks[:129])
  117. assert.Nil(t, err)
  118. assert.Equal(t, 129, count)
  119. lastBlock := blocks[128]
  120. statedb, privateStateRepo, _ := blockchain.StateAt(lastBlock.Root())
  121. assert.Equal(t, uint64(129), statedb.GetNonce(testAddress))
  122. privateState, _ := privateStateRepo.DefaultState()
  123. accPrivateState := privateState.GetState(Contract1AddressAfterDeployment, common.Hash{})
  124. assert.Equal(t, common.BytesToHash(big.NewInt(129).Bytes()), accPrivateState)
  125. // The following is an attempt to explain the process by which the trie nodes corresponding to privateState are being
  126. // garbage collected due to the TriesInMemory limit implemented in the blockchain
  127. // Expected state structure (block 2 - index 1 in the blocks array)
  128. // Public state just contains the testAddress(testKey) with nonce 2 and has PUB(BL2) root hash
  129. // privateState has C1(2) privateState(BL2) as root hash
  130. // the public state root PUB(BL2) references the private state root privateState(BL2)
  131. // Considering the above we can establish that
  132. // privateState(BL2) is referenced once by the public state root PUB(BL2) at block height 2
  133. // retrieve the state at block height 2
  134. block1 := blocks[1]
  135. statedbB1, privateStateRepoB1, _ := blockchain.StateAt(block1.Root())
  136. assert.Equal(t, uint64(2), statedbB1.GetNonce(testAddress))
  137. privateState, _ = privateStateRepoB1.DefaultState()
  138. privateStateRoot := privateState.IntermediateRoot(false)
  139. accPrivateStateB1 := privateState.GetState(Contract1AddressAfterDeployment, common.Hash{})
  140. assert.Equal(t, common.BytesToHash([]byte{2}), accPrivateStateB1)
  141. // check that the roots have NOT been written to the underlying DB
  142. contains, err := blockchain.db.Has(block1.Root().Bytes())
  143. assert.Nil(t, err)
  144. assert.False(t, contains)
  145. contains, err = blockchain.db.Has(privateStateRoot.Bytes())
  146. assert.Nil(t, err)
  147. assert.False(t, contains)
  148. // check that the roots are available in the cache
  149. data, err := blockchain.stateCache.TrieDB().Node(block1.Root())
  150. assert.Nil(t, err)
  151. assert.True(t, len(data) > 0)
  152. data, err = blockchain.stateCache.TrieDB().Node(privateStateRoot)
  153. assert.Nil(t, err)
  154. assert.True(t, len(data) > 0)
  155. // Process block 130 and reassess the underlying DB and the cache
  156. // When block 130 is processed the "chosen" in blockchain becomes 2 (130 - TriesInMemory) and the public root hash
  157. // PUB(BL2) is being de-referenced (reference count is reduced by 1) and the reference count (parents) becomes 0
  158. // As a result private state root privateState(BL2) is de-referenced and the reference count becomes 0
  159. // All nodes with reference counts (parents) equal to 0 are being garbage collected (removed from the cache)
  160. count, err = blockchain.InsertChain(blocks[129:])
  161. assert.Nil(t, err)
  162. assert.Equal(t, 1, count)
  163. // check that the roots have NOT been written to the underlying DB
  164. contains, err = blockchain.db.Has(block1.Root().Bytes())
  165. assert.Nil(t, err)
  166. assert.False(t, contains)
  167. contains, err = blockchain.db.Has(privateStateRoot.Bytes())
  168. assert.Nil(t, err)
  169. assert.False(t, contains)
  170. // check that the roots have been garbage collected (removed) from the cache (other intermediate trie nodes may have
  171. // been eliminated from the cache as well)
  172. data, err = blockchain.stateCache.TrieDB().Node(block1.Root())
  173. assert.Error(t, err, "not found")
  174. assert.Nil(t, data)
  175. data, err = blockchain.stateCache.TrieDB().Node(privateStateRoot)
  176. assert.Error(t, err, "not found")
  177. assert.Nil(t, data)
  178. }