diff --git a/hfc/lib/Chain.js b/hfc/lib/Chain.js
index a3ea953ce1..ce300eff32 100644
--- a/hfc/lib/Chain.js
+++ b/hfc/lib/Chain.js
@@ -35,6 +35,9 @@ var _transProto = grpc.load(__dirname + '/protos/peer/fabric_transaction.proto')
var _proposalProto = grpc.load(__dirname + '/protos/peer/fabric_proposal.proto').protos;
var _responseProto = grpc.load(__dirname + '/protos/peer/fabric_proposal_response.proto').protos;
var _commonProto = grpc.load(__dirname + '/protos/common/common.proto').common;
+var _configurationProto = grpc.load(__dirname + '/protos/common/configuration.proto').common;
+var _ordererConfigurationProto = grpc.load(__dirname +
+ '/protos/orderer/configuration.proto').orderer;
/**
* The class representing a chain with which the client SDK interacts.
@@ -67,7 +70,8 @@ var Chain = class {
throw new Error('Failed to create Chain. Missing requirement "clientContext" parameter.');
}
- // naming for the chain will be enforced later by the orderer when "initialize()" gets called
+ // naming for the chain will be enforced later by the orderer when
+ // "initialize()" gets called
this._name = name;
// Security enabled flag
@@ -79,11 +83,13 @@ var Chain = class {
// Is in dev mode or network mode
this._devMode = false;
- // If in prefetch mode, we prefetch tcerts from member services to help performance
+ // If in prefetch mode, we prefetch tcerts from member
+ // services to help performance
this._preFetchMode = true;//to do - not in doc
/**
- * @member [CryptoSuite]{@link module:api.CryptoSuite} cryptoPrimitives The crypto primitives object provides access to the crypto suite
+ * @member [CryptoSuite]{@link module:api.CryptoSuite} cryptoPrimitives
+ * The crypto primitives object provides access to the crypto suite
* for functions like sign, encrypt, decrypt, etc.
* @memberof module:api.Chain.prototype
*/
@@ -94,9 +100,25 @@ var Chain = class {
this._clientContext = clientContext;
+ // the following settings will be used when this chain
+ // is initialized (created) The user should set these
+ // to the desired values before initializing this chain
+ this._initial_epoch = 0;
+ this._initial_max_message_count = 10;
+ this._consensus_type = 'solo';
+ // user must set this value before the initializeChain() method
+ // is called
+ this._initial_transaction_id = null;
+
//to do update logger
- logger.info('Constructed Chain instance: name - %s, securityEnabled: %s, TCert download batch size: %s, network mode: %s',
- this._name, this._securityEnabled, this._tcertBatchSize, !this._devMode);
+ logger.info('Constructed Chain instance: name - %s, ' +
+ 'securityEnabled: %s, ' +
+ 'TCert download batch size: %s, ' +
+ 'network mode: %s',
+ this._name,
+ this._securityEnabled,
+ this._tcertBatchSize,
+ !this._devMode);
}
/**
@@ -197,6 +219,7 @@ var Chain = class {
* @returns {Peer[]} The peer list on the chain.
*/
getPeers() {
+ logger.debug('getPeers - list size: %s.', this._peers.length);
return this._peers;
}
@@ -207,8 +230,19 @@ var Chain = class {
* the orderer nodes, it can choose to use more than one by adding them to the chain instance.
* All APIs concerning the orderer will broadcast to all orderers simultaneously.
* @param {Orderer} orderer An instance of the Orderer class.
+ * @throws {Error} if the orderer with that url already exists.
*/
addOrderer(orderer) {
+ var url = orderer.getUrl();
+ for (let i = 0; i < this._orderers.length; i++) {
+ if (this._orderers[i].getUrl() === url) {
+ var error = new Error();
+ error.name = 'DuplicateOrderer';
+ error.message = 'Orderer with URL ' + url + ' already exists';
+ logger.error(error.message);
+ throw error;
+ }
+ }
this._orderers.push(orderer);
}
@@ -235,6 +269,93 @@ var Chain = class {
return this._orderers;
}
+ /**
+ * Get the consensus type that will be used when this
+ * chain is created.
+ * @return {string} consensus type
+ */
+ getConsesusType() {
+ return this._consensus_type;
+ }
+
+ /**
+ * Set the consensus type that will be used when this
+ * chain is created.
+ * Default 'solo'
+ *
+ * @param {string} consensus type
+ */
+ setConsesusType(consensus_type) {
+ this._consensus_type = consensus_type;
+ }
+
+ /**
+ * Get the initial epoch that will be used when this
+ * chain is created.
+ * @return {int} initial epoch
+ */
+ getInitialEpoch() {
+ return this._initial_epoch;
+ }
+
+ /**
+ * Set the initial epoch that will be used when this
+ * chain is created. Value must be a positive integer.
+ * Default 0
+ *
+ * @param {int} initial epoch
+ */
+ setInitialEpoch(initial_epoch) {
+ if(!Number.isInteger(initial_epoch) || initial_epoch < 0) {
+ throw new Error('initial epoch must be a positive integer');
+ }
+ this._initial_epoch = initial_epoch;
+ }
+
+ /**
+ * Get the initial maximum message count that will be used when this
+ * chain is created.
+ * @return {int} initial maximum message count
+ */
+ getInitialMaxMessageCount() {
+ return this._initial_max_message_count;
+ }
+
+ /**
+ * Set the initial maximum message count that will be used when this
+ * chain is created.
+ * Default 10
+ *
+ * @param {int} initial maximum message count
+ */
+ setInitialMaxMessageCount(initial_max_message_count) {
+ if(!Number.isInteger(initial_max_message_count) || initial_max_message_count < 0) {
+ throw new Error('initial maximum message count must be a positive integer');
+ }
+ this._initial_max_message_count = initial_max_message_count;
+ }
+
+ /**
+ * Get the initial transaction ID that will be used when this
+ * chain is created.
+ * @return {string} transaction ID
+ */
+ getInitialTransactionId() {
+ return this._initial_transaction_id;
+ }
+
+ /**
+ * Set the initial transaction ID that will be used when this
+ * chain is created. This value must be set before the
+ * initializeChain() method is called.
+ * There is no default.
+ *
+ * @param {int} initial transaction ID
+ */
+ setInitialTransactionId(initial_transaction_id) {
+ this._initial_transaction_id = initial_transaction_id;
+ }
+
/**
* Calls the orderer(s) to start building the new chain, which is a combination
* of opening new message stream and connecting the list of participating peers.
@@ -244,7 +365,199 @@ var Chain = class {
* @returns {boolean} Whether the chain initialization process was successful.
*/
initializeChain() {
- //to do
+ logger.debug('initializeChain - start');
+
+ // verify that we have an orderer configured
+ if(!this.getOrderers()[0]) {
+ logger.error('initializeChain - no primary orderer defined');
+ return Promise.reject(new Error('no primary orderer defined'));
+ }
+
+ // verify that we have a user configured
+ if(!this._clientContext._userContext) {
+ logger.error('initializeChain - no user defined');
+ return Promise.reject(new Error('no user defined'));
+ }
+
+ // verify that we have a name configured
+ if(!this._name) {
+ logger.error('initializeChain - no chain id defined');
+ return Promise.reject(new Error('Chain name is not defined'));
+ }
+ let chain_id = this._name;
+
+ // verify that we have a transactionid configured
+ if(!this._initial_transaction_id) {
+ logger.error('initializeChain - no transaction id defined');
+ return Promise.reject(new Error('Initial transaction id is not defined'));
+ }
+
+ logger.debug('initializeChain - building request');
+ // build fields to use when building the configuration items
+ var configItemChainHeader =
+ buildChainHeader(
+ _commonProto.HeaderType.CONFIGURATION_ITEM,
+ 1,
+ chain_id,
+ this._initial_transaction_id,
+ this._initial_epoch
+ );
+ logger.debug('initializeChain - header built');
+
+ var orderer_type =
+ _configurationProto.ConfigurationItem.ConfigurationType.Orderer;
+ var policy_type =
+ _configurationProto.ConfigurationItem.ConfigurationType.Policy;
+ var last_modified = '0';
+ var mod_policy = 'DefaultModificationPolicy';
+
+ var creation_items = [];
+
+ // build configuration items
+ var consensusType = new _ordererConfigurationProto.ConsensusType();
+ consensusType.setType(this._consensus_type);
+ var consensusTypeItem =
+ buildSignedConfigurationItem(
+ configItemChainHeader,
+ orderer_type,
+ last_modified,
+ mod_policy,
+ 'ConsensusType',
+ consensusType.toBuffer()
+ );
+ creation_items.push(consensusTypeItem.getConfigurationItem().toBuffer());
+
+ logger.debug('initializeChain - bytes for consesus item ::'
+ + JSON.stringify(consensusTypeItem.getConfigurationItem()));
+
+ var batchSize = new _ordererConfigurationProto.BatchSize();
+ batchSize.setMaxMessageCount(this._initial_max_message_count);
+ var batchSizeItem =
+ buildSignedConfigurationItem(
+ configItemChainHeader,
+ orderer_type,
+ last_modified,
+ mod_policy,
+ 'BatchSize',
+ batchSize.toBuffer()
+ );
+ creation_items.push(batchSizeItem.getConfigurationItem().toBuffer());
+
+ // TODO how do we deal with KafkaBrokers ?
+
+ var chainCreators = new _ordererConfigurationProto.ChainCreators();
+ var chainCreatorPolicyName = 'AcceptAllPolicy';
+ chainCreators.setPolicies([chainCreatorPolicyName]);
+ var chainCreatorsItem =
+ buildSignedConfigurationItem(
+ configItemChainHeader,
+ orderer_type,
+ last_modified,
+ mod_policy,
+ 'ChainCreators',
+ chainCreators.toBuffer()
+ );
+ creation_items.push(
+ chainCreatorsItem.getConfigurationItem().toBuffer()
+ );
+
+ var acceptAllPolicy = buildAcceptAllPolicy();
+ logger.debug('accept policy::'+JSON.stringify(acceptAllPolicy));
+ var acceptAllPolicyItem =
+ buildSignedConfigurationItem(
+ configItemChainHeader,
+ policy_type,
+ last_modified,
+ mod_policy,
+ chainCreatorPolicyName,
+ acceptAllPolicy.toBuffer()
+ );
+ creation_items.push(
+ acceptAllPolicyItem.getConfigurationItem().toBuffer()
+ );
+
+ var rejectAllPolicy = buildRejectAllPolicy();
+ logger.debug('reject policy::'+JSON.stringify(rejectAllPolicy));
+ var defaultModificationPolicyItem =
+ buildSignedConfigurationItem(
+ configItemChainHeader,
+ policy_type,
+ last_modified,
+ mod_policy,
+ 'DefaultModificationPolicy',
+ rejectAllPolicy.toBuffer()
+ );
+ creation_items.push(
+ defaultModificationPolicyItem.getConfigurationItem().toBuffer()
+ );
+
+ // hash all the bytes of all items
+ var itemBytes = Buffer.concat(creation_items);
+ logger.debug('initializeChain - itemBytes::'+itemBytes.toString('hex'));
+ //var creation_items_hash = this.cryptoPrimitives.hash(itemBytes);
+ var hashPrimitives = require('./hash.js');
+ var creation_items_hash = hashPrimitives.shake_256(itemBytes, 512);
+ logger.debug('initializeChain - creation_item_hash::'+creation_items_hash);
+
+ // final item to contain hash of all others
+ var creationPolicy = new _ordererConfigurationProto.CreationPolicy();
+ creationPolicy.setPolicy(chainCreatorPolicyName);
+ creationPolicy.setDigest(Buffer.from(creation_items_hash, 'hex'));
+ //creationPolicy.setDigest(creation_items_hash);
+ var createPolicyItem =
+ buildSignedConfigurationItem(
+ configItemChainHeader,
+ orderer_type,
+ last_modified,
+ mod_policy,
+ 'CreationPolicy',
+ creationPolicy.toBuffer()
+ );
+
+ logger.debug('initializeChain - all items built');
+
+ var configurationEnvelope = new _configurationProto.ConfigurationEnvelope();
+ configurationEnvelope.setItems([
+ createPolicyItem,
+ consensusTypeItem,
+ batchSizeItem,
+ chainCreatorsItem,
+ acceptAllPolicyItem,
+ defaultModificationPolicyItem
+ ]);
+
+ var chainHeader = new _commonProto.ChainHeader();
+ chainHeader.setChainID(chain_id);
+ chainHeader.setType(_commonProto.HeaderType.CONFIGURATION_TRANSACTION);
+
+ var header = new _commonProto.Header();
+ header.setChainHeader(chainHeader);
+
+ var payload = new _commonProto.Payload();
+ payload.setHeader(header);
+ payload.setData(configurationEnvelope.toBuffer());
+ //logger.debug('initializeChain - built payload::'+JSON.stringify(payload));
+
+ var payload_bytes = payload.toBuffer();
+
+ logger.debug('initializeChain - about to call sendBroadcast');
+ var self = this;
+ return this._clientContext.getUserContext()
+ .then(
+ function(userContext) {
+ let sig = userContext.getSigningIdentity().sign(payload_bytes);
+ let signature = Buffer.from(sig);
+
+ // building manually or will get protobuf errors on send
+ var envelope = {
+ signature: signature,
+ payload : payload_bytes
+ };
+
+ var orderer = self.getOrderers()[0];
+ return orderer.sendBroadcast(envelope);
+ }
+ );
}
/**
@@ -298,13 +611,16 @@ var Chain = class {
* Sends a deployment proposal to one or more endorsing peers.
*
* @param {Object} request - An object containing the following fields:
- *
`chaincodePath` : required - String of the path to location of the source code of the chaincode
+ *
`chaincodePath` : required - String of the path to location of
+ * the source code of the chaincode
*
`chaincodeId` : required - String of the name of the chaincode
*
`chainId` : required - String of the name of the chain
*
`txId` : required - String of the transaction id
*
`nonce` : required - Integer of the once time number
- *
`fcn` : optional - String of the function to be called on the chaincode once deployed (default 'init')
- *
`args` : optional - String Array arguments specific to the chaincode being deployed
+ *
`fcn` : optional - String of the function to be called on
+ * the chaincode once deployed (default 'init')
+ *
`args` : optional - String Array arguments specific to
+ * the chaincode being deployed
*
`dockerfile-contents` : optional - String defining the
* @returns {Promise} A Promise for a `ProposalResponse`
* @see /protos/peer/fabric_proposal_response.proto
@@ -585,7 +901,6 @@ var Chain = class {
payload.setData(transaction.toBuffer());
let payload_bytes = payload.toBuffer();
- // sign the proposal
var self = this;
return this._clientContext.getUserContext()
@@ -852,4 +1167,73 @@ function packageChaincode(chaincodePath, chaincodeId, dockerfileContents) {
});
}
+//utility method to build a common chain header
+function buildChainHeader(type, version, chain_id, tx_id, epoch) {
+ var chainHeader = new _commonProto.ChainHeader();
+ chainHeader.setType(type); // int32
+ chainHeader.setVersion(version); // int32
+ //chainHeader.setTimeStamp(time_stamp); // google.protobuf.Timestamp
+ chainHeader.setChainID(chain_id); //string
+ chainHeader.setTxID(tx_id); //string
+ chainHeader.setEpoch(epoch); // uint64
+ //chainHeader.setExtension(extension); // bytes
+
+ return chainHeader;
+};
+
+//utility method to build a signed configuration item
+function buildSignedConfigurationItem(
+ chain_header,
+ type,
+ last_modified,
+ mod_policy,
+ key,
+ value,
+ signatures) {
+ var configurationItem = new _configurationProto.ConfigurationItem();
+ configurationItem.setHeader(chain_header); // ChainHeader
+ configurationItem.setType(type); // ConfigurationType
+ configurationItem.setLastModified(last_modified); // uint64
+ configurationItem.setModificationPolicy(mod_policy); // ModificationPolicy
+ configurationItem.setKey(key); // string
+ configurationItem.setValue(value); // bytes
+
+ var signedConfigurationItem = new _configurationProto.SignedConfigurationItem();
+ signedConfigurationItem.setConfigurationItem(configurationItem.toBuffer());
+ if(signatures) {
+ signedConfigurationItem.setSignatures(signatures);
+ }
+
+ return signedConfigurationItem;
+};
+
+//utility method to build an accept all policy
+function buildAcceptAllPolicy() {
+ return buildPolicyEnvelope(1);
+}
+
+//utility method to build a reject all policy
+function buildRejectAllPolicy() {
+ return buildPolicyEnvelope(0);
+}
+
+//utility method to build a policy with a signature policy envelope
+function buildPolicyEnvelope(nOf) {
+ logger.debug('buildPolicyEnvelope - building policy with nOf::'+nOf);
+ var nOutOf = new _configurationProto.SignaturePolicy.NOutOf();
+ nOutOf.setN(nOf);
+ nOutOf.setPolicies([]);
+ var signaturePolicy = new _configurationProto.SignaturePolicy();
+ signaturePolicy.setFrom(nOutOf);
+ var signaturePolicyEnvelope = new _configurationProto.SignaturePolicyEnvelope();
+ signaturePolicyEnvelope.setVersion(0);
+ signaturePolicyEnvelope.setPolicy(signaturePolicy);
+ signaturePolicyEnvelope.setIdentities([]);
+
+ var policy = new _configurationProto.Policy();
+ policy.setType(_configurationProto.Policy.PolicyType.SIGNATURE);
+ policy.setPolicy(signaturePolicyEnvelope.toBuffer());
+ return policy;
+};
+
module.exports = Chain;
diff --git a/hfc/lib/Orderer.js b/hfc/lib/Orderer.js
index edcde78e08..35543e3b17 100644
--- a/hfc/lib/Orderer.js
+++ b/hfc/lib/Orderer.js
@@ -90,13 +90,13 @@ var Orderer = class extends Remote {
clearTimeout(broadcast_timeout);
all_done = true;
- if(response.Status) {
- if (response.Status === 'SUCCESS') {
- logger.debug('Orderer.sendBroadcast - resolve with %s', response.Status);
+ if(response.status) {
+ if (response.status === 'SUCCESS') {
+ logger.debug('Orderer.sendBroadcast - resolve with %s', response.status);
return resolve(response);
} else {
- logger.error('Orderer.sendBroadcast - reject with %s', response.Status);
- return reject(new Error(response.Status));
+ logger.error('Orderer.sendBroadcast - reject with %s', response.status);
+ return reject(response);
}
}
else {
diff --git a/hfc/lib/protos/common/common.proto b/hfc/lib/protos/common/common.proto
index 3700ac08b1..9280821e14 100644
--- a/hfc/lib/protos/common/common.proto
+++ b/hfc/lib/protos/common/common.proto
@@ -35,10 +35,18 @@ enum Status {
}
enum HeaderType {
- MESSAGE = 0; // Used for messages which are signed but opaque
- CONFIGURATION_TRANSACTION = 1; // Used for messages which reconfigure the chain
- CONFIGURATION_ITEM = 2; // Used inside of the the reconfiguration message for signing over ConfigurationItems
- ENDORSER_TRANSACTION = 3; // Used by the SDK to submit endorser based transactions
+ MESSAGE = 0; // Used for messages which are signed but opaque
+ CONFIGURATION_TRANSACTION = 1; // Used for messages which reconfigure the chain
+ CONFIGURATION_ITEM = 2; // Used inside of the the reconfiguration message for signing over ConfigurationItems
+ ENDORSER_TRANSACTION = 3; // Used by the SDK to submit endorser based transactions
+ ORDERER_TRANSACTION = 4; // Used internally by the orderer for management
+}
+
+// This enum enlist indexes of the block metadata array
+enum BlockMetadataIndex {
+ SIGNATURES = 0; // Block metadata array position for block signatures
+ LAST_CONFIGURATION = 1; // Block metadata array poistion to store last configuration block sequence number
+ TRANSACTIONS_FILTER = 2; // Block metadata array poistion to store serialized bit array filter of invalid transactions
}
message Header {
diff --git a/hfc/lib/protos/common/configuration.proto b/hfc/lib/protos/common/configuration.proto
index cedd2612c3..edc3983b70 100644
--- a/hfc/lib/protos/common/configuration.proto
+++ b/hfc/lib/protos/common/configuration.proto
@@ -73,12 +73,18 @@ message ConfigurationSignature {
bytes signature = 2; // Signature over the concatenation of configurationItem bytes and signatureHeader bytes
}
+//
+
// Policy expresses a policy which the orderer can evaluate, because there has been some desire expressed to support
// multiple policy engines, this is typed as a oneof for now
message Policy {
- oneof Type {
- SignaturePolicyEnvelope SignaturePolicy = 1;
+ enum PolicyType {
+ UNKNOWN = 0; // Reserved to check for proper initialization
+ SIGNATURE = 1;
+ MSP = 2;
}
+ int32 type = 1; // For outside implementors, consider the first 1000 types reserved, otherwise one of PolicyType
+ bytes policy = 2;
}
// SignaturePolicyEnvelope wraps a SignaturePolicy and includes a version for future enhancements
diff --git a/hfc/lib/protos/orderer/ab.proto b/hfc/lib/protos/orderer/ab.proto
index b809f7ed7f..3edcf8981c 100644
--- a/hfc/lib/protos/orderer/ab.proto
+++ b/hfc/lib/protos/orderer/ab.proto
@@ -23,41 +23,48 @@ option go_package = "github.com/hyperledger/fabric/protos/orderer";
package orderer;
message BroadcastResponse {
- common.Status Status = 1;
+ common.Status status = 1;
}
-message SeekInfo {
- // Start may be specified to a specific block number, or may be request from the newest or oldest available
- // The start location is always inclusive, so the first reply from NEWEST will contain the newest block at the time
- // of reception, it will must not wait until a new block is created. Similarly, when SPECIFIED, and SpecifiedNumber = 10
- // The first block received must be block 10, not block 11
- enum StartType {
- NEWEST = 0;
- OLDEST = 1;
- SPECIFIED = 2;
- }
- StartType Start = 1;
- uint64 SpecifiedNumber = 2; // Only used when start = SPECIFIED
- uint64 WindowSize = 3; // The window size is the maximum number of blocks that will be sent without Acknowledgement, the base of the window moves to the most recently received acknowledgment
- string ChainID = 4; // The chain to seek within
-}
+message SeekNewest { }
+
+message SeekOldest { }
-message Acknowledgement {
- uint64 Number = 1;
+message SeekSpecified {
+ uint64 number = 1;
}
-// The update message either causes a seek to a new stream start with a new window, or acknowledges a received block and advances the base of the window
-message DeliverUpdate {
+message SeekPosition {
oneof Type {
- Acknowledgement Acknowledgement = 1; // Acknowledgement should be sent monotonically and only for a block which has been received, Acknowledgements received non-monotonically has undefined behavior
- SeekInfo Seek = 2; // When set, SeekInfo causes a seek and potential reconfiguration of the window size
+ SeekNewest newest = 1;
+ SeekOldest oldest = 2;
+ SeekSpecified specified = 3;
+ }
+}
+
+// SeekInfo specifies the range of requested blocks to return
+// If the start position is not found, an error is immediately returned
+// Otherwise, blocks are returned until a missing block is encountered, then behavior is dictated
+// by the SeekBehavior specified. If BLOCK_UNTIL_READY is specified, the reply will block until
+// the requested blocks are available, if FAIL_IF_NOT_READY is specified, the reply will return an
+// error indicating that the block is not found. To request that all blocks be returned indefinitely
+// as they are created, behavior should be set to BLOCK_UNTIL_READY and the stop should be set to
+// specified with a number of MAX_UINT64
+message SeekInfo {
+ enum SeekBehavior {
+ BLOCK_UNTIL_READY = 0;
+ FAIL_IF_NOT_READY = 1;
}
+ string chainID = 1; // The chain to seek within
+ SeekPosition start = 2; // The position to start the deliver from
+ SeekPosition stop = 3; // The position to stop the deliver
+ SeekBehavior behavior = 4; // The behavior when a missing block is encountered
}
message DeliverResponse {
oneof Type {
- common.Status Error = 1;
- common.Block Block = 2;
+ common.Status status = 1;
+ common.Block block = 2;
}
}
@@ -66,7 +73,5 @@ service AtomicBroadcast {
rpc Broadcast(stream common.Envelope) returns (stream BroadcastResponse) {}
// deliver first requires an update containing a seek message, then a stream of block replies is received.
- // The receiver may choose to send an Acknowledgement for any block number it receives, however Acknowledgements must never be more than WindowSize apart
- // To avoid latency, clients will likely acknowledge before the WindowSize has been exhausted, preventing the server from stopping and waiting for an Acknowledgement
- rpc Deliver(stream DeliverUpdate) returns (stream DeliverResponse) {}
+ rpc Deliver(stream SeekInfo) returns (stream DeliverResponse) {}
}
diff --git a/hfc/lib/protos/orderer/configuration.proto b/hfc/lib/protos/orderer/configuration.proto
new file mode 100644
index 0000000000..990c70a400
--- /dev/null
+++ b/hfc/lib/protos/orderer/configuration.proto
@@ -0,0 +1,76 @@
+/*
+Copyright IBM Corp. 2016 All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+syntax = "proto3";
+
+import "../common/common.proto";
+
+option go_package = "github.com/hyperledger/fabric/protos/orderer";
+
+package orderer;
+
+
+// The orderer config is specified by the following convention:
+// For a configuration item with key "Key"
+// the encoded value is a a proto message of type "Key"
+// For example, for the configuration item of name "ConsensusType"
+// the encoded value is the proto message "ConsensusType"
+
+message ConsensusType {
+ string type = 1;
+}
+
+message BatchSize {
+ // Simply specified as number of messages for now, in the future
+ // we may want to allow this to be specified by size in bytes
+ uint32 maxMessageCount = 1;
+}
+
+message BatchTimeout {
+ // Any duration string parseable by ParseDuration():
+ // https://golang.org/pkg/time/#ParseDuration
+ string timeout = 1;
+}
+
+// When submitting a new chain configuration transaction to create a new chain,
+// the first configuration item must be of type Orderer with Key CreationPolicy
+// and contents of a Marshaled CreationPolicy. The policy should be set to the
+// policy which was supplied by the ordering service for the client's chain
+// creation. The digest should be the hash of the concatenation of the remaining
+// ConfigurationItem bytes. The signatures of the configuration item should
+// satisfy the policy for chain creation.
+message CreationPolicy {
+ // The name of the policy which should be used to validate the creation of
+ // this chain
+ string policy = 1;
+
+ // The hash of the concatenation of remaining configuration item bytes
+ bytes digest = 2;
+}
+
+message ChainCreators {
+ // A list of policies, any of which may be specified as the chain creation
+ // policy in a chain creation request
+ repeated string policies = 1;
+}
+
+// Carries a list of bootstrap brokers, i.e. this is not the exclusive set of
+// brokers an ordering service
+message KafkaBrokers {
+ // Each broker here should be identified using the (IP|host):port notation,
+ // e.g. 127.0.0.1:7050, or localhost:7050 are valid entries
+ repeated string brokers = 1;
+}
diff --git a/hfc/lib/protos/peer/events.proto b/hfc/lib/protos/peer/events.proto
index 2a0821654b..6221403eee 100644
--- a/hfc/lib/protos/peer/events.proto
+++ b/hfc/lib/protos/peer/events.proto
@@ -16,9 +16,9 @@ limitations under the License.
syntax = "proto3";
+import "../common/common.proto";
import "chaincodeevent.proto";
import "fabric_transaction.proto";
-import "fabric_block.proto";
option go_package = "github.com/hyperledger/fabric/protos/peer";
diff --git a/hfc/lib/protos/peer/fabric_block.proto b/hfc/lib/protos/peer/fabric_block.proto
deleted file mode 100644
index e8a7e26636..0000000000
--- a/hfc/lib/protos/peer/fabric_block.proto
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
-Copyright IBM Corp. 2016 All Rights Reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-syntax = "proto3";
-
-option go_package = "github.com/hyperledger/fabric/protos/peer";
-
-package protos;
-
-// Block contains a list of transactions and the crypto hash of previous block
-message Block2 {
- bytes PreviousBlockHash = 1;
- // transactions are stored in serialized form so that the concenters can avoid marshaling of transactions
- repeated bytes Transactions = 2;
-}
diff --git a/test/unit/end-to-end.js b/test/unit/end-to-end.js
index d05185d766..b22313774b 100644
--- a/test/unit/end-to-end.js
+++ b/test/unit/end-to-end.js
@@ -140,9 +140,9 @@ test('End-to-end flow of chaincode deploy, transaction invocation, and query', f
// in sequence, will need to sleep for 30sec here
promise = promise.then(
function(response) {
- if (response.Status === 'SUCCESS') {
+ if (response.status === 'SUCCESS') {
t.pass('Successfully ordered deployment endorsement.');
- console.log(' need to wait now for the committer to catch up after the deployment');
+ console.log(' ** need to wait now for the committer to catch up after the deployment');
return sleep(30000);
} else {
t.fail('Failed to order the deployment endorsement. Error code: ' + response.status);
@@ -238,13 +238,13 @@ test('End-to-end flow of chaincode deploy, transaction invocation, and query', f
// in sequence, will need to sleep for 30sec here
promise = promise.then(
function(response) {
- if (response.Status === 'SUCCESS') {
+ if (response.status === 'SUCCESS') {
t.pass('Successfully ordered endorsement transaction.');
} else {
t.fail('Failed to order the endorsement of the transaction. Error code: ' + response.status);
}
// always sleep and check with query
- console.log(' need to wait now for the committer to catch up after the **** MOVE ****');
+ console.log(' ** need to wait now for the committer to catch up after the **** MOVE ****');
t.end();
return sleep(30000);
},
diff --git a/test/unit/headless-tests.js b/test/unit/headless-tests.js
index 9036694bd7..e4a1f9f3d9 100644
--- a/test/unit/headless-tests.js
+++ b/test/unit/headless-tests.js
@@ -475,11 +475,117 @@ test('\n\n ** Chain - method tests **\n\n', function (t) {
_chain.addOrderer(orderer);
},
null,
- 'checking the set of Orderers'
+ 'checking the chain addOrderer()'
+ );
+ t.equal(_chain.getOrderers()[0].toString(), ' Orderer : {url:grpc://somehost.com:1234}', 'checking chain getOrderers()');
+ t.throws(
+ function () {
+ var orderer = new Orderer('grpc://somehost.com:1234');
+ _chain.addOrderer(orderer);
+ },
+ /^DuplicateOrderer: Orderer with URL/,
+ 'Chain tests: checking that orderer already exists.'
);
- t.equal(_chain.getOrderers()[0].toString(), ' Orderer : {url:grpc://somehost.com:1234}', 'checking getOrderers orderer');
t.equal(_chain.toString(), '{"name":"testChain","orderers":" Orderer : {url:grpc://somehost.com:1234}|"}', 'checking chain toString');
- t.end();
+
+ _chain.setConsesusType('SOMETYPE');
+ t.equal(_chain.getConsesusType(), 'SOMETYPE', 'Chain tests: checking set and get Consesus type');
+ t.throws(
+ function () {
+ _chain.setInitialEpoch(-1);
+ },
+ /^Error: initial epoch must be a positive integer/,
+ 'Chain tests: checking that epoch should be positive integer when input is negative.'
+ );
+ t.throws(
+ function () {
+ _chain.setInitialEpoch(1.1);
+ },
+ /^Error: initial epoch must be a positive integer/,
+ 'Chain tests: checking that epoch should be positive integer when input is float.'
+ );
+ t.throws(
+ function () {
+ _chain.setInitialEpoch('a');
+ },
+ /^Error: initial epoch must be a positive integer/,
+ 'Chain tests: checking that epoch should be positive integer when inut is char.'
+ );
+ t.doesNotThrow(
+ function () {
+ _chain.setInitialEpoch(3);
+ },
+ null,
+ 'checking the chain setInitialEpoch()'
+ );
+ t.equal(_chain.getInitialEpoch(), 3, 'Chain tests: checking set and get initial epoch');
+ t.throws(
+ function () {
+ _chain.setInitialMaxMessageCount(-1);
+ },
+ /^Error: initial maximum message count must be a positive integer/,
+ 'Chain tests: checking that max message count should be positive integer when input is negative.'
+ );
+ t.throws(
+ function () {
+ _chain.setInitialMaxMessageCount(1.1);
+ },
+ /^Error: initial maximum message count must be a positive integer/,
+ 'Chain tests: checking that max message count should be positive integer when input is float.'
+ );
+ t.throws(
+ function () {
+ _chain.setInitialMaxMessageCount('a');
+ },
+ /^Error: initial maximum message count must be a positive integer/,
+ 'Chain tests: checking that max message count should be positive integer when inut is char.'
+ );
+ t.doesNotThrow(
+ function () {
+ _chain.setInitialMaxMessageCount(30);
+ },
+ null,
+ 'checking the chain setInitialMaxMessageCount()'
+ );
+ t.equal(_chain.getInitialMaxMessageCount(), 30, 'Chain tests: checking set and get initial max message count');
+
+ t.doesNotThrow(
+ function () {
+ _chain.setInitialTransactionId('abcde');
+ },
+ null,
+ 'checking the chain setInitialTransactionId()'
+ );
+ t.equal(_chain.getInitialTransactionId(), 'abcde', 'Chain tests: checking set and get initial transaction id');
+ var test_chain = new Chain('someTestChain', client);
+ test_chain.initializeChain().then(
+ function (response) {
+ t.fail('Chain tests: orderer should have been required');
+ },
+ function (error) {
+ if(!error) {
+ t.fail('Should be getting an error back');
+ }
+ else {
+ t.equals(error.toString(),'Error: no primary orderer defined','Chain tests: orederer is required when initializing');
+ }
+ }
+ );
+ var test_chain2 = new Chain('someTestChain2', {_userContext : {} });
+ test_chain2.addOrderer(new Orderer('grpc://somehost.com:1234'));
+ test_chain2.initializeChain().then(
+ function (response) {
+ t.fail('Chain tests: transaction should have been required');
+ },
+ function (error) {
+ if(!error) {
+ t.fail('Should be getting an error back');
+ }
+ else {
+ t.equals(error.toString(),'Error: Initial transaction id is not defined','Chain tests: transaction id is required when initializing');
+ }
+ }
+ ); t.end();
});
// User tests /////////
diff --git a/test/unit/new-chain.js b/test/unit/new-chain.js
new file mode 100644
index 0000000000..154e69e039
--- /dev/null
+++ b/test/unit/new-chain.js
@@ -0,0 +1,187 @@
+/**
+ * Copyright 2016 IBM All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var tape = require('tape');
+var _test = require('tape-promise');
+var test = _test(tape);
+process.env.HFC_LOGGING = '{"debug": "console"}';
+var hfc = require('hfc');
+var util = require('util');
+var fs = require('fs');
+var testUtil = require('./util.js');
+
+var Orderer = require('hfc/lib/Orderer.js');
+var User = require('hfc/lib/User.js');
+
+var keyValStorePath = testUtil.KVS;
+//
+//Orderer via member send chain create
+//
+//Attempt to send a request to the orderer with the sendCreateChain method - fail
+// missing order or invalid order
+//
+test('\n\n** TEST ** new chain using chain.initializeChain() method with bad orderer address', function(t) {
+ //
+ // Create and configure the test chain
+ //
+ var client = new hfc();
+ client.setStateStore(hfc.newDefaultKeyValueStore({
+ path: testUtil.KVS
+ }));
+ var chain = client.newChain('testChain2');
+ chain.setInitialTransactionId('1234');
+ chain.addOrderer(new Orderer('grpc://localhost:9999'));
+
+ testUtil.getSubmitter(client, t)
+ .then(
+ function(admin) {
+ t.pass('Successfully enrolled user \'admin\'');
+ // send to orderer
+ return chain.initializeChain();
+ },
+ function(err) {
+ t.fail('Failed to enroll user \'admin\'. ' + err);
+ t.end();
+ }
+ )
+ .then(
+ function(response) {
+ if (response) {
+ t.fail('Successfully created chain.');
+ } else {
+ t.fail('Failed to order the chain create. Error code: ' + response.status);
+ }
+ t.end();
+ },
+ function(err) {
+ t.pass('Failed to send transaction create due to error: ' + err.stack ? err.stack : err);
+ t.end();
+ }
+ )
+ .catch(function(err) {
+ t.pass('Failed request. ' + err);
+ t.end();
+ });
+});
+
+//
+//Orderer via member send chain create
+//
+//Attempt to send a request to the orderer with the sendCreateChain method - good
+//
+test('\n\n** TEST ** new chain - chain.initializeChain() success', function(t) {
+ //
+ // Create and configure the test chain
+ //
+ var client = new hfc();
+ client.setStateStore(hfc.newDefaultKeyValueStore({
+ path: testUtil.KVS
+ }));
+ var chain = client.newChain('testChain2');
+ chain.setInitialTransactionId('1234');
+ chain.addOrderer(new Orderer('grpc://localhost:7050'));
+
+ testUtil.getSubmitter(client, t)
+ .then(
+ function(admin) {
+ t.pass('Successfully enrolled user \'admin\'');
+ // send to orderer
+ return chain.initializeChain();
+ },
+ function(err) {
+ t.fail('Failed to enroll user \'admin\'. ' + err);
+ t.end();
+ }
+ )
+ .then(
+ function(response) {
+ if (response.status === 'SUCCESS') {
+ t.pass('Successfully created chain.');
+ } else {
+ t.fail('Failed to get correct error. Error code: ' + response);
+ }
+ t.end();
+ },
+ function(err) {
+ if (err.status === 'BAD_REQUEST') {
+ t.fail('Failed to create chain.' + err);
+ }
+ else {
+ t.fail('Failed to get error status. Error code: ' + err);
+ }
+ t.end();
+ }
+ )
+ .catch(function(err) {
+ t.fail('Failed request. ' + err);
+ t.end();
+ });
+});
+
+//
+//Orderer via member send chain create
+//
+//Attempt to send a request to the orderer with the sendCreateChain method - fail
+// fail due to chain already exist
+//
+test('\n\n** TEST ** new chain - chain.initializeChain() fail due to already exist', function(t) {
+ //
+ // Create and configure the test chain
+ //
+ var client = new hfc();
+ client.setStateStore(hfc.newDefaultKeyValueStore({
+ path: testUtil.KVS
+ }));
+ var chain = client.newChain('testChain2');
+ chain.setInitialTransactionId('1234');
+ chain.addOrderer(new Orderer('grpc://localhost:7050'));
+
+ testUtil.getSubmitter(client, t)
+ .then(
+ function(admin) {
+ t.pass('Successfully enrolled user \'admin\'');
+ // send to orderer
+ return chain.initializeChain();
+ },
+ function(err) {
+ t.fail('Failed to enroll user \'admin\'. ' + err);
+ t.end();
+ }
+ )
+ .then(
+ function(response) {
+ if (response.status === 'SUCCESS') {
+ t.fail('Failed, the chain was created again.');
+ } else {
+ t.fail('Failed to get correct error. Response code: ' + response);
+ }
+ t.end();
+ },
+ function(err) {
+ if (err.status === 'BAD_REQUEST') {
+ t.pass('Received the correct error message.');
+ }
+ else {
+ t.fail('Failed to get correct error. Error code: ' + err);
+ }
+ t.end();
+ }
+ )
+ .catch(function(err) {
+ t.pass('Failed request. ' + err);
+ t.end();
+ });
+});