package core import ( "context" "encoding/base64" "math/big" "testing" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core/mps" "github.com/ethereum/go-ethereum/core/privatecache" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/private" "github.com/ethereum/go-ethereum/private/engine" "github.com/ethereum/go-ethereum/rpc" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" ) const ( // testCode is the testing contract binary code which will initialises some // variables in constructor testCode = "0x60806040527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060005534801561003457600080fd5b5060fc806100436000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80630c4dae8814603757806398a213cf146053575b600080fd5b603d607e565b6040518082815260200191505060405180910390f35b607c60048036036020811015606757600080fd5b81019080803590602001909291905050506084565b005b60005481565b806000819055507fe9e44f9f7da8c559de847a3232b57364adc0354f15a2cd8dc636d54396f9587a6000546040518082815260200191505060405180910390a15056fea265627a7a723058208ae31d9424f2d0bc2a3da1a5dd659db2d71ec322a17db8f87e19e209e3a1ff4a64736f6c634300050a0032" // testGas is the gas required for contract deployment. testGas = 144109 ) var ( testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") testAddress = crypto.PubkeyToAddress(testKey.PublicKey) ) func buildTestChain(n int, config *params.ChainConfig) ([]*types.Block, map[common.Hash]*types.Block, *BlockChain) { testdb := rawdb.NewMemoryDatabase() genesis := GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000)) blocks, _ := GenerateChain(config, genesis, ethash.NewFaker(), testdb, n, func(i int, block *BlockGen) { block.SetCoinbase(common.Address{0}) signer := types.QuorumPrivateTxSigner{} tx, err := types.SignTx(types.NewContractCreation(block.TxNonce(testAddress), big.NewInt(0), testGas, nil, common.FromHex(testCode)), signer, testKey) if err != nil { panic(err) } block.AddTx(tx) }) hashes := make([]common.Hash, n+1) hashes[len(hashes)-1] = genesis.Hash() blockm := make(map[common.Hash]*types.Block, n+1) blockm[genesis.Hash()] = genesis for i, b := range blocks { hashes[len(hashes)-i-2] = b.Hash() blockm[b.Hash()] = b } blockchain, _ := NewBlockChain(testdb, nil, config, ethash.NewFaker(), vm.Config{}, nil, nil, nil) return blocks, blockm, blockchain } func TestMultiplePSMRStateCreated(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mockptm := private.NewMockPrivateTransactionManager(mockCtrl) saved := private.P defer func() { private.P = saved }() private.P = mockptm mockpsm := mps.NewMockPrivateStateManager(mockCtrl) mockptm.EXPECT().Receive(gomock.Not(common.EncryptedPayloadHash{})).Return("", []string{"psi1", "psi2"}, common.FromHex(testCode), nil, nil).AnyTimes() mockptm.EXPECT().Receive(common.EncryptedPayloadHash{}).Return("", []string{}, common.EncryptedPayloadHash{}.Bytes(), nil, nil).AnyTimes() mockptm.EXPECT().HasFeature(engine.MultiplePrivateStates).Return(true) mockptm.EXPECT().Groups().Return(PrivacyGroups, nil).AnyTimes() mockpsm.EXPECT().ResolveForManagedParty("psi1").Return(&PSI1PSM, nil).AnyTimes() mockpsm.EXPECT().ResolveForManagedParty("psi2").Return(&PSI2PSM, nil).AnyTimes() mockpsm.EXPECT().PSIs().Return([]types.PrivateStateIdentifier{PSI1PSM.ID, PSI2PSM.ID, types.DefaultPrivateStateIdentifier, types.ToPrivateStateIdentifier("other")}).AnyTimes() blocks, blockmap, blockchain := buildTestChain(2, params.QuorumMPSTestChainConfig) cache := state.NewDatabase(blockchain.db) privateCacheProvider := privatecache.NewPrivateCacheProvider(blockchain.db, nil, cache, false) blockchain.privateStateManager = mockpsm for _, block := range blocks { parent := blockmap[block.ParentHash()] statedb, _ := state.New(parent.Root(), blockchain.StateCache(), nil) mockpsm.EXPECT().StateRepository(gomock.Any()).Return(mps.NewMultiplePrivateStateRepository(blockchain.db, cache, common.Hash{}, privateCacheProvider)).AnyTimes() privateStateRepo, err := blockchain.PrivateStateManager().StateRepository(parent.Root()) assert.NoError(t, err) publicReceipts, privateReceipts, _, _, _ := blockchain.Processor().Process(block, statedb, privateStateRepo, vm.Config{}) //managed states tests for _, privateReceipt := range privateReceipts { expectedContractAddress := privateReceipt.ContractAddress emptyState, _ := privateStateRepo.DefaultState() assert.True(t, emptyState.Exist(expectedContractAddress)) assert.Equal(t, emptyState.GetCodeSize(expectedContractAddress), 0) ps1, _ := privateStateRepo.StatePSI(types.PrivateStateIdentifier("psi1")) assert.True(t, ps1.Exist(expectedContractAddress)) assert.NotEqual(t, ps1.GetCodeSize(expectedContractAddress), 0) ps2, _ := privateStateRepo.StatePSI(types.PrivateStateIdentifier("psi2")) assert.True(t, ps2.Exist(expectedContractAddress)) assert.NotEqual(t, ps2.GetCodeSize(expectedContractAddress), 0) } //CommitAndWrite to db privateStateRepo.CommitAndWrite(false, block) //managed states test for _, privateReceipt := range privateReceipts { expectedContractAddress := privateReceipt.ContractAddress latestBlockRoot := block.Root() _, privDb, _ := blockchain.StateAtPSI(latestBlockRoot, types.ToPrivateStateIdentifier("empty")) assert.True(t, privDb.Exist(expectedContractAddress)) assert.Equal(t, privDb.GetCodeSize(expectedContractAddress), 0) //contract exists on both psi states _, privDb, _ = blockchain.StateAtPSI(latestBlockRoot, types.PrivateStateIdentifier("psi1")) assert.True(t, privDb.Exist(expectedContractAddress)) assert.NotEqual(t, privDb.GetCodeSize(expectedContractAddress), 0) _, privDb, _ = blockchain.StateAtPSI(latestBlockRoot, types.PrivateStateIdentifier("psi2")) assert.True(t, privDb.Exist(expectedContractAddress)) assert.NotEqual(t, privDb.GetCodeSize(expectedContractAddress), 0) //contract should exist on default private state but no contract code _, privDb, _ = blockchain.StateAtPSI(latestBlockRoot, types.DefaultPrivateStateIdentifier) assert.True(t, privDb.Exist(expectedContractAddress)) assert.Equal(t, privDb.GetCodeSize(expectedContractAddress), 0) //contract should exist on random state but no contract code _, privDb, _ = blockchain.StateAtPSI(latestBlockRoot, types.ToPrivateStateIdentifier("other")) assert.True(t, privDb.Exist(expectedContractAddress)) assert.Equal(t, privDb.GetCodeSize(expectedContractAddress), 0) } //mergeReceipts test for _, pubReceipt := range publicReceipts { assert.Equal(t, 0, len(pubReceipt.PSReceipts)) } for _, privReceipt := range privateReceipts { assert.Equal(t, 2, len(privReceipt.PSReceipts)) assert.NotEqual(t, nil, privReceipt.PSReceipts["psi1"]) assert.NotEqual(t, nil, privReceipt.PSReceipts["psi2"]) } allReceipts := privateStateRepo.MergeReceipts(publicReceipts, privateReceipts) for _, receipt := range allReceipts { assert.Equal(t, 3, len(receipt.PSReceipts)) assert.NotEqual(t, nil, receipt.PSReceipts["empty"]) assert.NotEqual(t, nil, receipt.PSReceipts["psi1"]) assert.NotEqual(t, nil, receipt.PSReceipts["psi2"]) } } } func TestMPSReset(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mockptm := private.NewMockPrivateTransactionManager(mockCtrl) saved := private.P defer func() { private.P = saved }() private.P = mockptm mockpsm := mps.NewMockPrivateStateManager(mockCtrl) mockptm.EXPECT().Receive(gomock.Not(common.EncryptedPayloadHash{})).Return("", []string{"psi1", "psi2"}, common.FromHex(testCode), nil, nil).AnyTimes() mockptm.EXPECT().Receive(common.EncryptedPayloadHash{}).Return("", []string{}, common.EncryptedPayloadHash{}.Bytes(), nil, nil).AnyTimes() mockptm.EXPECT().HasFeature(engine.MultiplePrivateStates).Return(true) mockptm.EXPECT().Groups().Return(PrivacyGroups, nil).AnyTimes() mockpsm.EXPECT().ResolveForManagedParty("psi1").Return(&PSI1PSM, nil).AnyTimes() mockpsm.EXPECT().ResolveForManagedParty("psi2").Return(&PSI2PSM, nil).AnyTimes() mockpsm.EXPECT().PSIs().Return([]types.PrivateStateIdentifier{PSI1PSM.ID, PSI2PSM.ID}).AnyTimes() blocks, blockmap, blockchain := buildTestChain(2, params.QuorumMPSTestChainConfig) blockchain.privateStateManager = mockpsm cache := state.NewDatabase(blockchain.db) privateCacheProvider := privatecache.NewPrivateCacheProvider(blockchain.db, nil, cache, false) for _, block := range blocks { parent := blockmap[block.ParentHash()] statedb, _ := state.New(parent.Root(), blockchain.StateCache(), nil) mockpsm.EXPECT().StateRepository(gomock.Any()).Return(mps.NewMultiplePrivateStateRepository(blockchain.db, cache, common.Hash{}, privateCacheProvider)).AnyTimes() privateStateRepo, err := blockchain.PrivateStateManager().StateRepository(parent.Root()) assert.NoError(t, err) _, privateReceipts, _, _, _ := blockchain.Processor().Process(block, statedb, privateStateRepo, vm.Config{}) for _, privateReceipt := range privateReceipts { expectedContractAddress := privateReceipt.ContractAddress emptyState, _ := privateStateRepo.DefaultState() assert.True(t, emptyState.Exist(expectedContractAddress)) assert.Equal(t, emptyState.GetCodeSize(expectedContractAddress), 0) ps1, _ := privateStateRepo.StatePSI(types.PrivateStateIdentifier("psi1")) assert.True(t, ps1.Exist(expectedContractAddress)) assert.NotEqual(t, ps1.GetCodeSize(expectedContractAddress), 0) ps2, _ := privateStateRepo.StatePSI(types.PrivateStateIdentifier("psi2")) assert.True(t, ps2.Exist(expectedContractAddress)) assert.NotEqual(t, ps2.GetCodeSize(expectedContractAddress), 0) privateStateRepo.Reset() emptyState, _ = privateStateRepo.DefaultState() assert.False(t, emptyState.Exist(expectedContractAddress)) assert.Equal(t, emptyState.GetCodeSize(expectedContractAddress), 0) ps1, _ = privateStateRepo.StatePSI(types.PrivateStateIdentifier("psi1")) assert.False(t, ps1.Exist(expectedContractAddress)) assert.Equal(t, ps1.GetCodeSize(expectedContractAddress), 0) ps2, _ = privateStateRepo.StatePSI(types.PrivateStateIdentifier("psi2")) assert.False(t, ps2.Exist(expectedContractAddress)) assert.Equal(t, ps2.GetCodeSize(expectedContractAddress), 0) } } } func TestPrivateStateMetadataResolver(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mockptm := private.NewMockPrivateTransactionManager(mockCtrl) saved := private.P defer func() { private.P = saved }() private.P = mockptm mockptm.EXPECT().Receive(gomock.Not(common.EncryptedPayloadHash{})).Return("", []string{"AAA", "CCC"}, common.FromHex(testCode), nil, nil).AnyTimes() mockptm.EXPECT().Receive(common.EncryptedPayloadHash{}).Return("", []string{}, common.EncryptedPayloadHash{}.Bytes(), nil, nil).AnyTimes() mockptm.EXPECT().HasFeature(engine.MultiplePrivateStates).Return(true) mockptm.EXPECT().Groups().Return(PrivacyGroups, nil).AnyTimes() _, _, blockchain := buildTestChain(1, params.QuorumMPSTestChainConfig) mpsm := blockchain.privateStateManager psm1, _ := mpsm.ResolveForManagedParty("AAA") psm2, _ := mpsm.ResolveForManagedParty("CCC") _, err := mpsm.ResolveForManagedParty("TEST") assert.Equal(t, psm1, privacyGroupToPrivateStateMetadata(PG1)) assert.Equal(t, psm2, privacyGroupToPrivateStateMetadata(PG2)) assert.Error(t, err, "unable to find private state metadata for managed party TEST") ctx := rpc.WithPrivateStateIdentifier(context.Background(), types.ToPrivateStateIdentifier("RG1")) psm1, _ = mpsm.ResolveForUserContext(ctx) assert.Equal(t, psm1, privacyGroupToPrivateStateMetadata(PG1)) ctx = rpc.WithPrivateStateIdentifier(context.Background(), types.ToPrivateStateIdentifier("OTHER")) _, err = mpsm.ResolveForUserContext(ctx) assert.Error(t, err, "unable to find private state for context psi OTHER") _, err = mpsm.ResolveForUserContext(context.Background()) assert.Error(t, err, "unable to find private state for context psi private") assert.Contains(t, mpsm.PSIs(), types.PrivateStateIdentifier("RG1")) assert.Contains(t, mpsm.PSIs(), types.PrivateStateIdentifier("RG2")) assert.Contains(t, mpsm.PSIs(), types.PrivateStateIdentifier("LEGACY1")) } var PSI1PSM = mps.PrivateStateMetadata{ ID: "psi1", Name: "psi1", Description: "private state 1", Type: mps.Resident, Addresses: nil, } var PSI2PSM = mps.PrivateStateMetadata{ ID: "psi2", Name: "psi2", Description: "private state 2", Type: mps.Resident, Addresses: nil, } var PG1 = engine.PrivacyGroup{ Type: "RESIDENT", Name: "RG1", PrivacyGroupId: "RG1", Description: "Resident Group 1", From: "", Members: []string{"AAA", "BBB"}, } var PG2 = engine.PrivacyGroup{ Type: "RESIDENT", Name: "RG2", PrivacyGroupId: "RG2", Description: "Resident Group 2", From: "", Members: []string{"CCC", "DDD"}, } var PrivacyGroups = []engine.PrivacyGroup{ { Type: "RESIDENT", Name: "RG1", PrivacyGroupId: base64.StdEncoding.EncodeToString([]byte("RG1")), Description: "Resident Group 1", From: "", Members: []string{"AAA", "BBB"}, }, { Type: "RESIDENT", Name: "RG2", PrivacyGroupId: base64.StdEncoding.EncodeToString([]byte("RG2")), Description: "Resident Group 2", From: "", Members: []string{"CCC", "DDD"}, }, { Type: "LEGACY", Name: "LEGACY1", PrivacyGroupId: "LEGACY1", Description: "Legacy Group 1", From: "", Members: []string{"LEG1", "LEG2"}, }, }