github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/scc/qscc/query_test.go (about) 1 /* 2 Copyright hechain. 2017 All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package qscc 8 9 import ( 10 "fmt" 11 "io/ioutil" 12 "os" 13 "testing" 14 15 "github.com/hechain20/hechain/bccsp/sw" 16 "github.com/hechain20/hechain/common/ledger/testutil" 17 "github.com/hechain20/hechain/common/util" 18 "github.com/hechain20/hechain/core/aclmgmt/mocks" 19 "github.com/hechain20/hechain/core/aclmgmt/resources" 20 ledger2 "github.com/hechain20/hechain/core/ledger" 21 "github.com/hechain20/hechain/core/ledger/ledgermgmt" 22 "github.com/hechain20/hechain/core/ledger/ledgermgmt/ledgermgmttest" 23 "github.com/hechain20/hechain/core/peer" 24 "github.com/hechain20/hechain/protoutil" 25 "github.com/hyperledger/fabric-chaincode-go/shim" 26 "github.com/hyperledger/fabric-chaincode-go/shimtest" 27 "github.com/hyperledger/fabric-protos-go/common" 28 peer2 "github.com/hyperledger/fabric-protos-go/peer" 29 "github.com/pkg/errors" 30 "github.com/spf13/viper" 31 "github.com/stretchr/testify/require" 32 ) 33 34 func setupTestLedger(chainid string, path string) (*shimtest.MockStub, *peer.Peer, func(), error) { 35 mockAclProvider.Reset() 36 37 viper.Set("peer.fileSystemPath", path) 38 testDir, err := ioutil.TempDir("", "qscc_test") 39 if err != nil { 40 return nil, nil, nil, err 41 } 42 43 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 44 if err != nil { 45 return nil, nil, nil, err 46 } 47 48 initializer := ledgermgmttest.NewInitializer(testDir) 49 50 ledgerMgr := ledgermgmt.NewLedgerMgr(initializer) 51 52 cleanup := func() { 53 ledgerMgr.Close() 54 os.RemoveAll(testDir) 55 } 56 peerInstance := &peer.Peer{ 57 LedgerMgr: ledgerMgr, 58 CryptoProvider: cryptoProvider, 59 } 60 peer.CreateMockChannel(peerInstance, chainid, nil) 61 62 lq := &LedgerQuerier{ 63 aclProvider: mockAclProvider, 64 ledgers: peerInstance, 65 } 66 stub := shimtest.NewMockStub("LedgerQuerier", lq) 67 if res := stub.MockInit("1", nil); res.Status != shim.OK { 68 return nil, peerInstance, cleanup, fmt.Errorf("Init failed for test ledger [%s] with message: %s", chainid, string(res.Message)) 69 } 70 return stub, peerInstance, cleanup, nil 71 } 72 73 // pass the prop so we can conveniently inline it in the call and get it back 74 func resetProvider(res, chainid string, prop *peer2.SignedProposal, retErr error) *peer2.SignedProposal { 75 if prop == nil { 76 prop, _ = protoutil.MockSignedEndorserProposalOrPanic( 77 chainid, 78 &peer2.ChaincodeSpec{ 79 ChaincodeId: &peer2.ChaincodeID{ 80 Name: "qscc", 81 }, 82 }, 83 []byte("Alice"), 84 []byte("msg1"), 85 ) 86 } 87 mockAclProvider.Reset() 88 mockAclProvider.On("CheckACL", res, chainid, prop).Return(retErr) 89 return prop 90 } 91 92 func tempDir(t *testing.T, stem string) string { 93 path, err := ioutil.TempDir("", "qscc-"+stem) 94 require.NoError(t, err) 95 return path 96 } 97 98 func TestQueryGetChainInfo(t *testing.T) { 99 chainid := "mytestchainid1" 100 path := tempDir(t, "test1") 101 defer os.RemoveAll(path) 102 103 stub, _, cleanup, err := setupTestLedger(chainid, path) 104 if err != nil { 105 t.Fatalf(err.Error()) 106 } 107 defer cleanup() 108 109 args := [][]byte{[]byte(GetChainInfo), []byte(chainid)} 110 prop := resetProvider(resources.Qscc_GetChainInfo, chainid, nil, nil) 111 res := stub.MockInvokeWithSignedProposal("1", args, prop) 112 require.Equal(t, int32(shim.OK), res.Status, "GetChainInfo failed with err: %s", res.Message) 113 114 args = [][]byte{[]byte(GetChainInfo)} 115 res = stub.MockInvoke("2", args) 116 require.Equal(t, int32(shim.ERROR), res.Status, "GetChainInfo should have failed because no channel id was provided") 117 118 args = [][]byte{[]byte(GetChainInfo), []byte("fakechainid")} 119 res = stub.MockInvoke("3", args) 120 require.Equal(t, int32(shim.ERROR), res.Status, "GetChainInfo should have failed because the channel id does not exist") 121 } 122 123 func TestQueryGetTransactionByID(t *testing.T) { 124 chainid := "mytestchainid2" 125 path := tempDir(t, "test2") 126 defer os.RemoveAll(path) 127 128 stub, _, cleanup, err := setupTestLedger(chainid, path) 129 if err != nil { 130 t.Fatalf(err.Error()) 131 } 132 defer cleanup() 133 134 args := [][]byte{[]byte(GetTransactionByID), []byte(chainid), []byte("1")} 135 prop := resetProvider(resources.Qscc_GetTransactionByID, chainid, &peer2.SignedProposal{}, nil) 136 res := stub.MockInvokeWithSignedProposal("1", args, prop) 137 require.Equal(t, int32(shim.ERROR), res.Status, "GetTransactionByID should have failed with invalid txid: 1") 138 139 args = [][]byte{[]byte(GetTransactionByID), []byte(chainid), []byte(nil)} 140 res = stub.MockInvoke("2", args) 141 require.Equal(t, int32(shim.ERROR), res.Status, "GetTransactionByID should have failed with invalid txid: nil") 142 143 // Test with wrong number of parameters 144 args = [][]byte{[]byte(GetTransactionByID), []byte(chainid)} 145 res = stub.MockInvoke("3", args) 146 require.Equal(t, int32(shim.ERROR), res.Status, "GetTransactionByID should have failed due to incorrect number of arguments") 147 } 148 149 func TestQueryGetBlockByNumber(t *testing.T) { 150 chainid := "mytestchainid3" 151 path := tempDir(t, "test3") 152 defer os.RemoveAll(path) 153 154 stub, _, cleanup, err := setupTestLedger(chainid, path) 155 if err != nil { 156 t.Fatalf(err.Error()) 157 } 158 defer cleanup() 159 160 // block number 0 (genesis block) would already be present in the ledger 161 args := [][]byte{[]byte(GetBlockByNumber), []byte(chainid), []byte("0")} 162 prop := resetProvider(resources.Qscc_GetBlockByNumber, chainid, nil, nil) 163 res := stub.MockInvokeWithSignedProposal("1", args, prop) 164 require.Equal(t, int32(shim.OK), res.Status, "GetBlockByNumber should have succeeded for block number: 0") 165 166 // block number 1 should not be present in the ledger 167 args = [][]byte{[]byte(GetBlockByNumber), []byte(chainid), []byte("1")} 168 res = stub.MockInvoke("2", args) 169 require.Equal(t, int32(shim.ERROR), res.Status, "GetBlockByNumber should have failed with invalid number: 1") 170 171 // block number cannot be nil 172 args = [][]byte{[]byte(GetBlockByNumber), []byte(chainid), []byte(nil)} 173 res = stub.MockInvoke("3", args) 174 require.Equal(t, int32(shim.ERROR), res.Status, "GetBlockByNumber should have failed with nil block number") 175 } 176 177 func TestQueryGetBlockByHash(t *testing.T) { 178 chainid := "mytestchainid4" 179 path := tempDir(t, "test4") 180 defer os.RemoveAll(path) 181 182 stub, _, cleanup, err := setupTestLedger(chainid, path) 183 if err != nil { 184 t.Fatalf(err.Error()) 185 } 186 defer cleanup() 187 188 args := [][]byte{[]byte(GetBlockByHash), []byte(chainid), []byte("0")} 189 prop := resetProvider(resources.Qscc_GetBlockByHash, chainid, &peer2.SignedProposal{}, nil) 190 res := stub.MockInvokeWithSignedProposal("1", args, prop) 191 require.Equal(t, int32(shim.ERROR), res.Status, "GetBlockByHash should have failed with invalid hash: 0") 192 193 args = [][]byte{[]byte(GetBlockByHash), []byte(chainid), []byte(nil)} 194 res = stub.MockInvoke("2", args) 195 require.Equal(t, int32(shim.ERROR), res.Status, "GetBlockByHash should have failed with nil hash") 196 } 197 198 func TestQueryGetBlockByTxID(t *testing.T) { 199 chainid := "mytestchainid5" 200 path := tempDir(t, "test5") 201 defer os.RemoveAll(path) 202 203 stub, _, cleanup, err := setupTestLedger(chainid, path) 204 if err != nil { 205 t.Fatalf(err.Error()) 206 } 207 defer cleanup() 208 209 args := [][]byte{[]byte(GetBlockByTxID), []byte(chainid), []byte("")} 210 prop := resetProvider(resources.Qscc_GetBlockByTxID, chainid, &peer2.SignedProposal{}, nil) 211 res := stub.MockInvokeWithSignedProposal("1", args, prop) 212 require.Equal(t, int32(shim.ERROR), res.Status, "GetBlockByTxID should have failed with blank txId.") 213 } 214 215 func TestFailingCC2CC(t *testing.T) { 216 t.Run("BadProposal", func(t *testing.T) { 217 stub := shimtest.NewMockStub("testchannel", &LedgerQuerier{}) 218 args := [][]byte{[]byte("funcname"), []byte("testchannel")} 219 sProp := &peer2.SignedProposal{ 220 ProposalBytes: []byte("garbage"), 221 } 222 sProp.Signature = sProp.ProposalBytes 223 // Set the ACLProvider to have a failure 224 resetProvider(resources.Qscc_GetChainInfo, "testchannel", sProp, nil) 225 res := stub.MockInvokeWithSignedProposal("2", args, sProp) 226 require.Equal(t, int32(shim.ERROR), res.Status, "GetChainInfo must fail: %s", res.Message) 227 require.Contains(t, res.Message, "Failed to identify the called chaincode: could not unmarshal proposal: proto: can't skip unknown wire type 7") 228 }) 229 230 t.Run("DifferentInvokedCC", func(t *testing.T) { 231 stub := shimtest.NewMockStub("testchannel", &LedgerQuerier{}) 232 args := [][]byte{[]byte("funcname"), []byte("testchannel")} 233 sProp, _ := protoutil.MockSignedEndorserProposalOrPanic( 234 "testchannel", 235 &peer2.ChaincodeSpec{ 236 ChaincodeId: &peer2.ChaincodeID{ 237 Name: "usercc", 238 }, 239 }, 240 []byte("Alice"), 241 []byte("msg1"), 242 ) 243 sProp.Signature = sProp.ProposalBytes 244 // Set the ACLProvider to have a failure 245 resetProvider(resources.Qscc_GetChainInfo, "testchannel", sProp, nil) 246 res := stub.MockInvokeWithSignedProposal("2", args, sProp) 247 require.Equal(t, int32(shim.ERROR), res.Status, "GetChainInfo must fail: %s", res.Message) 248 require.Contains(t, res.Message, "Rejecting invoke of QSCC from another chaincode because of potential for deadlocks, original invocation for 'usercc'") 249 }) 250 } 251 252 func TestFailingAccessControl(t *testing.T) { 253 chainid := "mytestchainid6" 254 path := tempDir(t, "test6") 255 defer os.RemoveAll(path) 256 257 _, p, cleanup, err := setupTestLedger(chainid, path) 258 if err != nil { 259 t.Fatalf(err.Error()) 260 } 261 defer cleanup() 262 e := &LedgerQuerier{ 263 aclProvider: mockAclProvider, 264 ledgers: p, 265 } 266 stub := shimtest.NewMockStub("LedgerQuerier", e) 267 268 // GetChainInfo 269 args := [][]byte{[]byte(GetChainInfo), []byte(chainid)} 270 sProp, _ := protoutil.MockSignedEndorserProposalOrPanic(chainid, 271 &peer2.ChaincodeSpec{ 272 ChaincodeId: &peer2.ChaincodeID{ 273 Name: "qscc", 274 }, 275 }, 276 []byte("Alice"), 277 []byte("msg1"), 278 ) 279 sProp.Signature = sProp.ProposalBytes 280 // Set the ACLProvider to have a failure 281 resetProvider(resources.Qscc_GetChainInfo, chainid, sProp, errors.New("Failed access control")) 282 res := stub.MockInvokeWithSignedProposal("2", args, sProp) 283 require.Equal(t, int32(shim.ERROR), res.Status, "GetChainInfo must fail: %s", res.Message) 284 require.Contains(t, res.Message, "Failed access control") 285 // assert that the expectations were met 286 mockAclProvider.AssertExpectations(t) 287 288 // GetBlockByNumber 289 args = [][]byte{[]byte(GetBlockByNumber), []byte(chainid), []byte("1")} 290 sProp, _ = protoutil.MockSignedEndorserProposalOrPanic( 291 chainid, 292 &peer2.ChaincodeSpec{ 293 ChaincodeId: &peer2.ChaincodeID{ 294 Name: "qscc", 295 }, 296 }, 297 []byte("Alice"), 298 []byte("msg1"), 299 ) 300 sProp.Signature = sProp.ProposalBytes 301 // Set the ACLProvider to have a failure 302 resetProvider(resources.Qscc_GetBlockByNumber, chainid, sProp, errors.New("Failed access control")) 303 res = stub.MockInvokeWithSignedProposal("2", args, sProp) 304 require.Equal(t, int32(shim.ERROR), res.Status, "GetBlockByNumber must fail: %s", res.Message) 305 require.Contains(t, res.Message, "Failed access control") 306 // assert that the expectations were met 307 mockAclProvider.AssertExpectations(t) 308 309 // GetBlockByHash 310 args = [][]byte{[]byte(GetBlockByHash), []byte(chainid), []byte("1")} 311 sProp, _ = protoutil.MockSignedEndorserProposalOrPanic( 312 chainid, 313 &peer2.ChaincodeSpec{ 314 ChaincodeId: &peer2.ChaincodeID{ 315 Name: "qscc", 316 }, 317 }, 318 []byte("Alice"), 319 []byte("msg1"), 320 ) 321 sProp.Signature = sProp.ProposalBytes 322 // Set the ACLProvider to have a failure 323 resetProvider(resources.Qscc_GetBlockByHash, chainid, sProp, errors.New("Failed access control")) 324 res = stub.MockInvokeWithSignedProposal("2", args, sProp) 325 require.Equal(t, int32(shim.ERROR), res.Status, "GetBlockByHash must fail: %s", res.Message) 326 require.Contains(t, res.Message, "Failed access control") 327 // assert that the expectations were met 328 mockAclProvider.AssertExpectations(t) 329 330 // GetBlockByTxID 331 args = [][]byte{[]byte(GetBlockByTxID), []byte(chainid), []byte("1")} 332 sProp, _ = protoutil.MockSignedEndorserProposalOrPanic( 333 chainid, 334 &peer2.ChaincodeSpec{ 335 ChaincodeId: &peer2.ChaincodeID{ 336 Name: "qscc", 337 }, 338 }, 339 []byte("Alice"), 340 []byte("msg1"), 341 ) 342 sProp.Signature = sProp.ProposalBytes 343 // Set the ACLProvider to have a failure 344 resetProvider(resources.Qscc_GetBlockByTxID, chainid, sProp, errors.New("Failed access control")) 345 res = stub.MockInvokeWithSignedProposal("2", args, sProp) 346 require.Equal(t, int32(shim.ERROR), res.Status, "GetBlockByTxID must fail: %s", res.Message) 347 require.Contains(t, res.Message, "Failed access control") 348 // assert that the expectations were met 349 mockAclProvider.AssertExpectations(t) 350 351 // GetTransactionByID 352 args = [][]byte{[]byte(GetTransactionByID), []byte(chainid), []byte("1")} 353 sProp, _ = protoutil.MockSignedEndorserProposalOrPanic( 354 chainid, 355 &peer2.ChaincodeSpec{ 356 ChaincodeId: &peer2.ChaincodeID{ 357 Name: "qscc", 358 }, 359 }, 360 []byte("Alice"), 361 []byte("msg1"), 362 ) 363 sProp.Signature = sProp.ProposalBytes 364 // Set the ACLProvider to have a failure 365 resetProvider(resources.Qscc_GetTransactionByID, chainid, sProp, errors.New("Failed access control")) 366 res = stub.MockInvokeWithSignedProposal("2", args, sProp) 367 require.Equal(t, int32(shim.ERROR), res.Status, "Qscc_GetTransactionByID must fail: %s", res.Message) 368 require.Contains(t, res.Message, "Failed access control") 369 // assert that the expectations were met 370 mockAclProvider.AssertExpectations(t) 371 } 372 373 func TestQueryNonexistentFunction(t *testing.T) { 374 chainid := "mytestchainid7" 375 path := tempDir(t, "test7") 376 defer os.RemoveAll(path) 377 378 stub, _, cleanup, err := setupTestLedger(chainid, path) 379 if err != nil { 380 t.Fatalf(err.Error()) 381 } 382 defer cleanup() 383 384 args := [][]byte{[]byte("GetBlocks"), []byte(chainid), []byte("arg1")} 385 prop := resetProvider("qscc/GetBlocks", chainid, &peer2.SignedProposal{}, nil) 386 res := stub.MockInvokeWithSignedProposal("1", args, prop) 387 require.Equal(t, int32(shim.ERROR), res.Status, "GetBlocks should have failed because the function does not exist") 388 } 389 390 // TestQueryGeneratedBlock tests various queries for a newly generated block 391 // that contains two transactions 392 func TestQueryGeneratedBlock(t *testing.T) { 393 chainid := "mytestchainid8" 394 path := tempDir(t, "test8") 395 defer os.RemoveAll(path) 396 397 stub, p, cleanup, err := setupTestLedger(chainid, path) 398 if err != nil { 399 t.Fatalf(err.Error()) 400 } 401 defer cleanup() 402 403 block1 := addBlockForTesting(t, chainid, p) 404 405 // block number 1 should now exist 406 args := [][]byte{[]byte(GetBlockByNumber), []byte(chainid), []byte("1")} 407 prop := resetProvider(resources.Qscc_GetBlockByNumber, chainid, nil, nil) 408 res := stub.MockInvokeWithSignedProposal("1", args, prop) 409 require.Equal(t, int32(shim.OK), res.Status, "GetBlockByNumber should have succeeded for block number 1") 410 411 // block number 1 412 args = [][]byte{[]byte(GetBlockByHash), []byte(chainid), protoutil.BlockHeaderHash(block1.Header)} 413 prop = resetProvider(resources.Qscc_GetBlockByHash, chainid, nil, nil) 414 res = stub.MockInvokeWithSignedProposal("2", args, prop) 415 require.Equal(t, int32(shim.OK), res.Status, "GetBlockByHash should have succeeded for block 1 hash") 416 417 // drill into the block to find the transaction ids it contains 418 for _, d := range block1.Data.Data { 419 ebytes := d 420 if ebytes != nil { 421 if env, err := protoutil.GetEnvelopeFromBlock(ebytes); err != nil { 422 t.Fatalf("error getting envelope from block: %s", err) 423 } else if env != nil { 424 payload, err := protoutil.UnmarshalPayload(env.Payload) 425 if err != nil { 426 t.Fatalf("error extracting payload from envelope: %s", err) 427 } 428 chdr, err := protoutil.UnmarshalChannelHeader(payload.Header.ChannelHeader) 429 if err != nil { 430 t.Fatalf(err.Error()) 431 } 432 if common.HeaderType(chdr.Type) == common.HeaderType_ENDORSER_TRANSACTION { 433 args = [][]byte{[]byte(GetBlockByTxID), []byte(chainid), []byte(chdr.TxId)} 434 mockAclProvider.Reset() 435 prop = resetProvider(resources.Qscc_GetBlockByTxID, chainid, nil, nil) 436 res = stub.MockInvokeWithSignedProposal("3", args, prop) 437 require.Equal(t, int32(shim.OK), res.Status, "GetBlockByTxId should have succeeded for txid: %s", chdr.TxId) 438 439 args = [][]byte{[]byte(GetTransactionByID), []byte(chainid), []byte(chdr.TxId)} 440 prop = resetProvider(resources.Qscc_GetTransactionByID, chainid, nil, nil) 441 res = stub.MockInvokeWithSignedProposal("4", args, prop) 442 require.Equal(t, int32(shim.OK), res.Status, "GetTransactionById should have succeeded for txid: %s", chdr.TxId) 443 } 444 } 445 } 446 } 447 } 448 449 func addBlockForTesting(t *testing.T, chainid string, p *peer.Peer) *common.Block { 450 ledger := p.GetLedger(chainid) 451 defer ledger.Close() 452 453 txid1 := util.GenerateUUID() 454 simulator, _ := ledger.NewTxSimulator(txid1) 455 simulator.SetState("ns1", "key1", []byte("value1")) 456 simulator.SetState("ns1", "key2", []byte("value2")) 457 simulator.SetState("ns1", "key3", []byte("value3")) 458 simulator.Done() 459 simRes1, _ := simulator.GetTxSimulationResults() 460 pubSimResBytes1, _ := simRes1.GetPubSimulationBytes() 461 462 txid2 := util.GenerateUUID() 463 simulator, _ = ledger.NewTxSimulator(txid2) 464 simulator.SetState("ns2", "key4", []byte("value4")) 465 simulator.SetState("ns2", "key5", []byte("value5")) 466 simulator.SetState("ns2", "key6", []byte("value6")) 467 simulator.Done() 468 simRes2, _ := simulator.GetTxSimulationResults() 469 pubSimResBytes2, _ := simRes2.GetPubSimulationBytes() 470 471 bcInfo, err := ledger.GetBlockchainInfo() 472 require.NoError(t, err) 473 block1 := testutil.ConstructBlock(t, 1, bcInfo.CurrentBlockHash, [][]byte{pubSimResBytes1, pubSimResBytes2}, false) 474 ledger.CommitLegacy(&ledger2.BlockAndPvtData{Block: block1}, &ledger2.CommitOptions{}) 475 return block1 476 } 477 478 var mockAclProvider *mocks.MockACLProvider 479 480 func TestMain(m *testing.M) { 481 mockAclProvider = &mocks.MockACLProvider{} 482 mockAclProvider.Reset() 483 484 os.Exit(m.Run()) 485 }