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(); + }); +});