github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+incompatible/core/scc/qscc/query_test.go (about) 1 /* 2 Copyright IBM Corp. 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/hyperledger/fabric-chaincode-go/shim" 16 "github.com/hyperledger/fabric-chaincode-go/shimtest" 17 "github.com/hyperledger/fabric-protos-go/common" 18 peer2 "github.com/hyperledger/fabric-protos-go/peer" 19 "github.com/hyperledger/fabric/bccsp/sw" 20 "github.com/hyperledger/fabric/common/ledger/testutil" 21 "github.com/hyperledger/fabric/common/util" 22 "github.com/hyperledger/fabric/core/aclmgmt/mocks" 23 "github.com/hyperledger/fabric/core/aclmgmt/resources" 24 ledger2 "github.com/hyperledger/fabric/core/ledger" 25 "github.com/hyperledger/fabric/core/ledger/ledgermgmt" 26 "github.com/hyperledger/fabric/core/ledger/ledgermgmt/ledgermgmttest" 27 "github.com/hyperledger/fabric/core/peer" 28 "github.com/hyperledger/fabric/protoutil" 29 "github.com/pkg/errors" 30 "github.com/spf13/viper" 31 "github.com/stretchr/testify/assert" 32 "github.com/stretchr/testify/require" 33 ) 34 35 func setupTestLedger(chainid string, path string) (*shimtest.MockStub, *peer.Peer, func(), error) { 36 mockAclProvider.Reset() 37 38 viper.Set("peer.fileSystemPath", path) 39 testDir, err := ioutil.TempDir("", "qscc_test") 40 if err != nil { 41 return nil, nil, nil, err 42 } 43 44 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 45 if err != nil { 46 return nil, nil, nil, err 47 } 48 49 initializer := ledgermgmttest.NewInitializer(testDir) 50 51 ledgerMgr := ledgermgmt.NewLedgerMgr(initializer) 52 53 cleanup := func() { 54 ledgerMgr.Close() 55 os.RemoveAll(testDir) 56 } 57 peerInstance := &peer.Peer{ 58 LedgerMgr: ledgerMgr, 59 CryptoProvider: cryptoProvider, 60 } 61 peer.CreateMockChannel(peerInstance, chainid, nil) 62 63 lq := &LedgerQuerier{ 64 aclProvider: mockAclProvider, 65 ledgers: peerInstance, 66 } 67 stub := shimtest.NewMockStub("LedgerQuerier", lq) 68 if res := stub.MockInit("1", nil); res.Status != shim.OK { 69 return nil, peerInstance, cleanup, fmt.Errorf("Init failed for test ledger [%s] with message: %s", chainid, string(res.Message)) 70 } 71 return stub, peerInstance, cleanup, nil 72 } 73 74 //pass the prop so we can conveniently inline it in the call and get it back 75 func resetProvider(res, chainid string, prop *peer2.SignedProposal, retErr error) *peer2.SignedProposal { 76 if prop == nil { 77 prop, _ = protoutil.MockSignedEndorserProposalOrPanic( 78 chainid, 79 &peer2.ChaincodeSpec{ 80 ChaincodeId: &peer2.ChaincodeID{ 81 Name: "qscc", 82 }, 83 }, 84 []byte("Alice"), 85 []byte("msg1"), 86 ) 87 } 88 mockAclProvider.Reset() 89 mockAclProvider.On("CheckACL", res, chainid, prop).Return(retErr) 90 return prop 91 } 92 93 func tempDir(t *testing.T, stem string) string { 94 path, err := ioutil.TempDir("", "qscc-"+stem) 95 require.NoError(t, err) 96 return path 97 } 98 99 func TestQueryGetChainInfo(t *testing.T) { 100 chainid := "mytestchainid1" 101 path := tempDir(t, "test1") 102 defer os.RemoveAll(path) 103 104 stub, _, cleanup, err := setupTestLedger(chainid, path) 105 if err != nil { 106 t.Fatalf(err.Error()) 107 } 108 defer cleanup() 109 110 args := [][]byte{[]byte(GetChainInfo), []byte(chainid)} 111 prop := resetProvider(resources.Qscc_GetChainInfo, chainid, nil, nil) 112 res := stub.MockInvokeWithSignedProposal("1", args, prop) 113 assert.Equal(t, int32(shim.OK), res.Status, "GetChainInfo failed with err: %s", res.Message) 114 115 args = [][]byte{[]byte(GetChainInfo)} 116 res = stub.MockInvoke("2", args) 117 assert.Equal(t, int32(shim.ERROR), res.Status, "GetChainInfo should have failed because no channel id was provided") 118 119 args = [][]byte{[]byte(GetChainInfo), []byte("fakechainid")} 120 res = stub.MockInvoke("3", args) 121 assert.Equal(t, int32(shim.ERROR), res.Status, "GetChainInfo should have failed because the channel id does not exist") 122 } 123 124 func TestQueryGetTransactionByID(t *testing.T) { 125 chainid := "mytestchainid2" 126 path := tempDir(t, "test2") 127 defer os.RemoveAll(path) 128 129 stub, _, cleanup, err := setupTestLedger(chainid, path) 130 if err != nil { 131 t.Fatalf(err.Error()) 132 } 133 defer cleanup() 134 135 args := [][]byte{[]byte(GetTransactionByID), []byte(chainid), []byte("1")} 136 prop := resetProvider(resources.Qscc_GetTransactionByID, chainid, &peer2.SignedProposal{}, nil) 137 res := stub.MockInvokeWithSignedProposal("1", args, prop) 138 assert.Equal(t, int32(shim.ERROR), res.Status, "GetTransactionByID should have failed with invalid txid: 1") 139 140 args = [][]byte{[]byte(GetTransactionByID), []byte(chainid), []byte(nil)} 141 res = stub.MockInvoke("2", args) 142 assert.Equal(t, int32(shim.ERROR), res.Status, "GetTransactionByID should have failed with invalid txid: nil") 143 144 // Test with wrong number of parameters 145 args = [][]byte{[]byte(GetTransactionByID), []byte(chainid)} 146 res = stub.MockInvoke("3", args) 147 assert.Equal(t, int32(shim.ERROR), res.Status, "GetTransactionByID should have failed due to incorrect number of arguments") 148 } 149 150 func TestQueryGetBlockByNumber(t *testing.T) { 151 chainid := "mytestchainid3" 152 path := tempDir(t, "test3") 153 defer os.RemoveAll(path) 154 155 stub, _, cleanup, err := setupTestLedger(chainid, path) 156 if err != nil { 157 t.Fatalf(err.Error()) 158 } 159 defer cleanup() 160 161 // block number 0 (genesis block) would already be present in the ledger 162 args := [][]byte{[]byte(GetBlockByNumber), []byte(chainid), []byte("0")} 163 prop := resetProvider(resources.Qscc_GetBlockByNumber, chainid, nil, nil) 164 res := stub.MockInvokeWithSignedProposal("1", args, prop) 165 assert.Equal(t, int32(shim.OK), res.Status, "GetBlockByNumber should have succeeded for block number: 0") 166 167 // block number 1 should not be present in the ledger 168 args = [][]byte{[]byte(GetBlockByNumber), []byte(chainid), []byte("1")} 169 res = stub.MockInvoke("2", args) 170 assert.Equal(t, int32(shim.ERROR), res.Status, "GetBlockByNumber should have failed with invalid number: 1") 171 172 // block number cannot be nil 173 args = [][]byte{[]byte(GetBlockByNumber), []byte(chainid), []byte(nil)} 174 res = stub.MockInvoke("3", args) 175 assert.Equal(t, int32(shim.ERROR), res.Status, "GetBlockByNumber should have failed with nil block number") 176 } 177 178 func TestQueryGetBlockByHash(t *testing.T) { 179 chainid := "mytestchainid4" 180 path := tempDir(t, "test4") 181 defer os.RemoveAll(path) 182 183 stub, _, cleanup, err := setupTestLedger(chainid, path) 184 if err != nil { 185 t.Fatalf(err.Error()) 186 } 187 defer cleanup() 188 189 args := [][]byte{[]byte(GetBlockByHash), []byte(chainid), []byte("0")} 190 prop := resetProvider(resources.Qscc_GetBlockByHash, chainid, &peer2.SignedProposal{}, nil) 191 res := stub.MockInvokeWithSignedProposal("1", args, prop) 192 assert.Equal(t, int32(shim.ERROR), res.Status, "GetBlockByHash should have failed with invalid hash: 0") 193 194 args = [][]byte{[]byte(GetBlockByHash), []byte(chainid), []byte(nil)} 195 res = stub.MockInvoke("2", args) 196 assert.Equal(t, int32(shim.ERROR), res.Status, "GetBlockByHash should have failed with nil hash") 197 } 198 199 func TestQueryGetBlockByTxID(t *testing.T) { 200 chainid := "mytestchainid5" 201 path := tempDir(t, "test5") 202 defer os.RemoveAll(path) 203 204 stub, _, cleanup, err := setupTestLedger(chainid, path) 205 if err != nil { 206 t.Fatalf(err.Error()) 207 } 208 defer cleanup() 209 210 args := [][]byte{[]byte(GetBlockByTxID), []byte(chainid), []byte("")} 211 prop := resetProvider(resources.Qscc_GetBlockByTxID, chainid, &peer2.SignedProposal{}, nil) 212 res := stub.MockInvokeWithSignedProposal("1", args, prop) 213 assert.Equal(t, int32(shim.ERROR), res.Status, "GetBlockByTxID should have failed with blank txId.") 214 } 215 216 func TestFailingCC2CC(t *testing.T) { 217 t.Run("BadProposal", func(t *testing.T) { 218 stub := shimtest.NewMockStub("testchannel", &LedgerQuerier{}) 219 args := [][]byte{[]byte("funcname"), []byte("testchannel")} 220 sProp := &peer2.SignedProposal{ 221 ProposalBytes: []byte("garbage"), 222 } 223 sProp.Signature = sProp.ProposalBytes 224 // Set the ACLProvider to have a failure 225 resetProvider(resources.Qscc_GetChainInfo, "testchannel", sProp, nil) 226 res := stub.MockInvokeWithSignedProposal("2", args, sProp) 227 assert.Equal(t, int32(shim.ERROR), res.Status, "GetChainInfo must fail: %s", res.Message) 228 assert.Contains(t, res.Message, "Failed to identify the called chaincode: could not unmarshal proposal: proto: can't skip unknown wire type 7") 229 }) 230 231 t.Run("DifferentInvokedCC", func(t *testing.T) { 232 stub := shimtest.NewMockStub("testchannel", &LedgerQuerier{}) 233 args := [][]byte{[]byte("funcname"), []byte("testchannel")} 234 sProp, _ := protoutil.MockSignedEndorserProposalOrPanic( 235 "testchannel", 236 &peer2.ChaincodeSpec{ 237 ChaincodeId: &peer2.ChaincodeID{ 238 Name: "usercc", 239 }, 240 }, 241 []byte("Alice"), 242 []byte("msg1"), 243 ) 244 sProp.Signature = sProp.ProposalBytes 245 // Set the ACLProvider to have a failure 246 resetProvider(resources.Qscc_GetChainInfo, "testchannel", sProp, nil) 247 res := stub.MockInvokeWithSignedProposal("2", args, sProp) 248 assert.Equal(t, int32(shim.ERROR), res.Status, "GetChainInfo must fail: %s", res.Message) 249 assert.Contains(t, res.Message, "Rejecting invoke of QSCC from another chaincode because of potential for deadlocks, original invocation for 'usercc'") 250 }) 251 } 252 253 func TestFailingAccessControl(t *testing.T) { 254 chainid := "mytestchainid6" 255 path := tempDir(t, "test6") 256 defer os.RemoveAll(path) 257 258 _, p, cleanup, err := setupTestLedger(chainid, path) 259 if err != nil { 260 t.Fatalf(err.Error()) 261 } 262 defer cleanup() 263 e := &LedgerQuerier{ 264 aclProvider: mockAclProvider, 265 ledgers: p, 266 } 267 stub := shimtest.NewMockStub("LedgerQuerier", e) 268 269 // GetChainInfo 270 args := [][]byte{[]byte(GetChainInfo), []byte(chainid)} 271 sProp, _ := protoutil.MockSignedEndorserProposalOrPanic(chainid, 272 &peer2.ChaincodeSpec{ 273 ChaincodeId: &peer2.ChaincodeID{ 274 Name: "qscc"}, 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 assert.Equal(t, int32(shim.ERROR), res.Status, "GetChainInfo must fail: %s", res.Message) 284 assert.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 assert.Equal(t, int32(shim.ERROR), res.Status, "GetBlockByNumber must fail: %s", res.Message) 305 assert.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 assert.Equal(t, int32(shim.ERROR), res.Status, "GetBlockByHash must fail: %s", res.Message) 326 assert.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 assert.Equal(t, int32(shim.ERROR), res.Status, "GetBlockByTxID must fail: %s", res.Message) 347 assert.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 assert.Equal(t, int32(shim.ERROR), res.Status, "Qscc_GetTransactionByID must fail: %s", res.Message) 368 assert.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 assert.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 assert.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 assert.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 assert.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 assert.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 assert.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 }