Skip to content

Commit

Permalink
[FAB-9679] Private Data Support
Browse files Browse the repository at this point in the history
Send Private Data collection definitions during chaincode instantiation.

Change-Id: I3334e572202b3a66e59ec5248ec981b64d408801
Signed-off-by: zhaochy <[email protected]>
  • Loading branch information
zhaochy1990 committed May 16, 2018
1 parent e55f817 commit 61b5493
Show file tree
Hide file tree
Showing 14 changed files with 489 additions and 58 deletions.
4 changes: 1 addition & 3 deletions build/tasks/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,7 @@ gulp.task('compile', shell.task([
ignoreErrors: false // once compile failed, throw error
}));

// ----- remomve 'compile until typescript issues are cleared
//gulp.task('test', ['clean-up', 'lint', 'pre-test', 'compile', 'docker-ready', 'ca'], function() {
gulp.task('test', ['clean-up', 'lint', 'pre-test', 'docker-ready', 'ca'], function() {
gulp.task('test', ['clean-up', 'lint', 'pre-test', 'compile', 'docker-ready', 'ca'], function() {
// use individual tests to control the sequence they get executed
// first run the ca-tests that tests all the member registration
// and enrollment scenarios (good and bad calls). Then the rest
Expand Down
90 changes: 57 additions & 33 deletions fabric-client/lib/Channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,38 @@

'use strict';

var utils = require('./utils.js');
var clientUtils = require('./client-utils.js');
var util = require('util');
var path = require('path');
var Peer = require('./Peer.js');
var ChannelEventHub = require('./ChannelEventHub.js');
var Orderer = require('./Orderer.js');
var BlockDecoder = require('./BlockDecoder.js');
var TransactionID = require('./TransactionID.js');
var grpc = require('grpc');
var logger = utils.getLogger('Channel.js');
var MSPManager = require('./msp/msp-manager.js');
var Policy = require('./Policy.js');
var Constants = require('./Constants.js');

var _ccProto = grpc.load(__dirname + '/protos/peer/chaincode.proto').protos;
var _transProto = grpc.load(__dirname + '/protos/peer/transaction.proto').protos;
var _proposalProto = grpc.load(__dirname + '/protos/peer/proposal.proto').protos;
var _responseProto = grpc.load(__dirname + '/protos/peer/proposal_response.proto').protos;
var _queryProto = grpc.load(__dirname + '/protos/peer/query.proto').protos;
var _peerConfigurationProto = grpc.load(__dirname + '/protos/peer/configuration.proto').protos;
var _commonProto = grpc.load(__dirname + '/protos/common/common.proto').common;
var _configtxProto = grpc.load(__dirname + '/protos/common/configtx.proto').common;
var _policiesProto = grpc.load(__dirname + '/protos/common/policies.proto').common;
var _ledgerProto = grpc.load(__dirname + '/protos/common/ledger.proto').common;
var _commonConfigurationProto = grpc.load(__dirname + '/protos/common/configuration.proto').common;
var _ordererConfigurationProto = grpc.load(__dirname + '/protos/orderer/configuration.proto').orderer;
var _abProto = grpc.load(__dirname + '/protos/orderer/ab.proto').orderer;
var _mspConfigProto = grpc.load(__dirname + '/protos/msp/msp_config.proto').msp;
var _mspPrincipalProto = grpc.load(__dirname + '/protos/msp/msp_principal.proto').common;
var _identityProto = grpc.load(path.join(__dirname, '/protos/msp/identities.proto')).msp;
const utils = require('./utils.js');
const clientUtils = require('./client-utils.js');
const util = require('util');
const path = require('path');
const Peer = require('./Peer.js');
const ChannelEventHub = require('./ChannelEventHub.js');
const Orderer = require('./Orderer.js');
const BlockDecoder = require('./BlockDecoder.js');
const TransactionID = require('./TransactionID.js');
const grpc = require('grpc');
const logger = utils.getLogger('Channel.js');
const MSPManager = require('./msp/msp-manager.js');
const Policy = require('./Policy.js');
const Constants = require('./Constants.js');
const CollectionConfig = require('./SideDB').CollectionConfig;

const _ccProto = grpc.load(__dirname + '/protos/peer/chaincode.proto').protos;
const _transProto = grpc.load(__dirname + '/protos/peer/transaction.proto').protos;
const _proposalProto = grpc.load(__dirname + '/protos/peer/proposal.proto').protos;
const _responseProto = grpc.load(__dirname + '/protos/peer/proposal_response.proto').protos;
const _queryProto = grpc.load(__dirname + '/protos/peer/query.proto').protos;
const _peerConfigurationProto = grpc.load(__dirname + '/protos/peer/configuration.proto').protos;
const _commonProto = grpc.load(__dirname + '/protos/common/common.proto').common;
const _configtxProto = grpc.load(__dirname + '/protos/common/configtx.proto').common;
const _policiesProto = grpc.load(__dirname + '/protos/common/policies.proto').common;
const _ledgerProto = grpc.load(__dirname + '/protos/common/ledger.proto').common;
const _commonConfigurationProto = grpc.load(__dirname + '/protos/common/configuration.proto').common;
const _ordererConfigurationProto = grpc.load(__dirname + '/protos/orderer/configuration.proto').orderer;
const _abProto = grpc.load(__dirname + '/protos/orderer/ab.proto').orderer;
const _mspConfigProto = grpc.load(__dirname + '/protos/msp/msp_config.proto').msp;
const _mspPrincipalProto = grpc.load(__dirname + '/protos/msp/msp_principal.proto').common;
const _identityProto = grpc.load(path.join(__dirname, '/protos/msp/identities.proto')).msp;

const ImplicitMetaPolicy_Rule = { 0: 'ANY', 1: 'ALL', 2: 'MAJORITY' };

Expand All @@ -62,7 +63,7 @@ const ORDERER_NOT_ASSIGNED_MSG = 'Orderer with name "%s" not assigned to this ch
*
* @class
*/
var Channel = class {
const Channel = class {

/**
* Returns a new instance of the class. This is a client-side-only call. To
Expand Down Expand Up @@ -1431,14 +1432,33 @@ var Channel = class {
chaincodeDeploymentSpec.setChaincodeSpec(ccSpec);

const signer = this._clientContext._getSigningIdentity(request.txId.isAdmin());
/**
* lcccSpec_args:
* args[0] is the command
* args[1] is the channel name
* args[2] is the ChaincodeDeploymentSpec
*
* the following optional arguments here (they can each be nil and may or may not be present)
* args[3] is a marshalled SignaturePolicyEnvelope representing the endorsement policy
* args[4] is the name of escc
* args[5] is the name of vscc
* args[6] is a marshalled CollectionConfigPackage struct
*/
const lcccSpec_args = [
Buffer.from(command),
Buffer.from(this._name),
chaincodeDeploymentSpec.toBuffer()
chaincodeDeploymentSpec.toBuffer(),
Buffer.from(''),
Buffer.from(''),
Buffer.from(''),
];
if (request['endorsement-policy']) {
lcccSpec_args[3] = this._buildEndorsementPolicy(request['endorsement-policy']);
}
if (request['collections-config']) {
const collectionConfigPackage = this._buildCollectionsConfigPackage(request['collections-config']);
lcccSpec_args[6] = collectionConfigPackage.toBuffer();
}

const lcccSpec = {
// type: _ccProto.ChaincodeSpec.Type.GOLANG,
Expand Down Expand Up @@ -2083,6 +2103,10 @@ var Channel = class {
return Policy.buildPolicy(this.getMSPManager().getMSPs(), policy);
}

_buildCollectionsConfigPackage(collectionsConfig) {
return CollectionConfig.buildCollectionConfigPackage(collectionsConfig);
}

/**
* return a printable representation of this channel object
*/
Expand Down
62 changes: 47 additions & 15 deletions fabric-client/lib/Policy.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,7 @@ var EndorsementPolicy = class {
return envelope.toBuffer();
} else {
// check the structure of the policy object is legit
if (typeof policy.identities === 'undefined' || policy.identities === null || policy.identities === '' || policy.identities === {}) {
throw new Error('Invalid policy, missing the "identities" property');
} else if (!Array.isArray(policy.identities)) {
throw new Error('Invalid policy, the "identities" property must be an array');
}

if (typeof policy.policy === 'undefined' || policy.policy === null || policy.policy === '' || policy.policy === {}) {
throw new Error('Invalid policy, missing the "policy" property');
}
checkPolicy(policy);

policy.identities.forEach((identity) => {
let newPrincipal = buildPrincipal(identity);
Expand All @@ -138,7 +130,7 @@ function buildPrincipal(identity) {
const principalType = getIdentityType(identity);
const newPrincipal = new _mspPrProto.MSPPrincipal();

if (principalType===IDENTITY_TYPE.Role) {
if (principalType === IDENTITY_TYPE.Role) {
newPrincipal.setPrincipalClassification(_mspPrProto.MSPPrincipal.Classification.ROLE);
const newRole = new _mspPrProto.MSPRole();
const roleName = identity[principalType].name;
Expand All @@ -153,14 +145,13 @@ function buildPrincipal(identity) {
}

let mspid = identity[principalType].mspId;
if (typeof mspid !== 'string' || !mspid ) {
if (typeof mspid !== 'string' || !mspid) {
throw new Error(util.format('Invalid mspid found: "%j"', mspid));
}
newRole.setMspIdentifier(identity[principalType].mspId);

newPrincipal.setPrincipal(newRole.toBuffer());
}
else {
} else {
throw new Error('NOT IMPLEMENTED');
}

Expand All @@ -184,8 +175,7 @@ function getIdentityType(obj) {
IDENTITY_TYPE.Role,
IDENTITY_TYPE.OrganizationUnit,
IDENTITY_TYPE.Identity,
invalidTypes)
);
invalidTypes));
}

function getPolicyType(spec) {
Expand Down Expand Up @@ -232,5 +222,47 @@ function parsePolicy(spec) {
}
}

function buildSignaturePolicy(spec) {
const type = getPolicyType(spec);
if (type === 'signed-by') {
return {
signed_by: spec[type]
};
} else {
let n = type.match(/^(\d+)-of$/)[1];
n = parseInt(n);
let ruleArray = spec[type];
let rules = [];
ruleArray.forEach(rule => {
rules.push(buildSignaturePolicy(rule));
});
const nOutOf = {
n_out_of: {
n,
rules
}
};
return nOutOf;
}
}

function checkPolicy(policy){
if (!policy) {
throw new Error('Missing Required Param "policy"');
}
if (typeof policy.identities === 'undefined' || policy.identities === null || policy.identities === '' || policy.identities === {}) {
throw new Error('Invalid policy, missing the "identities" property');
} else if (!Array.isArray(policy.identities)) {
throw new Error('Invalid policy, the "identities" property must be an array');
}

if (typeof policy.policy === 'undefined' || policy.policy === null || policy.policy === '' || policy.policy === {}) {
throw new Error('Invalid policy, missing the "policy" property');
}
}

module.exports = EndorsementPolicy;
module.exports.IDENTITY_TYPE = IDENTITY_TYPE;
module.exports.buildPrincipal = buildPrincipal;
module.exports.buildSignaturePolicy = buildSignaturePolicy;
module.exports.checkPolicy = checkPolicy;
115 changes: 115 additions & 0 deletions fabric-client/lib/SideDB.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
Copyright 2018 Zhao Chaoyi, All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

const grpc = require('grpc');
const fs = require('fs');
const Policy = require('./Policy.js');
const _collectionProto = grpc.load(__dirname + '/protos/common/collection.proto').common;

const utils = require('./utils.js');
const logger = utils.getLogger('SideDB.js');
const { format } = require('util');

class CollectionConfig {
static buildCollectionConfigPackage(collectionsConfig) {
/**
* collectionsConfig can be either:
* - A string represents the collections-config.json file path
* - An array of collectionConfig
*/
try {
let content = collectionsConfig;
if (typeof collectionsConfig === 'string') {
logger.debug('Read CollectionsConfig From %s', collectionsConfig);
content = fs.readFileSync(collectionsConfig, 'utf8');
content = JSON.parse(content);
}
if (!Array.isArray(content)) {
logger.error('Expect collections config of type Array, found %s', typeof content);
throw new Error('Expect collections config of type Array');
}
let collectionConfigPackage = [];
content.forEach(config => {
const collectionConfig = buildCollectionConfig(config);
collectionConfigPackage.push(collectionConfig);
});
collectionConfigPackage = new _collectionProto.CollectionConfigPackage(collectionConfigPackage);

return collectionConfigPackage;
} catch (e) {
logger.error(e);
throw e;
}
}
}
function checkCollectionConfig(collectionConfig) {
const {
name,
policy,
maxPeerCount,
requiredPeerCount
} = collectionConfig;
if (!name || typeof name !== 'string') {
throw new Error(format('CollectionConfig Requires Param "name" of type string, found %j(type: %s)', name, typeof name));
}
if (!policy) {
throw new Error('Missing Requires Param "policy"');
}
Policy.checkPolicy(policy);
if (!Number.isInteger(maxPeerCount)) {
throw new Error(format('CollectionConfig Requires Param "maxPeerCount" of type number, found %j(type: %s)', maxPeerCount, typeof maxPeerCount));
}
if (!Number.isInteger(requiredPeerCount)) {
throw new Error(format('CollectionConfig Requires Param "requiredPeerCount" of type number, found %j(type: %s)', requiredPeerCount, typeof requiredPeerCount));
}
}

function buildCollectionConfig(collectionConfig) {
try {
checkCollectionConfig(collectionConfig);

const {
name,
policy,
maxPeerCount,
requiredPeerCount
} = collectionConfig;

let static_collection_config = {
name,
member_orgs_policy: {},
required_peer_count: requiredPeerCount,
maximum_peer_count: maxPeerCount
};

let principals = [];
policy.identities.forEach((identity) => {
let newPrincipal = Policy.buildPrincipal(identity);
principals.push(newPrincipal);
});

let signaturePolicy = Policy.buildSignaturePolicy(policy.policy);

let signaturePolicyEnvelope = {
version: 0,
rule: signaturePolicy,
identities: principals
};

static_collection_config.member_orgs_policy.signature_policy = signaturePolicyEnvelope;

return { static_collection_config };
} catch (e) {
logger.error(e);
throw e;
}
}

module.exports = {
CollectionConfig,
checkCollectionConfig
};
2 changes: 1 addition & 1 deletion fabric-client/lib/protos/common/collection.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ SPDX-License-Identifier: Apache-2.0

syntax = "proto3";

import "common/policies.proto";
import "./policies.proto";

option go_package = "github.com/hyperledger/fabric/protos/common";
option java_package = "org.hyperledger.fabric.protos.common";
Expand Down
25 changes: 23 additions & 2 deletions test/fixtures/channel/configtx.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ Orderer: &OrdererDefaults
- orderer.example.com:7050

# Batch Timeout: The amount of time to wait before creating a batch
BatchTimeout: 2s
BatchTimeout: 0.5s

# Batch Size: Controls the number of messages batched into a block
BatchSize:
Expand Down Expand Up @@ -186,4 +186,25 @@ Capabilities:
# determined to be desired for all peers running v1.0.x, but the
# modification of which would cause incompatibilities. Users should
# leave this flag set to true.
V1_1: true
# V1_1: true

# V1.2 for Application enables the new non-backwards compatible
# features and fixes of fabric v1.2, it implies V1_1.
V1_2: true
# V1_1_PVTDATA_EXPERIMENTAL is an Application capability to enable the
# private data capability. It is only supported when using peers built
# with experimental build tag. When set to true, private data
# collections can be configured upon chaincode instantiation and
# utilized within chaincode Invokes.
# NOTE: Use of this feature with non "experimental" binaries on the
# network may cause a fork.
V1_1_PVTDATA_EXPERIMENTAL: true
# V1_1_RESOURCETREE_EXPERIMENTAL is an Application capability to enable
# the resources capability. Currently this is needed for defining
# resource-based access control (RBAC). RBAC helps set fine-grained
# access control on system resources such as the endorser and various
# system chaincodes. Default is v1.0-based access control based on
# CHANNEL_READERS and CHANNEL_WRITERS.
# NOTE: Use of this feature with non "experimental" binaries on
# the network may cause a fork.
V1_1_RESOURCETREE_EXPERIMENTAL: true
Loading

0 comments on commit 61b5493

Please sign in to comment.