blockchain_cache_provider_mps_test.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. package core
  2. import (
  3. "encoding/base64"
  4. "math/big"
  5. "strings"
  6. "testing"
  7. "time"
  8. "github.com/ethereum/go-ethereum/accounts/abi"
  9. "github.com/ethereum/go-ethereum/common"
  10. "github.com/ethereum/go-ethereum/consensus/ethash"
  11. "github.com/ethereum/go-ethereum/core/rawdb"
  12. "github.com/ethereum/go-ethereum/core/types"
  13. "github.com/ethereum/go-ethereum/core/vm"
  14. "github.com/ethereum/go-ethereum/crypto"
  15. "github.com/ethereum/go-ethereum/params"
  16. "github.com/ethereum/go-ethereum/private"
  17. "github.com/ethereum/go-ethereum/private/engine"
  18. "github.com/golang/mock/gomock"
  19. "github.com/stretchr/testify/assert"
  20. )
  21. /*
  22. pragma solidity ^0.5.0;
  23. contract Accumulator {
  24. uint public storedData;
  25. event IncEvent(uint value);
  26. constructor(uint initVal) public{
  27. storedData = initVal;
  28. }
  29. function inc(uint x) public {
  30. storedData = storedData + x;
  31. emit IncEvent(storedData);
  32. }
  33. function get() view public returns (uint retVal) {
  34. return storedData;
  35. }
  36. }
  37. */
  38. const AccumulatorABI = "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"initVal\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"IncEvent\",\"type\":\"event\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"retVal\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"}],\"name\":\"inc\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"storedData\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]"
  39. var (
  40. deployContract = common.BytesToEncryptedPayloadHash([]byte("deployContract"))
  41. incrementByOnePS1 = common.BytesToEncryptedPayloadHash([]byte("incContractPS1"))
  42. incrementByOnePS1PS2 = common.BytesToEncryptedPayloadHash([]byte("incContractPS1PS2"))
  43. AccumulatorParsedABI, _ = abi.JSON(strings.NewReader(AccumulatorABI))
  44. AccumulatorBin = "0x608060405234801561001057600080fd5b5060405161018a38038061018a8339818101604052602081101561003357600080fd5b8101908080519060200190929190505050806000819055505061012f8061005b6000396000f3fe6080604052348015600f57600080fd5b5060043610603c5760003560e01c80632a1afcd91460415780636d4ce63c14605d578063812600df146079575b600080fd5b604760a4565b6040518082815260200191505060405180910390f35b606360aa565b6040518082815260200191505060405180910390f35b60a260048036036020811015608d57600080fd5b810190808035906020019092919050505060b3565b005b60005481565b60008054905090565b80600054016000819055507fc13aa85405f3616d514cfd2316b12181b047ed7f229bce08ce53c671f6f94f986000546040518082815260200191505060405180910390a15056fea265627a7a723158208fb1390ecdc6d669bf1855aed67a225931aee2c14ac2b8f5cd2ac5a8fb3a21af64736f6c63430005110032"
  45. Contract1AddressAfterDeployment = crypto.CreateAddress(testAddress, 0)
  46. Contract2AddressAfterDeployment = crypto.CreateAddress(testAddress, 1)
  47. PS1PG = engine.PrivacyGroup{
  48. Type: "RESIDENT",
  49. Name: "PS1",
  50. PrivacyGroupId: base64.StdEncoding.EncodeToString([]byte("PS1")),
  51. Description: "Resident Group 1",
  52. From: "",
  53. Members: []string{"AAA", "BBB"},
  54. }
  55. PS2PG = engine.PrivacyGroup{
  56. Type: "RESIDENT",
  57. Name: "PS2",
  58. PrivacyGroupId: base64.StdEncoding.EncodeToString([]byte("PS2")),
  59. Description: "Resident Group 2",
  60. From: "",
  61. Members: []string{"CCC", "DDD"},
  62. }
  63. )
  64. func buildCacheProviderMPSTestChain(n int, config *params.ChainConfig, quorumChainConfig *QuorumChainConfig) ([]*types.Block, map[common.Hash]*types.Block, *BlockChain) {
  65. testdb := rawdb.NewMemoryDatabase()
  66. genesis := GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000))
  67. // The generated chain deploys two Accumulator contracts
  68. // - Accumulator contract 1 is incremented every 1 and 2 blocks for PS1 and PS1&PS2 respectively
  69. // - Accumulator contract 2 is incremented every block for both PS1 and PS2
  70. blocks, _ := GenerateChain(config, genesis, ethash.NewFaker(), testdb, n, func(i int, block *BlockGen) {
  71. block.SetCoinbase(common.Address{0})
  72. signer := types.QuorumPrivateTxSigner{}
  73. var tx *types.Transaction
  74. var err error
  75. if i == 0 {
  76. tx, err = types.SignTx(types.NewContractCreation(block.TxNonce(testAddress), big.NewInt(0), testGas, nil, deployContract.Bytes()), signer, testKey)
  77. if err != nil {
  78. panic(err)
  79. }
  80. block.AddTx(tx)
  81. tx, err = types.SignTx(types.NewContractCreation(block.TxNonce(testAddress), big.NewInt(0), testGas, nil, deployContract.Bytes()), signer, testKey)
  82. if err != nil {
  83. panic(err)
  84. }
  85. block.AddTx(tx)
  86. } else {
  87. if i%2 == 1 {
  88. tx, err = types.SignTx(types.NewTransaction(block.TxNonce(testAddress), Contract1AddressAfterDeployment, big.NewInt(0), testGas, nil, incrementByOnePS1.Bytes()), signer, testKey)
  89. if err != nil {
  90. panic(err)
  91. }
  92. block.AddTx(tx)
  93. tx, err = types.SignTx(types.NewTransaction(block.TxNonce(testAddress), Contract2AddressAfterDeployment, big.NewInt(0), testGas, nil, incrementByOnePS1PS2.Bytes()), signer, testKey)
  94. if err != nil {
  95. panic(err)
  96. }
  97. block.AddTx(tx)
  98. } else {
  99. tx, err = types.SignTx(types.NewTransaction(block.TxNonce(testAddress), Contract1AddressAfterDeployment, big.NewInt(0), testGas, nil, incrementByOnePS1PS2.Bytes()), signer, testKey)
  100. if err != nil {
  101. panic(err)
  102. }
  103. block.AddTx(tx)
  104. tx, err = types.SignTx(types.NewTransaction(block.TxNonce(testAddress), Contract2AddressAfterDeployment, big.NewInt(0), testGas, nil, incrementByOnePS1PS2.Bytes()), signer, testKey)
  105. if err != nil {
  106. panic(err)
  107. }
  108. block.AddTx(tx)
  109. }
  110. }
  111. })
  112. hashes := make([]common.Hash, n+1)
  113. hashes[len(hashes)-1] = genesis.Hash()
  114. blockm := make(map[common.Hash]*types.Block, n+1)
  115. blockm[genesis.Hash()] = genesis
  116. for i, b := range blocks {
  117. hashes[len(hashes)-i-2] = b.Hash()
  118. blockm[b.Hash()] = b
  119. }
  120. // recreate the DB so that we don't have the public state written already by the block generation logic
  121. testdb = rawdb.NewMemoryDatabase()
  122. genesis = GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000))
  123. // disable snapshots
  124. testingCacheConfig := &CacheConfig{
  125. TrieCleanLimit: 256,
  126. TrieDirtyLimit: 256,
  127. TrieTimeLimit: 5 * time.Minute,
  128. // TODO - figure out why is the snapshot causing panics when enabled during the test
  129. SnapshotLimit: 0,
  130. SnapshotWait: true,
  131. }
  132. blockchain, err := NewBlockChain(testdb, testingCacheConfig, config, ethash.NewFaker(), vm.Config{}, nil, nil, quorumChainConfig)
  133. if err != nil {
  134. return nil, nil, nil
  135. }
  136. return blocks, blockm, blockchain
  137. }
  138. func buildMockMPSPTM(mockCtrl *gomock.Controller) private.PrivateTransactionManager {
  139. mockptm := private.NewMockPrivateTransactionManager(mockCtrl)
  140. deployAccumulatorContractConstructor, _ := AccumulatorParsedABI.Pack("", big.NewInt(1))
  141. deployAccumulatorContract := append(common.FromHex(AccumulatorBin), deployAccumulatorContractConstructor...)
  142. incrementAccumulatorContract, _ := AccumulatorParsedABI.Pack("inc", big.NewInt(1))
  143. mockptm.EXPECT().Receive(deployContract).Return("", []string{"AAA", "CCC"}, deployAccumulatorContract, nil, nil).AnyTimes()
  144. mockptm.EXPECT().Receive(incrementByOnePS1).Return("", []string{"AAA"}, incrementAccumulatorContract, nil, nil).AnyTimes()
  145. mockptm.EXPECT().Receive(incrementByOnePS1PS2).Return("", []string{"AAA", "CCC"}, incrementAccumulatorContract, nil, nil).AnyTimes()
  146. mockptm.EXPECT().Receive(common.EncryptedPayloadHash{}).Return("", []string{}, common.EncryptedPayloadHash{}.Bytes(), nil, nil).AnyTimes()
  147. mockptm.EXPECT().HasFeature(engine.MultiplePrivateStates).Return(true)
  148. mockptm.EXPECT().Groups().Return([]engine.PrivacyGroup{PS1PG, PS2PG}, nil).AnyTimes()
  149. return mockptm
  150. }
  151. func TestSegregatedCacheProviderMPS(t *testing.T) {
  152. mockCtrl := gomock.NewController(t)
  153. defer mockCtrl.Finish()
  154. mockptm := buildMockMPSPTM(mockCtrl)
  155. saved := private.P
  156. defer func() {
  157. private.P = saved
  158. }()
  159. private.P = mockptm
  160. blocks, _, blockchain := buildCacheProviderMPSTestChain(11, params.QuorumMPSTestChainConfig, nil)
  161. count, err := blockchain.InsertChain(blocks)
  162. assert.Nil(t, err)
  163. assert.Equal(t, len(blocks), count)
  164. lastBlock := blocks[len(blocks)-1]
  165. statedbLast, privateStateRepoLast, _ := blockchain.StateAt(lastBlock.Root())
  166. assert.Equal(t, uint64(2*len(blocks)), statedbLast.GetNonce(testAddress))
  167. PS1, _ := privateStateRepoLast.StatePSI(types.PrivateStateIdentifier("PS1"))
  168. accPS1StateLast := PS1.GetState(Contract1AddressAfterDeployment, common.Hash{})
  169. assert.Equal(t, common.BytesToHash(big.NewInt(int64(len(blocks))).Bytes()), accPS1StateLast)
  170. PS2, _ := privateStateRepoLast.StatePSI(types.PrivateStateIdentifier("PS2"))
  171. accPS2StateLast := PS2.GetState(Contract1AddressAfterDeployment, common.Hash{})
  172. // PS2 is incremented every other block - thus the (len(blocks)+1)/2 formula
  173. assert.Equal(t, common.BytesToHash(big.NewInt(int64((len(blocks)+1)/2)).Bytes()), accPS2StateLast)
  174. // retrieve the state at block height 1
  175. block1 := blocks[1]
  176. statedbB1, privateStateRepoB1, _ := blockchain.StateAt(block1.Root())
  177. assert.Equal(t, uint64(4), statedbB1.GetNonce(testAddress))
  178. PS1, _ = privateStateRepoB1.StatePSI(types.PrivateStateIdentifier("PS1"))
  179. PS1Root := PS1.IntermediateRoot(false)
  180. accPS1StateB1 := PS1.GetState(Contract1AddressAfterDeployment, common.Hash{})
  181. assert.Equal(t, common.BytesToHash([]byte{2}), accPS1StateB1)
  182. PS2, _ = privateStateRepoB1.StatePSI(types.PrivateStateIdentifier("PS2"))
  183. PS2Root := PS2.IntermediateRoot(false)
  184. accPS2StateB1 := PS2.GetState(Contract1AddressAfterDeployment, common.Hash{})
  185. // PS2 is incremented every other block - thus the (len(blocks)+1)/2 formula
  186. assert.Equal(t, common.BytesToHash([]byte{1}), accPS2StateB1)
  187. // check that both roots have already been written to the underlying DB
  188. contains, err := blockchain.db.Has(PS1Root.Bytes())
  189. assert.Nil(t, err)
  190. assert.True(t, contains)
  191. contains, err = blockchain.db.Has(PS2Root.Bytes())
  192. assert.Nil(t, err)
  193. assert.True(t, contains)
  194. }
  195. func TestUnifiedCacheProviderMPS(t *testing.T) {
  196. mockCtrl := gomock.NewController(t)
  197. defer mockCtrl.Finish()
  198. mockptm := buildMockMPSPTM(mockCtrl)
  199. saved := private.P
  200. defer func() {
  201. private.P = saved
  202. }()
  203. private.P = mockptm
  204. blocks, _, blockchain := buildCacheProviderMPSTestChain(130, params.QuorumMPSTestChainConfig, &QuorumChainConfig{multiTenantEnabled: true, privateTrieCacheEnabled: true})
  205. count, err := blockchain.InsertChain(blocks[:129])
  206. assert.Nil(t, err)
  207. assert.Equal(t, 129, count)
  208. lastBlock := blocks[128]
  209. statedb, privateStateRepo, _ := blockchain.StateAt(lastBlock.Root())
  210. assert.Equal(t, uint64(258), statedb.GetNonce(testAddress))
  211. PS1, _ := privateStateRepo.StatePSI(types.PrivateStateIdentifier("PS1"))
  212. accPS1State := PS1.GetState(Contract1AddressAfterDeployment, common.Hash{})
  213. assert.Equal(t, common.BytesToHash(big.NewInt(129).Bytes()), accPS1State)
  214. PS2, _ := privateStateRepo.StatePSI(types.PrivateStateIdentifier("PS2"))
  215. accPS2State := PS2.GetState(Contract1AddressAfterDeployment, common.Hash{})
  216. // PS2 is incremented every other block - thus the (len(blocks)+1)/2 formula
  217. assert.Equal(t, common.BytesToHash(big.NewInt(65).Bytes()), accPS2State)
  218. // The following is an attempt to explain the process by which the trie nodes corresponding to PS1 and PS2 are being
  219. // garbage collected due to the TriesInMemory limit implemented in the blockchain
  220. // Expected state structure (block 2 - index 1 in the blocks array)
  221. // Public state just contains the testAddress(testKey) with nonce 4 and has PUB(BL2) root hash
  222. // PS1 has C1(2) and C2(2) with PS1(BL2) as root hash
  223. // PS2 has C1(1) and C2(2) with PS2(BL2) as root hash
  224. // the Trie of private states contains PS1(BL2) and PS2(BL2) and has TPS(BL2) root hash
  225. // the public state root references the trie of private states PUB(BL2) -> TPS(BL2)
  226. // the trie of private states leaves reference PS1(BL2) and PS2(BL2)
  227. // Considering the above we can establish that
  228. // PS1(BL2) is referenced once by the PS1 leaf in the trie of private states at block height 2
  229. // PS2(BL2) is referenced once by the PS2 leaf in the trie of private states at block height 2
  230. // retrieve the state at block height 2
  231. block1 := blocks[1]
  232. statedbB1, privateStateRepoB1, _ := blockchain.StateAt(block1.Root())
  233. assert.Equal(t, uint64(4), statedbB1.GetNonce(testAddress))
  234. PS1, _ = privateStateRepoB1.StatePSI(types.PrivateStateIdentifier("PS1"))
  235. PS1Root := PS1.IntermediateRoot(false)
  236. accPS1StateB1 := PS1.GetState(Contract1AddressAfterDeployment, common.Hash{})
  237. assert.Equal(t, common.BytesToHash([]byte{2}), accPS1StateB1)
  238. PS2, _ = privateStateRepoB1.StatePSI(types.PrivateStateIdentifier("PS2"))
  239. PS2Root := PS2.IntermediateRoot(false)
  240. accPS2StateB1 := PS2.GetState(Contract1AddressAfterDeployment, common.Hash{})
  241. // PS2 is incremented every other block - thus the (len(blocks)+1)/2 formula
  242. assert.Equal(t, common.BytesToHash([]byte{1}), accPS2StateB1)
  243. // check that the roots have NOT been written to the underlying DB
  244. contains, err := blockchain.db.Has(block1.Root().Bytes())
  245. assert.Nil(t, err)
  246. assert.False(t, contains)
  247. contains, err = blockchain.db.Has(PS1Root.Bytes())
  248. assert.Nil(t, err)
  249. assert.False(t, contains)
  250. contains, err = blockchain.db.Has(PS2Root.Bytes())
  251. assert.Nil(t, err)
  252. assert.False(t, contains)
  253. // check that the roots are available in the cache
  254. data, err := blockchain.stateCache.TrieDB().Node(block1.Root())
  255. assert.Nil(t, err)
  256. assert.True(t, len(data) > 0)
  257. data, err = blockchain.stateCache.TrieDB().Node(PS1Root)
  258. assert.Nil(t, err)
  259. assert.True(t, len(data) > 0)
  260. data, err = blockchain.stateCache.TrieDB().Node(PS2Root)
  261. assert.Nil(t, err)
  262. assert.True(t, len(data) > 0)
  263. // Process block 130 and reassess the underlying DB and the cache
  264. // When block 130 is processed the "chosen" in blockchain becomes 2 (130 - TriesInMemory) and the public root hash
  265. // PUB(BL2) is being de-referenced (reference count is reduced by 1) and the reference count (parents) becomes 0
  266. // As a result the trie of private states TPS(BL2) is also being de-referenced and becomes 0
  267. // Each leaf in the trie of private states is being de-referenced which then causes the private state roots PS1(BL2)
  268. // and PS2(BL2) to at block height 2 be de-referenced as well
  269. // All nodes with reference counts (parents) equal to 0 are being garbage collected (removed from the cache)
  270. count, err = blockchain.InsertChain(blocks[129:])
  271. assert.Nil(t, err)
  272. assert.Equal(t, 1, count)
  273. // check that the roots have NOT been written to the underlying DB
  274. contains, err = blockchain.db.Has(block1.Root().Bytes())
  275. assert.Nil(t, err)
  276. assert.False(t, contains)
  277. contains, err = blockchain.db.Has(PS1Root.Bytes())
  278. assert.Nil(t, err)
  279. assert.False(t, contains)
  280. contains, err = blockchain.db.Has(PS2Root.Bytes())
  281. assert.Nil(t, err)
  282. assert.False(t, contains)
  283. // check that the roots have been garbage collected (removed) from the cache (other intermediate trie nodes may have
  284. // been eliminated from the cache as well)
  285. data, err = blockchain.stateCache.TrieDB().Node(block1.Root())
  286. assert.Error(t, err, "not found")
  287. assert.Nil(t, data)
  288. data, err = blockchain.stateCache.TrieDB().Node(PS1Root)
  289. assert.Error(t, err, "not found")
  290. assert.Nil(t, data)
  291. data, err = blockchain.stateCache.TrieDB().Node(PS2Root)
  292. assert.Error(t, err, "not found")
  293. assert.Nil(t, data)
  294. }