@@ -14,6 +14,7 @@ import (
1414 "strings"
1515 "sync"
1616 "testing"
17+ "time"
1718
1819 "github.com/hyperledger/fabric-sdk-go/pkg/common/errors/retry"
1920 "github.com/hyperledger/fabric-sdk-go/pkg/common/errors/status"
@@ -366,3 +367,221 @@ func newCollectionConfig(colName, policy string, reqPeerCount, maxPeerCount int3
366367 },
367368 }, nil
368369}
370+
371+ // TestPrivateDataReconcilePutAndGet tests put and get for private data with reconciliation of missing eligible data on some peers (org2's peers)
372+ // the idea to test private data reconciliation is to set a test collection with a policy of 1 member org, put/get private data
373+ // then update the collection config with a new policy of 2 member orgs, private data should be reconciled on peers of the newly added org
374+ func TestPrivateDataReconcilePutAndGet (t * testing.T ) {
375+ sdk := mainSDK
376+ singleOrgPolicy := "AND('Org1MSP.member')"
377+ multiOrgsPolicy := "OR('Org1MSP.member','Org2MSP.member')"
378+ coll1 := "collectionx"
379+ ccID := integration .GenerateExamplePvtID (true )
380+ orgsContext := setupMultiOrgContext (t , sdk )
381+
382+ // instantiate and install CC on all peers using collection policy for org1 only then put/get some pvt data
383+ runPvtDataPreReconcilePutAndGet (t , sdk , orgsContext , singleOrgPolicy , ccID , coll1 )
384+ // now verify pvt data is not available on org2 peers
385+ verifyPvtDataPreReconcileGet (t , sdk , ccID , coll1 )
386+ // upgrade CC to include org2 in collection policy then verify pvt data is available on org2's peers
387+ runPvtDataPostReconcileGet (t , sdk , orgsContext , multiOrgsPolicy , ccID , coll1 )
388+ }
389+
390+ func runPvtDataPreReconcilePutAndGet (t * testing.T , sdk * fabsdk.FabricSDK , orgsContext []* integration.OrgContext , policy , ccID , coll1 string ) {
391+ err := integration .EnsureChannelCreatedAndPeersJoined (t , sdk , orgChannelID , "orgchannel.tx" , orgsContext )
392+ require .NoError (t , err )
393+
394+ collConfig , err := newCollectionConfig (coll1 , policy , 0 , 2 , 1000 )
395+ require .NoError (t , err )
396+
397+ err = integration .InstallExamplePvtChaincode (orgsContext , ccID )
398+ require .NoError (t , err )
399+ err = integration .InstantiateExamplePvtChaincode (orgsContext , orgChannelID , ccID , policy , collConfig )
400+ require .NoError (t , err )
401+
402+ ctxProvider := sdk .ChannelContext (orgChannelID , fabsdk .WithUser (org1User ), fabsdk .WithOrg (org1Name ))
403+
404+ chClient , err := channel .New (ctxProvider )
405+ require .NoError (t , err )
406+
407+ key1 := "key1"
408+ key2 := "key2"
409+ key3 := "key3"
410+ value1 := "pvtValue1"
411+ value2 := "pvtValue2"
412+ value3 := "pvtValue3"
413+
414+ response , err := chClient .Query (
415+ channel.Request {
416+ ChaincodeID : ccID ,
417+ Fcn : "getprivate" ,
418+ Args : [][]byte {[]byte (coll1 ), []byte (key1 )},
419+ },
420+ channel .WithRetry (retry .DefaultChannelOpts ),
421+ )
422+ require .NoError (t , err )
423+ t .Logf ("Got response payload: [%s]" , string (response .Payload ))
424+ require .Nil (t , response .Payload )
425+
426+ response , err = chClient .Query (
427+ channel.Request {
428+ ChaincodeID : ccID ,
429+ Fcn : "getprivatebyrange" ,
430+ Args : [][]byte {[]byte (coll1 ), []byte (key1 ), []byte (key3 )},
431+ },
432+ channel .WithRetry (retry .DefaultChannelOpts ),
433+ )
434+ require .NoError (t , err )
435+ t .Logf ("Got response payload: [%s]" , string (response .Payload ))
436+ require .Empty (t , string (response .Payload ))
437+
438+ response , err = chClient .Execute (
439+ channel.Request {
440+ ChaincodeID : ccID ,
441+ Fcn : "putprivate" ,
442+ Args : [][]byte {[]byte (coll1 ), []byte (key1 ), []byte (value1 )},
443+ },
444+ channel .WithRetry (retry .DefaultChannelOpts ),
445+ )
446+ require .NoError (t , err )
447+ require .NotEmptyf (t , response .Responses , "expecting at least one response" )
448+
449+ response , err = chClient .Execute (
450+ channel.Request {
451+ ChaincodeID : ccID ,
452+ Fcn : "putprivate" ,
453+ Args : [][]byte {[]byte (coll1 ), []byte (key2 ), []byte (value2 )},
454+ },
455+ channel .WithRetry (retry .DefaultChannelOpts ),
456+ )
457+ require .NoError (t , err )
458+ require .NotEmptyf (t , response .Responses , "expecting at least one response" )
459+
460+ response , err = chClient .Execute (
461+ channel.Request {
462+ ChaincodeID : ccID ,
463+ Fcn : "putprivate" ,
464+ Args : [][]byte {[]byte (coll1 ), []byte (key3 ), []byte (value3 )},
465+ },
466+ channel .WithRetry (retry .TestRetryOpts ),
467+ )
468+ require .NoError (t , err )
469+ require .NotEmptyf (t , response .Responses , "expecting at least one response" )
470+
471+ response , err = chClient .Query (
472+ channel.Request {
473+ ChaincodeID : ccID ,
474+ Fcn : "getprivate" ,
475+ Args : [][]byte {[]byte (coll1 ), []byte (key1 )},
476+ },
477+ channel .WithRetry (retry .TestRetryOpts ),
478+ )
479+ require .NoError (t , err )
480+ t .Logf ("Got response payload for getprivate: %s" , string (response .Payload ))
481+ require .Equal (t , value1 , string (response .Payload ))
482+
483+ response , err = chClient .Query (
484+ channel.Request {
485+ ChaincodeID : ccID ,
486+ Fcn : "getprivatebyrange" ,
487+ Args : [][]byte {[]byte (coll1 ), []byte (key1 ), []byte (key3 )},
488+ },
489+ channel .WithRetry (retry .DefaultChannelOpts ),
490+ )
491+ require .NoError (t , err )
492+ t .Logf ("Got response payload for getprivatebyrange: [%s]" , string (response .Payload ))
493+ require .NotEmpty (t , string (response .Payload ))
494+ }
495+
496+ func verifyPvtDataPreReconcileGet (t * testing.T , sdk * fabsdk.FabricSDK , ccID , coll1 string ) {
497+ // create ctxProvider for org2 to query org2 peers for pvt data (should be empty)
498+ ctxProvider := sdk .ChannelContext (orgChannelID , fabsdk .WithUser (org1User ), fabsdk .WithOrg (org2Name ))
499+
500+ // org2 peers are the only targets to test pre reconciliation as they should not have the pvt data as per the collection policy (singleOrgPolicy)
501+ org2TargetOpts := channel .WithTargetEndpoints ("peer0.org2.example.com" , "peer1.org2.example.com" )
502+ chClient , err := channel .New (ctxProvider )
503+ require .NoError (t , err )
504+
505+ key1 := "key1"
506+ key3 := "key3"
507+
508+ response , err := chClient .Query (
509+ channel.Request {
510+ ChaincodeID : ccID ,
511+ Fcn : "getprivate" ,
512+ Args : [][]byte {[]byte (coll1 ), []byte (key1 )},
513+ },
514+ channel .WithRetry (retry .TestRetryOpts ),
515+ org2TargetOpts , // query org2 peers to ensure they don't have pvt data
516+ )
517+ require .Error (t , err )
518+ t .Logf ("Got response payload for getprivate: %s" , string (response .Payload ))
519+ require .Empty (t , response .Payload )
520+
521+ response , err = chClient .Query (
522+ channel.Request {
523+ ChaincodeID : ccID ,
524+ Fcn : "getprivatebyrange" ,
525+ Args : [][]byte {[]byte (coll1 ), []byte (key1 ), []byte (key3 )},
526+ },
527+ channel .WithRetry (retry .DefaultChannelOpts ),
528+ org2TargetOpts , // query org2 peers to ensure they don't have pvt data
529+ )
530+
531+ // for some reason, the peer throws an error for getprivate cc invoke(Failed to handle GET_STATE. error: private data matching public hash version is not available.),
532+ // but not for getprivatebyrange cc invoke (it only returns empty payload)
533+ require .NoError (t , err )
534+ t .Logf ("Got response payload for getprivatebyrange: [%s]" , string (response .Payload ))
535+ require .Empty (t , string (response .Payload ))
536+
537+ }
538+
539+ func runPvtDataPostReconcileGet (t * testing.T , sdk * fabsdk.FabricSDK , orgsContext []* integration.OrgContext , policy , ccID , coll1 string ) {
540+ collConfig , err := newCollectionConfig (coll1 , policy , 0 , 2 , 1000 )
541+ require .NoError (t , err )
542+
543+ // org2 peers are the only targets to test post reconciliation as they should have the pvt data after cc upgrade as per the new collection policy (multiOrgsPolicy)
544+ org2TargetOpts := channel .WithTargetEndpoints ("peer0.org2.example.com" , "peer1.org2.example.com" )
545+
546+ err = integration .UpgradeExamplePvtChaincode (orgsContext , orgChannelID , ccID , policy , collConfig )
547+ require .NoError (t , err )
548+
549+ // wait for pvt data reconciliation occurs on peers of org2
550+ time .Sleep (2 * time .Second )
551+
552+ // create ctxProvider for org2 to query org2 peers for pvt data (should be not empty/reconciled)
553+ ctxProvider := sdk .ChannelContext (orgChannelID , fabsdk .WithUser (org1User ), fabsdk .WithOrg (org2Name ))
554+
555+ chClient , err := channel .New (ctxProvider )
556+ require .NoError (t , err )
557+
558+ key1 := "key1"
559+ key3 := "key3"
560+ value1 := "pvtValue1"
561+
562+ response , err := chClient .Query (
563+ channel.Request {
564+ ChaincodeID : ccID ,
565+ Fcn : "getprivate" ,
566+ Args : [][]byte {[]byte (coll1 ), []byte (key1 )},
567+ },
568+ channel .WithRetry (retry .TestRetryOpts ),
569+ org2TargetOpts ,
570+ )
571+ require .NoError (t , err )
572+ t .Logf ("Got response payload for getprivate: %s" , string (response .Payload ))
573+ require .Equal (t , value1 , string (response .Payload ))
574+
575+ response , err = chClient .Query (
576+ channel.Request {
577+ ChaincodeID : ccID ,
578+ Fcn : "getprivatebyrange" ,
579+ Args : [][]byte {[]byte (coll1 ), []byte (key1 ), []byte (key3 )},
580+ },
581+ channel .WithRetry (retry .DefaultChannelOpts ),
582+ org2TargetOpts ,
583+ )
584+ require .NoError (t , err )
585+ t .Logf ("Got response payload for getprivatebyrange: [%s]" , string (response .Payload ))
586+ require .NotEmpty (t , string (response .Payload ))
587+ }
0 commit comments