Skip to content

Commit ffa075e

Browse files
fraVlacajt-nti
authored andcommitted
added purgePrivateData function with tests
Signed-off-by: fraVlaca <[email protected]>
1 parent 501af57 commit ffa075e

File tree

12 files changed

+152
-6
lines changed

12 files changed

+152
-6
lines changed

apis/fabric-shim-api/types/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ declare module 'fabric-shim-api' {
8787
getPrivateDataHash(collection: string, key: string): Promise<Uint8Array>;
8888
putPrivateData(collection: string, key: string, value: Uint8Array): Promise<void>;
8989
deletePrivateData(collection: string, key: string): Promise<void>;
90+
purgePrivateData(collection: string, key: string): Promise<void>;
9091
setPrivateDataValidationParameter(collection: string, key: string, ep: Uint8Array): Promise<void>;
9192
getPrivateDataValidationParameter(collection: string, key: string): Promise<Uint8Array>;
9293
getPrivateDataByRange(collection: string, startKey: string, endKey: string): Promise<Iterators.StateQueryIterator> & AsyncIterable<Iterators.KV>;

ci/azure-pipelines.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ variables:
4343
- name: rewire_node_version_spec # see https://github.com/jhnns/rewire/issues/178
4444
value: '16.4.0'
4545
- name: FABRIC_VERSION
46-
value: 2.4
46+
value: 2.5
4747

4848
# Build on Ubuntu
4949
pool:
@@ -147,6 +147,7 @@ stages:
147147
set -ev
148148
./tools/getEdgeDocker.sh # essential to get main docker images of peer etc.
149149
docker image load --input build/fabric-nodeenv.tar.gz # gets the build image of nodeenv
150+
docker tag hyperledger/fabric-nodeenv:latest hyperledger/fabric-nodeenv:$(FABRIC_VERSION)
150151
docker images
151152
node common/scripts/install-run-rush.js install
152153
node common/scripts/install-run-rush.js update # should the tests need 'building' this will need to go here

common/config/rush/command-line.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
"name": "start-fabric",
6161
"summary": "Starts local Fabric test network ",
6262
"description": "Run this command to start local Fabric network for testing",
63-
"shellCommand": "rm -rf ./fabric-samples && curl -sSL https://bit.ly/2ysbOFE | bash -s -- 2.4.0-beta 1.5.1 && cd ./fabric-samples/test-network && ./network.sh down && ./network.sh up createChannel -ca -s couchdb && cd -"
63+
"shellCommand": "rm -rf ./fabric-samples && curl -sSL https://bit.ly/2ysbOFE | bash -s -- 2.4.5 1.5.5 -d && cd ./fabric-samples/test-network && ./network.sh down && ./network.sh up createChannel -ca -s couchdb && cd -"
6464
},
6565
{
6666
"commandKind": "global",

libraries/fabric-shim/lib/handler.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,19 @@ class ChaincodeMessageHandler {
415415
return await this._askPeerAndListen(msg, 'DeleteState');
416416
}
417417

418+
async handlePurgeState(collection, key, channel_id, txId) {
419+
const msgPb = new peer.PurgePrivateState();
420+
msgPb.setKey(key);
421+
msgPb.setCollection(collection);
422+
const msg = mapToChaincodeMessage({
423+
type: peer.ChaincodeMessage.Type.PURGE_PRIVATE_DATA,
424+
payload: msgPb.serializeBinary(),
425+
txid: txId,
426+
channel_id: channel_id
427+
});
428+
return await this._askPeerAndListen(msg, 'PurgePrivateState');
429+
}
430+
418431
async handlePutStateMetadata(collection, key, metakey, ep, channel_id, txId) {
419432
const msgPb = new peer.PutStateMetadata();
420433
msgPb.setCollection(collection);

libraries/fabric-shim/lib/stub.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,30 @@ class ChaincodeStub {
10071007
return this.handler.handleDeleteState(collection, key, this.channel_id, this.txId);
10081008
}
10091009

1010+
/**
1011+
* PurgePrivateData records the specified `key` to be purged in the private writeset
1012+
* of the transaction. Note that only hash of the private writeset goes into the
1013+
* transaction proposal response (which is sent to the client who issued the
1014+
* transaction) and the actual private writeset gets temporarily stored in a
1015+
* transient store. The `key` and its value will be deleted from the collection
1016+
* when the transaction is validated and successfully committed, and will
1017+
* subsequently be completely removed from the private data store (that maintains
1018+
* the historical versions of private writesets) as a background operation.
1019+
* @param {string} collection The collection name
1020+
* @param {string} key Private data variable key to delete from the state store
1021+
*/
1022+
async purgePrivateData(collection, key) {
1023+
// Access public data by setting the collection to empty string
1024+
logger.debug('purgePrivateData called with collection:%s, key:%s', collection, key);
1025+
if (!collection || typeof collection !== 'string') {
1026+
throw new Error('collection must be a valid string');
1027+
}
1028+
if (!key || typeof key !== 'string') {
1029+
throw new Error('key must be a valid string');
1030+
}
1031+
return await this.handler.handlePurgeState(collection, key, this.channel_id, this.txId);
1032+
}
1033+
10101034
/**
10111035
* SetPrivateDataValidationParameter sets the key-level endorsement policy
10121036
* for the private data specified by `key`.

libraries/fabric-shim/test/typescript/chaincode.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ class TestTS implements ChaincodeInterface {
188188
await putPrivData;
189189
const delPrivateData: Promise<void> = stub.deletePrivateData(collection, key);
190190
await delPrivateData;
191+
const purgePrivData: Promise<void> = stub.purgePrivateData(collection, key);
192+
await purgePrivData;
191193

192194
}
193195

libraries/fabric-shim/test/unit/handler.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,6 +1053,56 @@ describe('Handler', () => {
10531053
});
10541054
});
10551055

1056+
describe('handlePurgeState', () => {
1057+
const key = 'theKey';
1058+
const collection = '';
1059+
1060+
let expectedMsg;
1061+
1062+
before(() => {
1063+
const payloadPb = new peer.PurgePrivateState();
1064+
payloadPb.setKey(key);
1065+
payloadPb.setCollection(collection);
1066+
expectedMsg = mapToChaincodeMessage({
1067+
type: peer.ChaincodeMessage.Type.PURGE_PRIVATE_DATA,
1068+
payload: payloadPb.serializeBinary(),
1069+
channel_id: 'theChannelID',
1070+
txid: 'theTxID'
1071+
});
1072+
});
1073+
1074+
afterEach(() => {
1075+
Handler = rewire('../../../fabric-shim/lib/handler.js');
1076+
sandbox.restore();
1077+
});
1078+
1079+
it ('should resolve when _askPeerAndListen resolves', async () => {
1080+
const mockStream = {write: sinon.stub(), end: sinon.stub()};
1081+
const handler = new Handler.ChaincodeMessageHandler(mockStream, mockChaincodeImpl);
1082+
const _askPeerAndListenStub = sandbox.stub(handler, '_askPeerAndListen').resolves('some response');
1083+
1084+
const result = await handler.handlePurgeState(collection, key, 'theChannelID', 'theTxID');
1085+
1086+
expect(result).to.deep.equal('some response');
1087+
expect(_askPeerAndListenStub.firstCall.args.length).to.deep.equal(2);
1088+
expect(_askPeerAndListenStub.firstCall.args[0]).to.deep.equal(expectedMsg);
1089+
expect(_askPeerAndListenStub.firstCall.args[1]).to.deep.equal('PurgePrivateState');
1090+
});
1091+
1092+
it ('should reject when _askPeerAndListen rejects', async () => {
1093+
const mockStream = {write: sinon.stub(), end: sinon.stub()};
1094+
const handler = new Handler.ChaincodeMessageHandler(mockStream, mockChaincodeImpl);
1095+
const _askPeerAndListenStub = sandbox.stub(handler, '_askPeerAndListen').rejects();
1096+
1097+
const result = handler.handlePurgeState(collection, key, 'theChannelID', 'theTxID');
1098+
1099+
await expect(result).to.eventually.be.rejected;
1100+
expect(_askPeerAndListenStub.firstCall.args.length).to.deep.equal(2);
1101+
expect(_askPeerAndListenStub.firstCall.args[0]).to.deep.equal(expectedMsg);
1102+
expect(_askPeerAndListenStub.firstCall.args[1]).to.deep.equal('PurgePrivateState');
1103+
});
1104+
});
1105+
10561106
describe('handlePutStateMetadata', () => {
10571107
const key = 'theKey';
10581108
const collection = '';

libraries/fabric-shim/test/unit/stub.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,6 +1210,41 @@ describe('Stub', () => {
12101210
});
12111211
});
12121212

1213+
describe('purgePrivateData', () => {
1214+
let handlePurgeStateStub;
1215+
let stub;
1216+
1217+
beforeEach(() => {
1218+
handlePurgeStateStub = sinon.stub().resolves('some state');
1219+
stub = new Stub({
1220+
handlePurgeState: handlePurgeStateStub
1221+
}, 'dummyChannelId', 'dummyTxid', chaincodeInput);
1222+
});
1223+
1224+
it ('should throw an error if no arguments supplied', async () => {
1225+
const result = stub.purgePrivateData();
1226+
await expect(result).to.eventually.be.rejectedWith(Error, 'collection must be a valid string');
1227+
});
1228+
1229+
it ('should throw an error if one argument supplied', async () => {
1230+
const result = stub.purgePrivateData('some arg');
1231+
await expect(result).to.eventually.be.rejectedWith(Error, 'key must be a valid string');
1232+
});
1233+
1234+
it ('should throw an error if collection null', async () => {
1235+
const result = stub.purgePrivateData(null, 'some key');
1236+
await expect(result).to.eventually.be.rejectedWith(Error, 'collection must be a valid string');
1237+
});
1238+
1239+
it ('should return handler.handlePurgeState', async () => {
1240+
const result = await stub.purgePrivateData('some collection', 'some key');
1241+
1242+
expect(result).to.deep.equal('some state');
1243+
expect(handlePurgeStateStub.calledOnce).to.be.true;
1244+
expect(handlePurgeStateStub.firstCall.args).to.deep.equal(['some collection', 'some key', 'dummyChannelId', 'dummyTxid']);
1245+
});
1246+
});
1247+
12131248
describe('setPrivateDataValidationParameter', () => {
12141249
it('should return handler.handlePutStateMetadata', async () => {
12151250
const handlePutStateMetadataStub = sinon.stub().resolves('nothing');

libraries/fabric-shim/types/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ declare module 'fabric-shim' {
126126
getPrivateDataHash(collection: string, key: string): Promise<Uint8Array>;
127127
putPrivateData(collection: string, key: string, value: Uint8Array): Promise<void>;
128128
deletePrivateData(collection: string, key: string): Promise<void>;
129+
purgePrivateData(collection: string, key: string): Promise<void>;
129130
setPrivateDataValidationParameter(collection: string, key: string, ep: Uint8Array): Promise<void>;
130131
getPrivateDataValidationParameter(collection: string, key: string): Promise<Uint8Array>;
131132
getPrivateDataByRange(collection: string, startKey: string, endKey: string): Promise<Iterators.StateQueryIterator> & AsyncIterable<Iterators.KV>;

test/chaincodes/privateData/chaincode.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ class privateDataContract extends Contract {
6666
await ctx.stub.deletePrivateData("collection", assetId);
6767
}
6868

69+
async purgeAsset(ctx, assetId) {
70+
const exists = await this.assetExists(ctx, assetId);
71+
if (!exists) {
72+
throw new Error(`The asset asset ${assetId} does not exist`);
73+
}
74+
await ctx.stub.purgePrivateData("collection", assetId);
75+
}
76+
6977
async verifyAsset(ctx, mspid, assetId, objectToVerify) {
7078

7179
const hashToVerify = crypto.createHash('sha256').update(objectToVerify).digest('hex');

0 commit comments

Comments
 (0)