From 795673dee393fe22ddc6bfef565751942defe3b2 Mon Sep 17 00:00:00 2001 From: Matthew B White Date: Mon, 23 Jan 2023 16:40:52 +0000 Subject: [PATCH] Add in examples and notes on using PurgePrivateData (#878) * Add in examples and notes on using PurgePrivateData - Update the configtx.yaml to enable the 2.5 capabillity - Added purge into the chancode - Added pruge into the application client code. Signed-off-by: Matthew B White * Temporarily remove java cc test Signed-off-by: Matthew B White --- .../application-gateway-typescript/src/app.ts | 15 +++++ .../chaincode-go/chaincode/asset_transfer.go | 62 +++++++++++++++++++ .../chaincode-go/go.mod | 2 +- .../chaincode-go/go.sum | 2 + .../chaincode-java/build.gradle | 2 +- .../samples/privatedata/AssetTransfer.java | 50 +++++++++++++++ ci/scripts/lint-java.sh | 3 +- interest_rate_swaps/network/configtx.yaml | 12 ++-- test-network-nano-bash/configtx.yaml | 12 ++-- test-network/configtx/configtx.yaml | 13 ++-- 10 files changed, 149 insertions(+), 24 deletions(-) diff --git a/asset-transfer-private-data/application-gateway-typescript/src/app.ts b/asset-transfer-private-data/application-gateway-typescript/src/app.ts index 0513807d..ac292330 100644 --- a/asset-transfer-private-data/application-gateway-typescript/src/app.ts +++ b/asset-transfer-private-data/application-gateway-typescript/src/app.ts @@ -129,6 +129,10 @@ async function main(): Promise { // Delete AssetID2 as Org1. await deleteAsset(contractOrg1, assetID2); + + // Trigger a purge of the private data for the asset + // The previous delete is optinal if purge is used + await purgeAsset(contractOrg1, assetID2); } finally { gatewayOrg1.close(); clientOrg1.close(); @@ -261,6 +265,17 @@ async function deleteAsset(contract: Contract, assetID: string): Promise { console.log('*** Transaction committed successfully'); } + +async function purgeAsset(contract: Contract, assetID: string): Promise { + console.log('\n--> Submit Transaction: PurgeAsset, ID:', assetID); + const dataForPurge = { assetID }; + await contract.submit('PurgeAsset', { + transientData: { asset_purge: JSON.stringify(dataForPurge) }, + }); + + console.log('*** Transaction committed successfully'); +} + async function readAssetPrivateDetails(contract: Contract, assetID: string, collectionName: string): Promise { console.log(`\n--> Evaluate Transaction: ReadAssetPrivateDetails from ${collectionName}, ID: ${assetID}`); diff --git a/asset-transfer-private-data/chaincode-go/chaincode/asset_transfer.go b/asset-transfer-private-data/chaincode-go/chaincode/asset_transfer.go index 92c10424..a377e57c 100644 --- a/asset-transfer-private-data/chaincode-go/chaincode/asset_transfer.go +++ b/asset-transfer-private-data/chaincode-go/chaincode/asset_transfer.go @@ -475,6 +475,68 @@ func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface) } +// PurgeAsset can be used by the owner of the asset to delete the asset +// Trigger removal of the asset +func (s *SmartContract) PurgeAsset(ctx contractapi.TransactionContextInterface) error { + + transientMap, err := ctx.GetStub().GetTransient() + if err != nil { + return fmt.Errorf("Error getting transient: %v", err) + } + + // Asset properties are private, therefore they get passed in transient field + transientDeleteJSON, ok := transientMap["asset_purge"] + if !ok { + return fmt.Errorf("asset to purge not found in the transient map") + } + + type assetPurge struct { + ID string `json:"assetID"` + } + + var assetPurgeInput assetPurge + err = json.Unmarshal(transientDeleteJSON, &assetPurgeInput) + if err != nil { + return fmt.Errorf("failed to unmarshal JSON: %v", err) + } + + if len(assetPurgeInput.ID) == 0 { + return fmt.Errorf("assetID field must be a non-empty string") + } + + // Verify that the client is submitting request to peer in their organization + err = verifyClientOrgMatchesPeerOrg(ctx) + if err != nil { + return fmt.Errorf("PurgeAsset cannot be performed: Error %v", err) + } + + log.Printf("Purging Asset: %v", assetPurgeInput.ID) + + // Note that there is no check here to see if the id exist; it might have been 'deleted' already + // so a check here is pointless. We would need to call purge irrespective of the result + // A delete can be called before purge, but is not essential + + ownerCollection, err := getCollectionName(ctx) // Get owners collection + if err != nil { + return fmt.Errorf("failed to infer private collection name for the org: %v", err) + } + + // delete the asset from state + err = ctx.GetStub().PurgePrivateData(assetCollection, assetPurgeInput.ID) + if err != nil { + return fmt.Errorf("failed to purge state from asset collection: %v", err) + } + + // Finally, delete private details of asset + err = ctx.GetStub().PurgePrivateData(ownerCollection, assetPurgeInput.ID) + if err != nil { + return fmt.Errorf("failed to purge state from owner collection: %v", err) + } + + return nil + +} + // DeleteTranferAgreement can be used by the buyer to withdraw a proposal from // the asset collection and from his own collection. func (s *SmartContract) DeleteTranferAgreement(ctx contractapi.TransactionContextInterface) error { diff --git a/asset-transfer-private-data/chaincode-go/go.mod b/asset-transfer-private-data/chaincode-go/go.mod index ba25be99..305208de 100644 --- a/asset-transfer-private-data/chaincode-go/go.mod +++ b/asset-transfer-private-data/chaincode-go/go.mod @@ -3,7 +3,7 @@ module github.com/hyperledger/fabric-samples/asset-transfer-private-data/chainco go 1.17 require ( - github.com/hyperledger/fabric-chaincode-go v0.0.0-20220720122508-9207360bbddd + github.com/hyperledger/fabric-chaincode-go v0.0.0-20220920210243-7bc6fa0dd58b github.com/hyperledger/fabric-contract-api-go v1.2.0 github.com/hyperledger/fabric-protos-go v0.0.0-20220613214546-bf864f01d75e github.com/stretchr/testify v1.8.0 diff --git a/asset-transfer-private-data/chaincode-go/go.sum b/asset-transfer-private-data/chaincode-go/go.sum index 6d35e832..f352bca5 100644 --- a/asset-transfer-private-data/chaincode-go/go.sum +++ b/asset-transfer-private-data/chaincode-go/go.sum @@ -165,6 +165,8 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hyperledger/fabric-chaincode-go v0.0.0-20220720122508-9207360bbddd h1:AIa0b7UPrt8e1YN4/68vhNnPxy/Mrgq9d2bYJ6O/KTE= github.com/hyperledger/fabric-chaincode-go v0.0.0-20220720122508-9207360bbddd/go.mod h1:OxME3M0bbgoWYHpXIVMzpbXgFqrTZnFmlH0Cpml54m0= +github.com/hyperledger/fabric-chaincode-go v0.0.0-20220920210243-7bc6fa0dd58b h1:MGT5rdajc4zbsbU7yMzkLJmsiRwJk5gBX5OdpU117Bg= +github.com/hyperledger/fabric-chaincode-go v0.0.0-20220920210243-7bc6fa0dd58b/go.mod h1:OxME3M0bbgoWYHpXIVMzpbXgFqrTZnFmlH0Cpml54m0= github.com/hyperledger/fabric-contract-api-go v1.2.0 h1:BmArPRmTjiC2brHk2FNlDoJ8bOI0ExKZhj2YqWAiv5o= github.com/hyperledger/fabric-contract-api-go v1.2.0/go.mod h1:GU2NV95E5LNkFTCL3xcPgXzi8QNLXBZhx7DGnKskuqw= github.com/hyperledger/fabric-protos-go v0.0.0-20220613214546-bf864f01d75e h1:Ae2p0e+v5ekrl4KgkbCStBTSoV67Cg9fPkEWrv0f3nk= diff --git a/asset-transfer-private-data/chaincode-java/build.gradle b/asset-transfer-private-data/chaincode-java/build.gradle index bc59a38f..e5acd742 100644 --- a/asset-transfer-private-data/chaincode-java/build.gradle +++ b/asset-transfer-private-data/chaincode-java/build.gradle @@ -14,7 +14,7 @@ version '1.0-SNAPSHOT' dependencies { - implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.+' + implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.+' implementation 'org.json:json:+' testImplementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.+' diff --git a/asset-transfer-private-data/chaincode-java/src/main/java/org/hyperledger/fabric/samples/privatedata/AssetTransfer.java b/asset-transfer-private-data/chaincode-java/src/main/java/org/hyperledger/fabric/samples/privatedata/AssetTransfer.java index 94a261c4..6467f2c8 100644 --- a/asset-transfer-private-data/chaincode-java/src/main/java/org/hyperledger/fabric/samples/privatedata/AssetTransfer.java +++ b/asset-transfer-private-data/chaincode-java/src/main/java/org/hyperledger/fabric/samples/privatedata/AssetTransfer.java @@ -488,6 +488,9 @@ public final class AssetTransfer implements ContractInterface { * Deletes a asset & related details from the ledger. * Input in transient map: asset_delete * + * This deletes the private data, but does not trigger an immediate cleanup + * of the history. To specifically force removal right now use purge + * * @param ctx the transaction context */ @Transaction(intent = Transaction.TYPE.SUBMIT) @@ -539,6 +542,53 @@ public final class AssetTransfer implements ContractInterface { stub.delPrivateData(ownersCollectionName, assetID); } + /** + * Purges the history of the asset from Private Data + * (delete does not need to be called as well) + * Input in transient map: asset_delete + * + * @param ctx the transaction context + */ + @Transaction(intent = Transaction.TYPE.SUBMIT) + public void PurgeAsset(final Context ctx) { + ChaincodeStub stub = ctx.getStub(); + Map transientMap = ctx.getStub().getTransient(); + if (!transientMap.containsKey("asset_purge")) { + String errorMessage = String.format("PurgeAsset call must specify 'asset_purge' in Transient map input"); + System.err.println(errorMessage); + throw new ChaincodeException(errorMessage, AssetTransferErrors.INCOMPLETE_INPUT.toString()); + } + + byte[] transientAssetJSON = transientMap.get("asset_purge"); + final String assetID; + + try { + JSONObject json = new JSONObject(new String(transientAssetJSON, UTF_8)); + assetID = json.getString("assetID"); + + } catch (Exception err) { + String errorMessage = String.format("TransientMap deserialized error: %s ", err); + System.err.println(errorMessage); + throw new ChaincodeException(errorMessage, AssetTransferErrors.INCOMPLETE_INPUT.toString()); + } + + // Note that there is no check here to see if the id exist; it might have been 'deleted' already + // so a check here is pointless. We would need to call purge irrespective of the result + // A delete can be called before purge, but is not essential + + String ownersCollectionName = getCollectionName(ctx); + verifyClientOrgMatchesPeerOrg(ctx); + + // delete the key from asset collection + System.out.printf("PurgeAsset: collection %s, ID %s\n", ASSET_COLLECTION_NAME, assetID); + stub.purgePrivateData(ASSET_COLLECTION_NAME, assetID); + + // Finally, delete private details of asset + System.out.printf("PurgeAsset: collection %s, ID %s\n", ownersCollectionName, assetID); + stub.purgePrivateData(ownersCollectionName, assetID); + } + + // Used by TransferAsset to verify that the transfer is being initiated by the owner and that // the buyer has agreed to the same appraisal value as the owner private void verifyAgreement(final Context ctx, final String assetID, final String owner, final String buyerMSP) { diff --git a/ci/scripts/lint-java.sh b/ci/scripts/lint-java.sh index 327a91e7..82f3a810 100755 --- a/ci/scripts/lint-java.sh +++ b/ci/scripts/lint-java.sh @@ -8,7 +8,8 @@ function print() { echo -e "${GREEN}${1}${NC}" } -dirs=("$(find . -name "*-java" -type d -not -path '*/.*')") +# remove the java asset-private-data until the publishing issues have been resolved +dirs=("$(find . -name "*-java" -type d -not -path '*/.*' -not -path '*/asset-transfer-private-data/*')") for dir in $dirs; do print "Linting $dir" pushd $dir diff --git a/interest_rate_swaps/network/configtx.yaml b/interest_rate_swaps/network/configtx.yaml index 2d1c6cae..43948e72 100644 --- a/interest_rate_swaps/network/configtx.yaml +++ b/interest_rate_swaps/network/configtx.yaml @@ -269,13 +269,11 @@ Capabilities: # used with prior release orderers. # Set the value of the capability to true to require it. Application: &ApplicationCapabilities - # V2_0 application capability ensures that peers behave according - # to v2.0 application capabilities. Peers from - # prior releases would behave in an incompatible way, and are therefore - # not able to participate in channels at v2.0 application capability. - # Prior to enabling V2.0 application capabilities, ensure that all - # peers on channel are at v2.0.0 or later. - V2_0: true + # V2.5 for Application enables the new non-backwards compatible + # features of fabric v2.5, namely the ability to purge private data. + # Prior to enabling V2.5 application capabilities, ensure that all + # peers on a channel are at v2.5.0 or later. + V2_5: true ################################################################################ # diff --git a/test-network-nano-bash/configtx.yaml b/test-network-nano-bash/configtx.yaml index e1634014..0a0c05d6 100644 --- a/test-network-nano-bash/configtx.yaml +++ b/test-network-nano-bash/configtx.yaml @@ -163,13 +163,11 @@ Capabilities: # used with prior release orderers. # Set the value of the capability to true to require it. Application: &ApplicationCapabilities - # V2_0 application capability ensures that peers behave according - # to v2.0 application capabilities. Peers from - # prior releases would behave in an incompatible way, and are therefore - # not able to participate in channels at v2.0 application capability. - # Prior to enabling V2.0 application capabilities, ensure that all - # peers on channel are at v2.0.0 or later. - V2_0: true + # V2.5 for Application enables the new non-backwards compatible + # features of fabric v2.5, namely the ability to purge private data. + # Prior to enabling V2.5 application capabilities, ensure that all + # peers on a channel are at v2.5.0 or later. + V2_5: true ################################################################################ # diff --git a/test-network/configtx/configtx.yaml b/test-network/configtx/configtx.yaml index 48bd1d75..01fb15fb 100644 --- a/test-network/configtx/configtx.yaml +++ b/test-network/configtx/configtx.yaml @@ -129,6 +129,7 @@ Capabilities: # Prior to enabling V2.0 channel capabilities, ensure that all # orderers and peers on a channel are at v2.0.0 or later. V2_0: true + # Orderer capabilities apply only to the orderers, and may be safely # used with prior release peers. @@ -146,13 +147,11 @@ Capabilities: # used with prior release orderers. # Set the value of the capability to true to require it. Application: &ApplicationCapabilities - # V2_0 application capability ensures that peers behave according - # to v2.0 application capabilities. Peers from - # prior releases would behave in an incompatible way, and are therefore - # not able to participate in channels at v2.0 application capability. - # Prior to enabling V2.0 application capabilities, ensure that all - # peers on channel are at v2.0.0 or later. - V2_0: true + # V2.5 for Application enables the new non-backwards compatible + # features of fabric v2.5, namely the ability to purge private data. + # Prior to enabling V2.5 application capabilities, ensure that all + # peers on a channel are at v2.5.0 or later. + V2_5: true ################################################################################ #