diff --git a/build/tasks/test.js b/build/tasks/test.js index b407dac7c0..cbf0348489 100644 --- a/build/tasks/test.js +++ b/build/tasks/test.js @@ -92,8 +92,8 @@ gulp.task('clean-up', () => { gulp.task('docker-clean', shell.task([ // stop and remove chaincode docker instances - 'docker kill $(docker ps | grep "dev-" | awk \'{print $1}\')', - 'docker rm $(docker ps -a | grep "dev-" | awk \'{print $1}\')', + 'docker kill $(docker ps -aq)', + 'docker rm $(docker ps -aq) -f', // remove chaincode images so that they get rebuilt during test 'docker rmi $(docker images | grep "^dev-" | awk \'{print $3}\')', @@ -103,7 +103,7 @@ gulp.task('docker-clean', shell.task([ 'docker-compose -f test/fixtures/docker-compose/docker-compose-tls.yaml -p node down' ], { verbose: true, // so we can see the docker command output - ignoreErrors: true // kill and rm may fail because the containers may have been cleaned up + ignoreErrors: true // kill, rm, and rmi may fail because the containers may have been cleaned up or not exist })); gulp.task('docker-ready', ['docker-clean'], shell.task([ @@ -294,10 +294,6 @@ gulp.task('run-tape-e2e', ['docker-ready'], // Typescript 'test/typescript/test.js', - - // Perf - 'test/integration/perf/orderer.js', - 'test/integration/perf/peer.js' ])) .pipe(tape({ reporter: tapColorize() diff --git a/fabric-ca-client/lib/FabricCAServices.js b/fabric-ca-client/lib/FabricCAServices.js index b1ea35c1a0..ad228b23cc 100644 --- a/fabric-ca-client/lib/FabricCAServices.js +++ b/fabric-ca-client/lib/FabricCAServices.js @@ -198,12 +198,7 @@ const FabricCAServices = class extends BaseClient { } } - let opts; - if (this.getCryptoSuite()._cryptoKeyStore) { - opts = {ephemeral: false}; - } else { - opts = {ephemeral: true}; - } + const storeKey = this.getCryptoSuite()._cryptoKeyStore ? true : false; try { let csr; @@ -213,7 +208,11 @@ const FabricCAServices = class extends BaseClient { csr = req.csr; } else { try { - privateKey = await this.getCryptoSuite().generateKey(opts); + if (storeKey) { + privateKey = await this.getCryptoSuite().generateKey(); + } else { + privateKey = this.getCryptoSuite().generateEphemeralKey(); + } logger.debug('successfully generated key pairs'); } catch (err) { throw new Error(util.format('Failed to generate key for enrollment due to error [%s]', err)); diff --git a/fabric-ca-client/test/FabricCAServices.js b/fabric-ca-client/test/FabricCAServices.js index 69cff28358..3315312e2b 100644 --- a/fabric-ca-client/test/FabricCAServices.js +++ b/fabric-ca-client/test/FabricCAServices.js @@ -219,6 +219,7 @@ describe('FabricCAServices', () => { service = new FabricCAServicesRewire('http://penguin.com', null, 'ca_name', cryptoPrimitives); clientMock = sinon.createStubInstance(FabricCAClient); service._fabricCAClient = clientMock; + cryptoPrimitives._cryptoKeyStore = false; }); it('should throw if missing required argument "request"', async () => { @@ -252,7 +253,7 @@ describe('FabricCAServices', () => { const keyStub = sinon.createStubInstance(ECDSAKey); keyStub.generateCSR.returns('CN=penguin'); - cryptoPrimitives.generateKey.resolves(keyStub); + cryptoPrimitives.generateEphemeralKey.returns(keyStub); // Take control of the enroll clientMock.enroll.rejects(new Error('enroll error')); @@ -271,6 +272,7 @@ describe('FabricCAServices', () => { const keyStub = sinon.createStubInstance(ECDSAKey); keyStub.generateCSR.throws(new Error('CSR error')); cryptoPrimitives.generateKey.resolves(keyStub); + cryptoPrimitives.generateEphemeralKey.returns(keyStub); const atts = [{name: 'penguin'}, {name: 'power'}]; const req = {enrollmentID: 'enrollmentID', enrollmentSecret: 'enrollmentSecret', profile: 'profile', attr_reqs: atts}; @@ -283,7 +285,7 @@ describe('FabricCAServices', () => { getSubjectCommonNameStub.returns('mr_penguin'); normalizeX509Stub.returns('normal'); - cryptoPrimitives.generateKey.rejects(new Error('Key error')); + cryptoPrimitives.generateEphemeralKey.throws(new Error('Key error')); const atts = [{name: 'penguin'}, {name: 'power'}]; const req = {enrollmentID: 'enrollmentID', enrollmentSecret: 'enrollmentSecret', profile: 'profile', attr_reqs: atts}; @@ -319,9 +321,8 @@ describe('FabricCAServices', () => { const req = {enrollmentID: 'enrollmentID', enrollmentSecret: 'enrollmentSecret', profile: 'profile', attr_reqs: atts}; await service.enroll(req); - // Opts should contain false - const callArgs = newSuite.generateKey.getCall(0); - callArgs.args[0].should.deep.equal({ephemeral: false}); + // should call generateKey, not generateEphemeral + sinon.assert.called(newSuite.generateKey); }); @@ -333,6 +334,7 @@ describe('FabricCAServices', () => { const keyStub = sinon.createStubInstance(ECDSAKey); keyStub.generateCSR.returns('CN=penguin'); cryptoPrimitives.generateKey.resolves(keyStub); + cryptoPrimitives._cryptoKeyStore = true; // Take control of the enroll clientMock.enroll.resolves({ @@ -343,9 +345,8 @@ describe('FabricCAServices', () => { const req = {enrollmentID: 'enrollmentID', enrollmentSecret: 'enrollmentSecret', profile: 'profile', attr_reqs: atts}; await service.enroll(req); - // generateKey should be called with ephmereal set to true - const genKeyArgs = cryptoPrimitives.generateKey.getCall(0); - genKeyArgs.args[0].should.deep.equal({ephemeral: true}); + // generateKey should be called + sinon.assert.calledOnce(cryptoPrimitives.generateKey); // Enrol should be called with test values sinon.assert.calledOnce(clientMock.enroll); @@ -367,6 +368,7 @@ describe('FabricCAServices', () => { const keyStub = sinon.createStubInstance(ECDSAKey); keyStub.generateCSR.returns('CN=penguin'); cryptoPrimitives.generateKey.resolves(keyStub); + cryptoPrimitives._cryptoKeyStore = true; // Take control of the enroll clientMock.enroll.resolves({ diff --git a/fabric-client/lib/BaseClient.js b/fabric-client/lib/BaseClient.js index c1918fdfee..5d9fc3b371 100644 --- a/fabric-client/lib/BaseClient.js +++ b/fabric-client/lib/BaseClient.js @@ -73,12 +73,13 @@ const BaseClient = class { * This can be overriden with a configuration setting key-value-store, the value of which is the * full path of a CommonJS module for the alternative implementation. * + * @async * @param {Object} options Specific to the implementation, for initializing the instance. For the built-in * file-based implementation, this requires a single property path to the top-level folder for the store * @returns {Promise} A Promise for a {@link module:api.KeyValueStore} instance of the KeyValueStore implementation */ - static newDefaultKeyValueStore(options) { - return sdkUtils.newKeyValueStore(options); + static async newDefaultKeyValueStore(options) { + return await sdkUtils.newKeyValueStore(options); } /** diff --git a/fabric-client/lib/Channel.js b/fabric-client/lib/Channel.js index 7418a3747b..c630afd89d 100644 --- a/fabric-client/lib/Channel.js +++ b/fabric-client/lib/Channel.js @@ -3704,7 +3704,7 @@ const Channel = class { * @returns {boolean} A boolean value of true when both the identity and * the signature are valid, false otherwise. */ - verifyProposalResponse(proposal_response) { + async verifyProposalResponse(proposal_response) { logger.debug('verifyProposalResponse - start'); if (!proposal_response) { throw new Error('Missing proposal response'); @@ -3730,7 +3730,7 @@ const Channel = class { logger.debug('verifyProposalResponse - found endorser\'s MSP'); try { - identity = msp.deserializeIdentity(endorsement.endorser, false); + identity = await msp.deserializeIdentity(endorsement.endorser, false); if (!identity) { throw new Error('Unable to find the endorser identity'); } diff --git a/fabric-client/lib/Client.js b/fabric-client/lib/Client.js index 65f5cc59cb..dc3c46f5e4 100644 --- a/fabric-client/lib/Client.js +++ b/fabric-client/lib/Client.js @@ -106,12 +106,13 @@ const Client = class extends BaseClient { /** * Load a common connection profile object or load a JSON file and return a Client object. * + * @async * @param {object | string} loadConfig - This may be the config object or a path to the configuration file * @return {Client} An instance of this class initialized with the network end points. */ - static loadFromConfig(loadConfig) { + static async loadFromConfig(loadConfig) { const client = new Client(); - client.loadFromConfig(loadConfig); + await client.loadFromConfig(loadConfig); return client; } @@ -119,9 +120,10 @@ const Client = class extends BaseClient { * Load a common connection profile object or load a JSON file and update this client with * any values in the config. * + * @async * @param {object | string} config - This may be the config object or a path to the configuration file */ - loadFromConfig(loadConfig) { + async loadFromConfig(loadConfig) { const additional_network_config = _getNetworkConfig(loadConfig, this); if (!this._network_config) { this._network_config = additional_network_config; @@ -129,7 +131,7 @@ const Client = class extends BaseClient { this._network_config.mergeSettings(additional_network_config); } if (this._network_config.hasClient()) { - this._setAdminFromConfig(); + await this._setAdminFromConfig(); this._setMspidFromConfig(); this._addConnectionOptionsFromConfig(); } @@ -889,6 +891,7 @@ const Client = class extends BaseClient { * Queries the target peer for a list of {@link Peer} objects of all peers * known by the target peer. * + * @async * @param {PeerQueryRequest} request - The request parameters. * @returns {PeerQueryResponse} The list of peer information */ @@ -941,6 +944,7 @@ const Client = class extends BaseClient { * Queries the target peer for the names of all the channels that a * peer has joined. * + * @async * @param {Peer} peer - The target peer to send the query * @param {boolean} useAdmin - Optional. Indicates that the admin credentials * should be used in making this call to the peer. An administrative @@ -1015,6 +1019,7 @@ const Client = class extends BaseClient { /** * Queries the installed chaincodes on a peer. * + * @async * @param {Peer} peer - The target peer * @param {boolean} useAdmin - Optional. Indicates that the admin credentials * should be used in making this call to the peer. An administrative @@ -1124,6 +1129,7 @@ const Client = class extends BaseClient { * performed on a peer-by-peer basis. Only the peer organization's ADMIN * identities are allowed to perform this operation. * + * @async * @deprecated * @param {Deprecated_ChaincodeInstallRequest} request - The request object * @param {Number} timeout - A number indicating milliseconds to wait on the @@ -1228,8 +1234,6 @@ const Client = class extends BaseClient { * from the common connection profile along with the system configuration to build * instances of the stores and assign them to this client and the crypto suites * if needed. - * - * @returns {Promise} - A promise to build a key value store and crypto store. */ async initCredentialStores() { if (!this._network_config) { @@ -1241,9 +1245,9 @@ const Client = class extends BaseClient { this.setStateStore(key_value_store); const crypto_suite = BaseClient.newCryptoSuite(); // all crypto suites should extends api.CryptoSuite - crypto_suite.setCryptoKeyStore(BaseClient.newCryptoKeyStore(client_config.credentialStore.cryptoStore)); + const cryptoStore = BaseClient.newCryptoKeyStore(client_config.credentialStore.cryptoStore); + crypto_suite.setCryptoKeyStore(cryptoStore); this.setCryptoSuite(crypto_suite); - return true; } else { throw new Error('No credentialStore settings found'); } @@ -1305,11 +1309,13 @@ const Client = class extends BaseClient { * Set the admin signing identity object. This method will only assign a * signing identity for use by this client instance and will not persist * the identity. + * + * @async * @param {string} private_key - the private key PEM string * @param {string} certificate the PEM-encoded string of certificate * @param {string} mspid The Member Service Provider id for the local signing identity */ - setAdminSigningIdentity(private_key, certificate, mspid) { + async setAdminSigningIdentity(private_key, certificate, mspid) { logger.debug('setAdminSigningIdentity - start mspid:%s', mspid); if (typeof private_key === 'undefined' || private_key === null || private_key === '') { throw new Error('Invalid parameter. Must have a valid private key.'); @@ -1324,8 +1330,8 @@ const Client = class extends BaseClient { if (!crypto_suite) { crypto_suite = BaseClient.newCryptoSuite(); } - const key = crypto_suite.importKey(private_key, {ephemeral: true}); - const public_key = crypto_suite.importKey(certificate, {ephemeral: true}); + const key = await crypto_suite.createKeyFromRaw(private_key); + const public_key = await crypto_suite.createKeyFromRaw(certificate); this._adminSigningIdentity = new SigningIdentity(certificate, public_key, mspid, crypto_suite, new Signer(crypto_suite, key)); } @@ -1336,7 +1342,7 @@ const Client = class extends BaseClient { * be must loaded that defines an organization for this client and have an * admin credentials defined. */ - _setAdminFromConfig() { + async _setAdminFromConfig() { let admin_key, admin_cert, mspid = null; if (!this._network_config) { throw new Error('No common connection profile has been loaded'); @@ -1353,7 +1359,7 @@ const Client = class extends BaseClient { } // if we found all we need then set the admin if (admin_key && admin_cert && mspid) { - this.setAdminSigningIdentity(admin_key, admin_cert, mspid); + await this.setAdminSigningIdentity(admin_key, admin_cert, mspid); } } @@ -1397,6 +1403,7 @@ const Client = class extends BaseClient { * and the organization in the client section of the common connection profile * settings. * + * @async * @param {Object} opts - contains * - username [required] - username of the user * - password [optional] - password of the user @@ -1463,6 +1470,7 @@ const Client = class extends BaseClient { /** * Persist the current userContext to the key value store. * + * @async * @returns {Promise} A Promise for the userContext object upon successful persistence */ async saveUserToStateStore() { @@ -1508,6 +1516,7 @@ const Client = class extends BaseClient { * has been set on the Client instance. If no state store has been set, this cache will not be established * and the application is responsible for setting the user context again if the application crashes and is recovered. * + * @async * @param {User | UserNamePasswordObject} user - An instance of the User class encapsulating the authenticated * user’s signing materials (private key and enrollment certificate). * The parameter may also be a {@link UserNamePasswordObject} that contains the username @@ -1553,6 +1562,7 @@ const Client = class extends BaseClient { * (via the KeyValueStore interface). The loaded user object must represent an enrolled user with a valid * enrollment certificate signed by a trusted CA (such as the CA server). * + * @async * @param {string} name - Optional. If not specified, will only return the current in-memory user context object, or null * if none has been set. If "name" is specified, will also attempt to load it from the state store * if search in memory failed. @@ -1609,6 +1619,7 @@ const Client = class extends BaseClient { /** * Restore the state of the {@link User} by the given name from the key value store (if found). If not found, return null. * + * @async * @param {string} name - Name of the user * @returns {Promise} A Promise for a {User} object upon successful restore, or if the user by the name * does not exist in the state store, returns null without rejecting the promise @@ -1671,6 +1682,7 @@ const Client = class extends BaseClient { * Note that upon successful creation of the new user object, it is set to * the client instance as the current userContext. * + * @async * @param {UserOpts} opts - Essential information about the user * @returns {Promise} Promise for the user object. */ @@ -1699,7 +1711,8 @@ const Client = class extends BaseClient { if (this.getCryptoSuite() === null) { logger.debug('cryptoSuite is null, creating default cryptoSuite and cryptoKeyStore'); this.setCryptoSuite(sdkUtils.newCryptoSuite()); - this.getCryptoSuite().setCryptoKeyStore(Client.newCryptoKeyStore()); // This is impossible + const cryptoStore = Client.newCryptoKeyStore(); + this.getCryptoSuite().setCryptoKeyStore(cryptoStore); } else { if (this.getCryptoSuite()._cryptoKeyStore) { logger.debug('cryptoSuite has a cryptoKeyStore'); @@ -1727,9 +1740,9 @@ const Client = class extends BaseClient { if (privateKeyPEM) { logger.debug('then privateKeyPEM data'); if (opts.skipPersistence) { - importedKey = await this.getCryptoSuite().importKey(privateKeyPEM.toString(), {ephemeral: true}); + importedKey = await this.getCryptoSuite().createKeyFromRaw(privateKeyPEM.toString()); } else { - importedKey = await this.getCryptoSuite().importKey(privateKeyPEM.toString(), {ephemeral: !this.getCryptoSuite()._cryptoKeyStore}); + importedKey = await this.getCryptoSuite().importKey(privateKeyPEM.toString()); } } else { importedKey = opts.cryptoContent.privateKeyObj; diff --git a/fabric-client/lib/User.js b/fabric-client/lib/User.js index 1f30c22196..77b4819937 100644 --- a/fabric-client/lib/User.js +++ b/fabric-client/lib/User.js @@ -147,6 +147,8 @@ const User = class { /** * Set the enrollment object for this User instance + * + * @async * @param {module:api.Key} privateKey the private key object * @param {string} certificate the PEM-encoded string of certificate * @param {string} mspId The Member Service Provider id for the local signing identity @@ -179,7 +181,7 @@ const User = class { if (this._cryptoSuite._cryptoKeyStore && !skipPersistence) { pubKey = await this._cryptoSuite.importKey(certificate); } else { - pubKey = await this._cryptoSuite.importKey(certificate, {ephemeral: true}); + pubKey = await this._cryptoSuite.createKeyFromRaw(certificate); } this._identity = new Identity(certificate, pubKey, mspId, this._cryptoSuite); @@ -196,11 +198,13 @@ const User = class { /** * Set the current state of this member from a string based JSON object + * + * @async * @param {string} str - the member state serialized * @param {boolean} no_save - to indicate that the cryptoSuite should not save * @return {Member} Promise of the unmarshalled Member object represented by the serialized string */ - fromString(str, no_save) { + async fromString(str, no_save) { logger.debug('fromString --start'); const state = JSON.parse(str); @@ -223,52 +227,36 @@ const User = class { this._cryptoSuite.setCryptoKeyStore(sdkUtils.newCryptoKeyStore()); } - const self = this; let pubKey; - let import_promise = null; const opts = {algorithm: CryptoAlgorithms.X509Certificate}; if (no_save) { - opts.ephemeral = true; - import_promise = new Promise((resolve, reject) => { - const key = this._cryptoSuite.importKey(state.enrollment.identity.certificate, opts); - // construct Promise because importKey does not return Promise when ephemeral is true - if (key) { - resolve(key); - } else { - reject(new Error('Import of saved user has failed')); - } - }); + pubKey = this._cryptoSuite.createKeyFromRaw(state.enrollment.identity.certificate); } else { - import_promise = this._cryptoSuite.importKey(state.enrollment.identity.certificate, opts); + pubKey = await this._cryptoSuite.importKey(state.enrollment.identity.certificate, opts); } - return import_promise - .then((key) => { - pubKey = key; - - const identity = new Identity(state.enrollment.identity.certificate, pubKey, self._mspId, this._cryptoSuite); - self._identity = identity; - - // during serialization (see toString() below) only the key's SKI are saved - // swap out that for the real key from the crypto provider - return self._cryptoSuite.getKey(state.enrollment.signingIdentity); - }).then((privateKey) => { - // the key retrieved from the key store using the SKI could be a public key - // or a private key, check to make sure it's a private key - if (privateKey.isPrivate()) { - self._signingIdentity = new SigningIdentity( - state.enrollment.identity.certificate, - pubKey, - self._mspId, - self._cryptoSuite, - new Signer(self._cryptoSuite, privateKey)); - - return self; - } else { - throw new Error(util.format('Private key missing from key store. Can not establish the signing identity for user %s', state.name)); - } - }); + const identity = new Identity(state.enrollment.identity.certificate, pubKey, this._mspId, this._cryptoSuite); + this._identity = identity; + + // during serialization (see toString() below) only the key's SKI are saved + // swap out that for the real key from the crypto provider + const privateKey = await this._cryptoSuite.getKey(state.enrollment.signingIdentity); + + // the key retrieved from the key store using the SKI could be a public key + // or a private key, check to make sure it's a private key + if (privateKey.isPrivate()) { + this._signingIdentity = new SigningIdentity( + state.enrollment.identity.certificate, + pubKey, + this._mspId, + this._cryptoSuite, + new Signer(this._cryptoSuite, privateKey)); + + return this; + } else { + throw new Error(util.format('Private key missing from key store. Can not establish the signing identity for user %s', state.name)); + } } /** diff --git a/fabric-client/lib/impl/CouchDBKeyValueStore.js b/fabric-client/lib/impl/CouchDBKeyValueStore.js index dccfbeb789..5310b0abdf 100644 --- a/fabric-client/lib/impl/CouchDBKeyValueStore.js +++ b/fabric-client/lib/impl/CouchDBKeyValueStore.js @@ -43,7 +43,6 @@ const CouchDBKeyValueStore = class extends KeyValueStore { // Create the keyValStore instance super(); - const self = this; // url is the database instance url this._url = options.url; // Name of the database, optional @@ -52,125 +51,90 @@ const CouchDBKeyValueStore = class extends KeyValueStore { } else { this._name = options.name; } + } - return new Promise(((resolve, reject) => { - // Initialize the CouchDB database client - const dbClient = nano(self._url); - // Check if the database already exists. If not, create it. - dbClient.db.get(self._name, (err) => { - // Check for error - if (err) { - // Database doesn't exist - if (err.error === 'not_found') { - logger.debug('No %s found, creating %s', self._name, self._name); - - dbClient.db.create(self._name, (error) => { - if (error) { - return reject(new Error(util.format('Failed to create %s database due to error: %s', self._name, error.stack ? error.stack : error))); - } - - logger.debug('Created %s database', self._name); - // Specify it as the database to use - self._database = dbClient.use(self._name); - resolve(self); - }); - } else { - // Other error - return reject(new Error(util.format('Error creating %s database to store membership data: %s', self._name, err.stack ? err.stack : err))); - } - } else { - // Database exists - logger.debug('%s already exists', self._name); + async initialize() { + + // Initialize the CouchDB database client + const dbClient = nano(this._url); + const get = util.promisify(dbClient.db.get); + try { + await get(this._name); + // Database exists + logger.debug('%s already exists', this._name); + // Specify it as the database to use + this._database = dbClient.use(this._name); + } catch (err) { + if (err.error === 'not_found') { + logger.debug('No %s found, creating %s', this._name); + const create = util.promisify(dbClient.db.create); + try { + await create(this._name); + logger.debug('Created %s database', this._name); // Specify it as the database to use - self._database = dbClient.use(self._name); - resolve(self); + this._database = dbClient.use(this._name); + } catch (error) { + throw new Error(util.format('Failed to create %s database due to error: %s', this._name, error.stack ? error.stack : error.description)); } - }); - })); + } else { + // Other error + throw new Error(util.format('Error initializing database to store membership data: %s', this._name, err.stack ? err.stack : err.description)); + } + } } - getValue(name) { + async getValue(name) { logger.debug('getValue', {key: name}); - const self = this; - return new Promise(((resolve, reject) => { - self._database.get(name, (err, body) => { - // Check for error on retrieving from database - if (err) { - if (err.error !== 'not_found') { - logger.error('getValue: %s, ERROR: [%s.get] - ', name, self._name, err.error); - return reject(err.error); - } else { - logger.debug('getValue: %s, Entry does not exist', name); - return resolve(null); - } - } else { - logger.debug('getValue: %s, Retrieved message from %s.', name, self._name); - return resolve(body.member); - } - }); - })); + const get = util.promisify(this._database.get); + + try { + const body = await get(name); + return body.member; + } catch (err) { + if (err.error !== 'not_found') { + logger.error('getValue: %s, ERROR: [%s.get] - ', name, this._name, err.error); + throw err; + } else { + logger.debug('getValue: %s, Entry does not exist', name); + return null; + } + } } - setValue(name, value) { + async setValue(name, value) { logger.debug('setValue', {key: name}); - const self = this; - - return new Promise(((resolve, reject) => { - // Attempt to retrieve from the database to see if the entry exists - self._database.get(name, (err, body) => { - // Check for error on retrieving from database - if (err) { - if (err.error !== 'not_found') { - logger.error('setValue: %s, ERROR: [%s.get] - ', name, self._name, err.error); - reject(err.error); - } else { - // Entry does not exist - logger.debug('setValue: %s, Entry does not exist, insert it.', name); - self._dbInsert({_id: name, member: value}) - .then((status) => { - logger.debug('setValue add: ' + name + ', status: ' + status); - if (status === true) { - resolve(value); - } else { - reject(new Error('Couch database insert add failed.')); - } - }); - } - } else { - // Entry already exists and must be updated - // Update the database entry using the latest rev number - logger.debug('setValue: %s, Retrieved entry from %s. Latest rev number: %s', name, self._name, body._rev); - - self._dbInsert({_id: name, _rev: body._rev, member: value}) - .then((status) => { - logger.debug('setValue update: ' + name + ', status: ' + status); - if (status === true) { - resolve(value); - } else { - reject(new Error('Couch database insert update failed.')); - } - }); - } - }); - })); - } + const insert = util.promisify(this._database.insert); + const get = util.promisify(this._database.get); + let isNew; + let body; + try { + // perform a get to see if the key exists + body = await get(name); + + // Didn't error, so it exists + isNew = false; + } catch (error) { + if (error.error !== 'not_found') { + logger.error('setValue: %s, key check ERROR: - ', name, error.error); + throw error; + } else { + // Does not exist + isNew = true; + } + } - _dbInsert(options) { - logger.debug('setValue, _dbInsert', {options: options}); - const self = this; - return new Promise(((resolve, reject) => { - self._database.insert(options, (err) => { - if (err) { - logger.error('setValue, _dbInsert, ERROR: [%s.insert] - ', self._name, err.error); - reject(new Error(err.error)); - } else { - logger.debug('setValue, _dbInsert, Inserted member into %s.', self._name); - resolve(true); - } - }); - })); + // conditionally perform the set/update + const opts = isNew ? {_id: name, member: value} : {_id: name, _rev: body._rev, member: value}; + try { + await insert(opts); + logger.debug('setValue [add]: ' + name + ', status: SUCCESS'); + return value; + } catch (err) { + logger.debug('Couch database insert: ' + name + ', status: FAILED'); + throw err; + } } }; diff --git a/fabric-client/lib/impl/CryptoKeyStore.js b/fabric-client/lib/impl/CryptoKeyStore.js index 5edd6429dc..208e9f4662 100644 --- a/fabric-client/lib/impl/CryptoKeyStore.js +++ b/fabric-client/lib/impl/CryptoKeyStore.js @@ -6,7 +6,6 @@ */ 'use strict'; - const jsrsasign = require('jsrsasign'); const KEYUTIL = jsrsasign.KEYUTIL; diff --git a/fabric-client/lib/impl/CryptoSuite_ECDSA_AES.js b/fabric-client/lib/impl/CryptoSuite_ECDSA_AES.js index b6045550a7..37b76ab440 100755 --- a/fabric-client/lib/impl/CryptoSuite_ECDSA_AES.js +++ b/fabric-client/lib/impl/CryptoSuite_ECDSA_AES.js @@ -96,23 +96,16 @@ class CryptoSuite_ECDSA_AES extends CryptoSuite { } async generateKey(opts) { - const pair = KEYUTIL.generateKeypair('EC', this._curveName); - - if (typeof opts !== 'undefined' && typeof opts.ephemeral !== 'undefined' && opts.ephemeral === true) { - logger.debug('generateKey, ephemeral true, Promise resolved'); - return new ECDSAKey(pair.prvKeyObj); - } else { - if (!this._cryptoKeyStore) { - throw new Error('generateKey opts.ephemeral is false, which requires CryptoKeyStore to be set.'); - } - // unless "opts.ephemeral" is explicitly set to "true", default to saving the key - const key = new ECDSAKey(pair.prvKeyObj); - - const store = await this._cryptoKeyStore._getKeyStore(); - logger.debug('generateKey, store.setValue'); - await store.putKey(key); - return key; + if (!this._cryptoKeyStore) { + throw new Error('generateKey requires CryptoKeyStore to be set.'); } + + const key = this.generateEphemeralKey(); + + const store = await this._cryptoKeyStore._getKeyStore(); + logger.debug('generateKey, store.setValue'); + await store.putKey(key); + return key; } /** @@ -124,24 +117,11 @@ class CryptoSuite_ECDSA_AES extends CryptoSuite { } /** - * This is an implementation of {@link module:api.CryptoSuite#importKey} + * This is an implementation of {@link module:api.CryptoSuite#createKeyFromRaw} */ - importKey(pem, opts) { - logger.debug('importKey - start'); - let store_key = true; // default - if (typeof opts !== 'undefined' && typeof opts.ephemeral !== 'undefined' && opts.ephemeral === true) { - store_key = false; - } - if (store_key && !this._cryptoKeyStore) { - throw new Error('importKey opts.ephemeral is false, which requires CryptoKeyStore to be set.'); - } + createKeyFromRaw(pem) { + logger.debug('createKeyFromRaw - start'); - const self = this; - // attempt to import the raw content, assuming it's one of the following: - // X.509v1/v3 PEM certificate (RSA/DSA/ECC) - // PKCS#8 PEM RSA/DSA/ECC public key - // PKCS#5 plain PEM DSA/RSA private key - // PKCS#8 plain PEM RSA/ECDSA private key // TODO: add support for the following passcode-protected PEM formats // - PKCS#5 encrypted PEM RSA/DSA private // - PKCS#8 encrypted PEM RSA/ECDSA private key @@ -149,43 +129,43 @@ class CryptoSuite_ECDSA_AES extends CryptoSuite { pemString = makeRealPem(pemString); let key = null; let theKey = null; - let error = null; + try { key = KEYUTIL.getKey(pemString); } catch (err) { - error = new Error('Failed to parse key from PEM: ' + err); + logger.error('createKeyFromRaw - Failed to parse key from PEM: ', err); + throw new Error('Failed to parse key from PEM: ' + err); } if (key && key.type && key.type === 'EC') { theKey = new ECDSAKey(key); - logger.debug('importKey - have the key %j', theKey); + logger.debug('createKeyFromRaw - have the key %j', theKey); + return theKey; } else { - error = new Error('Does not understand PEM contents other than ECDSA private keys and certificates'); + logger.error('createKeyFromRaw - Does not understand PEM contents other than ECDSA private keys and certificates'); + throw new Error('Does not understand PEM contents other than ECDSA private keys and certificates'); } + } - if (!store_key) { - if (error) { - logger.error('importKey - %s', error); - throw error; - } - return theKey; - } else { - if (error) { - logger.error('importKey - %j', error); - return Promise.reject(error); - } - return new Promise((resolve, reject) => { - return self._cryptoKeyStore._getKeyStore() - .then((store) => { - return store.putKey(theKey); - }).then(() => { - return resolve(theKey); - }).catch((err) => { - reject(err); - }); - - }); + /** + * This is an implementation of {@link module:api.CryptoSuite#importKey} + * Attempt to import the raw content, assuming it's one of the following: + * X.509v1/v3 PEM certificate (RSA/DSA/ECC) + * PKCS#8 PEM RSA/DSA/ECC public key + * PKCS#5 plain PEM DSA/RSA private key + * PKCS#8 plain PEM RSA/ECDSA private key + */ + async importKey(pem) { + + if (!this._cryptoKeyStore) { + throw new Error('importKey requires CryptoKeyStore to be set.'); } + + // Attempt Key creation from Raw input + const key = this.createKeyFromRaw(pem); + const store = await this._cryptoKeyStore._getKeyStore(); + await store.putKey(key); + return key; } async getKey(ski) { diff --git a/fabric-client/lib/impl/FileKeyValueStore.js b/fabric-client/lib/impl/FileKeyValueStore.js index 08724c875f..cd0edb1f30 100644 --- a/fabric-client/lib/impl/FileKeyValueStore.js +++ b/fabric-client/lib/impl/FileKeyValueStore.js @@ -39,54 +39,50 @@ const FileKeyValueStore = class extends KeyValueStore { // Create the keyValStore instance super(); - const self = this; this._dir = options.path; - return new Promise(((resolve, reject) => { - fs.mkdirs(self._dir, (err) => { - if (err) { - logger.error('constructor, error creating directory, code: %s', err.code); - return reject(err); - } - return resolve(self); - }); - })); } - getValue(name) { + async initialize() { + // Build directories from set path in constructor + try { + await fs.mkdirs(this._dir); + } catch (err) { + // Don't throw if it already exists + if (err.code !== 'EEXIST') { + logger.error('constructor, error creating directory, code: %s', err.code); + throw err; + } + } + } + + async getValue(name) { logger.debug('getValue', {key: name}); - const self = this; - - return new Promise(((resolve, reject) => { - const p = path.join(self._dir, name); - fs.readFile(p, 'utf8', (err, data) => { - if (err) { - if (err.code !== 'ENOENT') { - return reject(err); - } else { - return resolve(null); - } - } - return resolve(data); - }); - })); + try { + const p = path.join(this._dir, name); + return await fs.readFile(p, 'utf8'); + } catch (err) { + if (err.code !== 'ENOENT') { + // reject + throw err; + } else { + // resolve null + return null; + } + } } - setValue(name, value) { + async setValue(name, value) { logger.debug('setValue', {key: name}); - const self = this; - - return new Promise(((resolve, reject) => { - const p = path.join(self._dir, name); - fs.writeFile(p, value, (err) => { - if (err) { - reject(err); - } else { - return resolve(value); - } - }); - })); + try { + const p = path.join(this._dir, name); + return await fs.writeFile(p, value); + } catch (err) { + // rethrow + logger.error('setValue, error for key', name); + throw err; + } } }; diff --git a/fabric-client/lib/impl/bccsp_pkcs11.js b/fabric-client/lib/impl/bccsp_pkcs11.js index c36cea1778..d62e2555a4 100644 --- a/fabric-client/lib/impl/bccsp_pkcs11.js +++ b/fabric-client/lib/impl/bccsp_pkcs11.js @@ -21,7 +21,6 @@ const KEYUTIL = jsrsa.KEYUTIL; const BN = require('bn.js'); const ecsig = require('elliptic/lib/elliptic/ec/signature.js'); const callsite = require('callsite'); -// const crypto = require('crypto'); const pkcs11js = require('pkcs11js'); const util = require('util'); const ECDSAKey = require('./ecdsa/key.js'); @@ -229,10 +228,12 @@ class CryptoSuite_PKCS11 extends CryptoSuite { this._pkcs11 = _pkcs11; this._pkcs11OpenSession(this._pkcs11, pkcs11Lib, pkcs11Slot, pkcs11Pin, pkcs11UserType, pkcs11ReadWrite); + /* * SKI to key cache for getKey(ski) function. */ this._skiToKey = {}; + this.keyToSki = new Map(); } /** ******************************************************************************** @@ -794,70 +795,78 @@ class CryptoSuite_PKCS11 extends CryptoSuite { **********************************************************************************/ /** - * This is an implementation of {@link module:api.CryptoSuite#generateKey} - * Returns an instance of {@link module.api.Key} representing the private key, - * which also encapsulates the public key. By default the generated key (keypar) - * is (are) ephemeral unless opts.ephemeral is set to false, in which case the - * key (keypair) will be saved across PKCS11 sessions by the HSM hardware. - * + * This is an implementation of {@link module:api.CryptoSuite#generateEphemeralKey} * @returns {module:api.Key} Promise of an instance of {@link module:PKCS11_ECDSA_KEY} * containing the private key and the public key. */ - generateKey(opts) { + generateEphemeralKey(opts) { if (opts !== null && (typeof opts.algorithm === 'undefined' || opts.algorithm === null)) { opts.algorithm = 'ECDSA'; } if (typeof opts === 'undefined' || opts === null || typeof opts.algorithm === 'undefined' || opts.algorithm === null || typeof opts.algorithm !== 'string') { - return Promise.reject(Error(__func() + 'opts.algorithm must be String type')); + throw new Error(__func() + ' opts.algorithm must be String type'); } - const token = !opts.ephemeral; - const self = this; + switch (opts.algorithm.toUpperCase()) { + case 'AES': { + if (this._keySize !== 256) { + throw new Error(__func() + ' AES key size must be 256 (bits)'); + } + + const attributes = this._pkcs11GenerateKey(this._pkcs11, this._pkcs11Session, !!opts.persist); + // generateKey will need to know the ski, so set in a map here for use later + this.keyToSki.set(key, attributes.ski); + const key = new aesKey(attributes, this._keySize); + return key; + } + case 'ECDSA': { + const attributes = this._pkcs11GenerateECKeyPair(this._pkcs11, this._pkcs11Session, !!opts.persist); + // generateKey will need to know the ski, so set in a map here for use later + this.keyToSki.set(key, attributes.ski); + const key = new ecdsaKey(attributes, this._keySize); + key._cryptoSuite = this; + return key; + } + default: + throw new Error(__func() + ' must specify AES or ECDSA key algorithm'); + } + } + + /** + * This is an implementation of {@link module:api.CryptoSuite#generateKey} + * Returns an instance of {@link module.api.Key} representing the private key, + * which also encapsulates the public key. The key (keypair) will be saved + * across PKCS11 sessions by the HSM hardware. Use generateEphemeralKey to + * retrieve an ephmeral key. + * + * @returns {module:api.Key} Promise of an instance of {@link module:PKCS11_ECDSA_KEY} + * containing the private key and the public key. + */ + generateKey(opts) { + + // Use internal method to get key from passed options (if any) + if (!opts) { + opts = {}; + } + opts.persist = true; + const key = this.generateEphemeralKey(opts); + + // Set array + const ski = this.keyToSki.get(key); switch (opts.algorithm.toUpperCase()) { case 'AES': - return new Promise(((resolve, reject) => { - try { - if (self._keySize !== 256) { - throw new Error( - __func() + 'AES key size must be 256 (bits)'); - } - - const attr = self._pkcs11GenerateKey( - self._pkcs11, self._pkcs11Session, token); - /* - * Put key in the session cache and return - * promise of the key. - */ - const key = new aesKey(attr, self._keySize); - self._skiToKey[attr.ski.toString('hex')] = key; - return resolve(key); - } catch (e) { - return reject(e); - } - })); + this._skiToKey[ski.toString('hex')] = key; + return key; case 'ECDSA': - return new Promise(((resolve, reject) => { - try { - const attr = self._pkcs11GenerateECKeyPair( - self._pkcs11, self._pkcs11Session, token); - /* - * Put key in the session cache and return - * promise of the key. - */ - const key = new ecdsaKey(attr, self._keySize); - self._skiToKey[attr.ski.toString('hex')] = key; - key._cryptoSuite = self; - return resolve(key); - } catch (e) { - return reject(e); - } - })); + delete key._cryptoSuite; + this._skiToKey[ski.toString('hex')] = key; + key._cryptoSuite = this; + return key; default: - return Promise.reject(Error( - __func() + 'must specify AES or ECDSA key algorithm')); + throw new Error(__func() + ' must specify AES or ECDSA key algorithm'); } } @@ -867,7 +876,7 @@ class CryptoSuite_PKCS11 extends CryptoSuite { */ getKey(ski) { if (!ski || !(ski instanceof Buffer || typeof ski === 'string')) { - return Promise.reject(Error(__func() + 'ski must be Buffer|string type')); + return Promise.reject(Error(__func() + ' ski must be Buffer|string type')); } /* * Found the ski in the session key cache. @@ -893,7 +902,7 @@ class CryptoSuite_PKCS11 extends CryptoSuite { let key; if (typeof handle.secretKey !== 'undefined') { if (self._keySize !== 256) { - throw new Error(__func() + 'key size mismatch, class: ' + self._keySize + ', ski: 256'); + throw new Error(__func() + ' key size mismatch, class: ' + self._keySize + ', ski: 256'); } key = new aesKey({ski, key: handle.secretKey}, self._keySize); } else { /* ECDSA key. */ @@ -906,7 +915,7 @@ class CryptoSuite_PKCS11 extends CryptoSuite { toUpperCase()]; if (keySize === undefined || keySize !== self._keySize) { - throw new Error(__func() + 'key size mismatch, class: ' + self._keySize + ', ski: ' + keySize); + throw new Error(__func() + ' key size mismatch, class: ' + self._keySize + ', ski: ' + keySize); } key = new ecdsaKey({ski, ecpt: attr.ecpt, pub: handle.publicKey, priv: handle.privateKey}, self._keySize); @@ -931,11 +940,11 @@ class CryptoSuite_PKCS11 extends CryptoSuite { sign(key, digest) { if (typeof key === 'undefined' || key === null || !(key instanceof ecdsaKey) || !key.isPrivate()) { - throw new Error(__func() + 'key must be PKCS11_ECDSA_KEY type private key'); + throw new Error(__func() + ' key must be PKCS11_ECDSA_KEY type private key'); } if (typeof digest === 'undefined' || digest === null || !(digest instanceof Buffer)) { - throw new Error(__func() + 'digest must be Buffer type'); + throw new Error(__func() + ' digest must be Buffer type'); } return this._pkcs11Sign(this._pkcs11, this._pkcs11Session, key, digest); @@ -948,15 +957,15 @@ class CryptoSuite_PKCS11 extends CryptoSuite { verify(key, signature, digest) { if (typeof key === 'undefined' || key === null || !(key instanceof ecdsaKey || key instanceof ECDSAKey)) { - throw new Error(__func() + 'key must be PKCS11_ECDSA_KEY type or ECDSA_KEY type'); + throw new Error(__func() + ' key must be PKCS11_ECDSA_KEY type or ECDSA_KEY type'); } if (typeof signature === 'undefined' || signature === null || !(signature instanceof Buffer)) { - throw new Error(__func() + 'signature must be Buffer type'); + throw new Error(__func() + ' signature must be Buffer type'); } if (typeof digest === 'undefined' || digest === null || !(digest instanceof Buffer)) { - throw new Error(__func() + 'digest must be Buffer type'); + throw new Error(__func() + ' digest must be Buffer type'); } if (key instanceof ECDSAKey) { @@ -976,11 +985,11 @@ class CryptoSuite_PKCS11 extends CryptoSuite { */ encrypt(key, plainText, opts) { if (typeof key === 'undefined' || key === null || !(key instanceof aesKey)) { - throw new Error(__func() + 'key must be PKCS11_AES_KEY type'); + throw new Error(__func() + ' key must be PKCS11_AES_KEY type'); } if (typeof plainText === 'undefined' || plainText === null || !(plainText instanceof Buffer)) { - throw new Error(__func() + 'plainText must be Buffer type'); + throw new Error(__func() + ' plainText must be Buffer type'); } return this._pkcs11Encrypt(this._pkcs11, this._pkcs11Session, key, plainText); @@ -993,11 +1002,11 @@ class CryptoSuite_PKCS11 extends CryptoSuite { */ decrypt(key, cipherText, opts) { if (typeof key === 'undefined' || key === null || !(key instanceof aesKey)) { - throw new Error(__func() + 'key must be PKCS11_AES_KEY type'); + throw new Error(__func() + ' key must be PKCS11_AES_KEY type'); } if (typeof cipherText === 'undefined' || cipherText === null || !(cipherText instanceof Buffer)) { - throw new Error(__func() + 'cipherText must be Buffer type'); + throw new Error(__func() + ' cipherText must be Buffer type'); } return this._pkcs11Decrypt(this._pkcs11, this._pkcs11Session, key, cipherText); @@ -1007,57 +1016,63 @@ class CryptoSuite_PKCS11 extends CryptoSuite { * This is an implementation of {@link module:api.CryptoSuite#deriveKey} */ deriveKey(key, opts) { - throw new Error(__func() + 'not yet supported'); + throw new Error(__func() + ' not yet supported'); + } + + /** + * This is an implementation of {@link module:api.CryptoSuite#createKeyFromRaw} + */ + createKeyFromRaw(pem, opts) { + const optsLocal = opts ? opts : {}; + const token = !optsLocal.ephemeral; + switch (optsLocal.algorithm.toUpperCase()) { + case 'X509CERTIFICATE': + return new ECDSAKey(KEYUTIL.getKey(pem)); + case 'AES': + if (pem.length !== (256 / 8)) { + throw new Error(__func() + 'AES key size must be 256 (bits)'); + } else { + const attributes = this._pkcs11CreateObject(this._pkcs11, this._pkcs11Session, pem, token); + const key = new aesKey(attributes, pem.length * 8); + this.keyToSki.set(key, attributes.ski); + return key; + } + case 'ECDSA': + throw new Error(__func() + ' ECDSA key not yet supported'); + default: + throw new Error(__func() + ' only AES or ECDSA key supported'); + } } /** * This is an implementation of {@link module:api.CryptoSuite#importKey} */ - importKey(pem, opts) { + async importKey(pem, opts) { const optsLocal = opts ? opts : {}; const algorithm = optsLocal.algorithm ? optsLocal.algorithm : 'X509Certificate'; if (!pem || !(pem instanceof Buffer || typeof pem === 'string')) { - return Promise.reject(Error(__func() + 'pem must be Buffer type or String type')); + throw new Error(__func() + ' pem must be Buffer type or String type'); } if (typeof algorithm !== 'string') { - return Promise.reject(Error(__func() + 'opts.algorithm must be String type')); + throw new Error(__func() + ' opts.algorithm must be String type'); } - const token = !optsLocal.ephemeral; - const self = this; + // Create key + const key = this.createKeyFromRaw(pem, optsLocal); switch (algorithm.toUpperCase()) { case 'X509CERTIFICATE': - if (token) { - return Promise.resolve(new ECDSAKey(KEYUTIL.getKey(pem))); - } else { - return new ECDSAKey(KEYUTIL.getKey(pem)); - } - case 'AES': - return new Promise(((resolve, reject) => { - try { - if (pem.length !== (256 / 8)) { - throw new Error(__func() + 'AES key size must be 256 (bits)'); - } - - const attr = self._pkcs11CreateObject(self._pkcs11, self._pkcs11Session, pem, token); - /* - * Put key in the session cache and return - * promise of the key. - */ - const key = new aesKey(attr, pem.length * 8); - self._skiToKey[attr.ski.toString('hex')] = key; - return resolve(key); - } catch (e) { - reject(e); - } - })); - case 'ECDSA': - return Promise.reject(Error(__func() + 'ECDSA key not yet supported')); + return key; + case 'AES': { + // Store in array cache + const ski = this.keyToSki.get(key); + this._skiToKey[ski.toString('hex')] = key; + return key; + } default: - return Promise.reject(Error(__func() + 'only AES or ECDSA key supported')); + throw new Error(__func() + ' only X509 or AES key supported'); } } diff --git a/fabric-client/lib/msp/msp.js b/fabric-client/lib/msp/msp.js index e8eaf3a841..760c8c8b0b 100755 --- a/fabric-client/lib/msp/msp.js +++ b/fabric-client/lib/msp/msp.js @@ -141,7 +141,7 @@ const MSP = class { * @returns {Promise} Promise for an {@link Identity} instance or * or the Identity object itself if "storeKey" argument is false */ - deserializeIdentity(serializedIdentity, storeKey) { + async deserializeIdentity(serializedIdentity, storeKey) { logger.debug('importKey - start'); let store_key = true; // default // if storing is not required and therefore a promise will not be returned @@ -152,15 +152,14 @@ const MSP = class { const sid = fabprotos.msp.SerializedIdentity.decode(serializedIdentity); const cert = sid.getIdBytes().toBinary(); logger.debug('Encoded cert from deserialized identity: %s', cert); + + let publicKey; if (!store_key) { - const publicKey = this.cryptoSuite.importKey(cert, {algorithm: CryptoAlgorithms.X509Certificate, ephemeral: true}); - return new Identity(cert, publicKey, this.getId(), this.cryptoSuite); + publicKey = this.cryptoSuite.createKeyFromRaw(cert, {algorithm: CryptoAlgorithms.X509Certificate}); } else { - return this.cryptoSuite.importKey(cert, {algorithm: CryptoAlgorithms.X509Certificate}) - .then((publicKey) => { - return new Identity(cert, publicKey, this.getId(), this.cryptoSuite); - }); + publicKey = await this.cryptoSuite.importKey(cert, {algorithm: CryptoAlgorithms.X509Certificate}); } + return new Identity(cert, publicKey, this.getId(), this.cryptoSuite); } /** diff --git a/fabric-client/lib/utils.js b/fabric-client/lib/utils.js index 89fce15ef3..071f14fe9b 100644 --- a/fabric-client/lib/utils.js +++ b/fabric-client/lib/utils.js @@ -82,12 +82,14 @@ module.exports.newCryptoSuite = (setting) => { return new cryptoSuite(keysize, hashAlgo, opts); }; -// Provide a Promise-based keyValueStore for couchdb, etc. +// Provide a keyValueStore for couchdb, etc. module.exports.newKeyValueStore = async (options) => { // initialize the correct KeyValueStore const kvsEnv = exports.getConfigSetting('key-value-store'); - const store = require(kvsEnv); - return new store(options); + const Store = require(kvsEnv); + const store = new Store(options); + await store.initialize(); + return store; }; const LOGGING_LEVELS = ['debug', 'info', 'warn', 'error']; @@ -391,26 +393,23 @@ const CryptoKeyStore = function (KVSImplClass, opts) { }; - this._getKeyStore = function () { - const CKS = require('./impl/CryptoKeyStore.js'); - - const self = this; - return new Promise((resolve, reject) => { - if (self._store === null) { - self.logger.debug(util.format('This class requires a CryptoKeyStore to save keys, using the store: %j', self._storeConfig)); - - CKS(self._storeConfig.superClass, self._storeConfig.opts).then((ks) => { - self.logger.debug('_getKeyStore returning ks'); - self._store = ks; - return resolve(self._store); - }).catch((err) => { - reject(err); - }); - } else { - self.logger.debug('_getKeyStore resolving store'); - return resolve(self._store); + this._getKeyStore = async function () { + const CKS = require('fabric-client/lib/impl/CryptoKeyStore'); + + if (this._store === null) { + this.logger.debug(util.format('This class requires a CryptoKeyStore to save keys, using the store: %j', this._storeConfig)); + + try { + this._store = await CKS(this._storeConfig.superClass, this._storeConfig.opts); + await this._store.initialize(); + return this._store; + } catch (err) { + throw err; } - }); + } else { + this.logger.debug('_getKeyStore returning store'); + return this._store; + } }; }; diff --git a/fabric-client/package.json b/fabric-client/package.json index 46989edddb..8de5b3d54d 100644 --- a/fabric-client/package.json +++ b/fabric-client/package.json @@ -40,7 +40,7 @@ "nano": "^6.4.4", "nconf": "^0.10.0", "pkcs11js": "^1.0.6", - "promise-settle": "^0.3.0", + "promise-settle": "^0.3.0", "sjcl": "1.0.7", "stream-buffers": "3.0.1", "tar-stream": "1.6.1", @@ -48,11 +48,12 @@ "winston": "^2.2.0" }, "devDependencies": { - "chai": "^4.1.2", - "chai-as-promised": "^7.1.1", - "mocha": "^5.2.0", - "nyc": "^12.0.2", - "rewire": "^4.0.1", + "chai": "^4.1.2", + "chai-as-promised": "^7.1.1", + "mocha": "^5.2.0", + "mock-couch": "^0.1.11", + "nyc": "^12.0.2", + "rewire": "^4.0.1", "sinon": "^6.1.3" }, "license": "Apache-2.0", diff --git a/fabric-client/test/BaseClient.js b/fabric-client/test/BaseClient.js index 9d840c9dac..d6ddad4a47 100644 --- a/fabric-client/test/BaseClient.js +++ b/fabric-client/test/BaseClient.js @@ -84,13 +84,13 @@ describe('BaseClient', () => { sandbox.restore(); }); - it('should call `sdkUtils.newKeyValueStore` with passed parameters and return result', () => { + it('should call `sdkUtils.newKeyValueStore` with passed parameters and return result', async () => { const sdkUtilsStub = sandbox.stub(); - const newDefaultKeyValueStoreStub = sandbox.stub().returns('newDefaultKeyValueStore'); + const newDefaultKeyValueStoreStub = sandbox.stub().resolves('newDefaultKeyValueStore'); sdkUtilsStub.newKeyValueStore = newDefaultKeyValueStoreStub; BaseClientRewire.__set__('sdkUtils', sdkUtilsStub); - const result = BaseClientRewire.newDefaultKeyValueStore('setting'); + const result = await BaseClientRewire.newDefaultKeyValueStore('setting'); result.should.equal('newDefaultKeyValueStore'); sinon.assert.calledOnce(newDefaultKeyValueStoreStub); diff --git a/fabric-client/test/Channel.js b/fabric-client/test/Channel.js index 8cae994594..3e816d75cd 100644 --- a/fabric-client/test/Channel.js +++ b/fabric-client/test/Channel.js @@ -357,10 +357,10 @@ describe('Channel', () => { expect(org2PeerNames, 'org2').to.deep.equal([peer2.getName()]); }); - it('uses org from client if none supplied', () => { + it('uses org from client if none supplied', async () => { const org1 = 'org1'; const org2 = 'org2'; - client.loadFromConfig({ + await client.loadFromConfig({ version: '1.0', client: { organization: 'Org1' @@ -528,10 +528,10 @@ describe('Channel', () => { assertChannelEventHubsMatchPeers(eventHubs, [peer1]); }); - it('returns channel event hubs for channel\'s orgnanization if no organization specified', () => { + it('returns channel event hubs for channel\'s orgnanization if no organization specified', async () => { const org1 = 'org1'; const org2 = 'org2'; - client.loadFromConfig({ + await client.loadFromConfig({ version: '1.0', client: { organization: 'Org1' @@ -743,70 +743,66 @@ describe('Channel', () => { }); describe('#verifyProposalResponse', () => { - it('throws if proposal_response is missing', () => { - expect(() => channel.verifyProposalResponse(null)).to.throw('Missing proposal response'); + it('throws if proposal_response is missing', async () => { + await channel.verifyProposalResponse(null).should.be.rejectedWith('Missing proposal response'); }); - it('throws if parameter is not a ProposalResponse', () => { - expect(() => channel.verifyProposalResponse({})).to.throw('ProposalResponse'); + it('throws if parameter is not a ProposalResponse', async () => { + await channel.verifyProposalResponse({}).should.be.rejectedWith('ProposalResponse'); }); - it('throws if parameter is not a ProposalResponse', () => { - expect(() => channel.verifyProposalResponse([])).to.throw('ProposalResponse'); + it('throws if parameter is not a ProposalResponse', async () => { + await channel.verifyProposalResponse([]).should.be.rejectedWith('ProposalResponse'); }); - it('throws for unknown MSP ID in proposal response', () => { + it('throws for unknown MSP ID in proposal response', async () => { channel.getMSPManager().getMSP.withArgs(mspId).returns(null); const proposalResponse = createProposalResponse('messsage'); - expect(() => channel.verifyProposalResponse(proposalResponse)).to.throw(mspId); + await channel.verifyProposalResponse(proposalResponse).should.be.rejectedWith(mspId); }); - it('returns false if MSP unable to deserialize identity', () => { + it('returns false if MSP unable to deserialize identity', async () => { stubMsp.deserializeIdentity.returns(null); const proposalResponse = createProposalResponse('messsage'); - const result = channel.verifyProposalResponse(proposalResponse); - + const result = await channel.verifyProposalResponse(proposalResponse); expect(result).to.be.false; }); - it('returns false if identity not valid', () => { + it('returns false if identity not valid', async () => { const proposalResponse = createProposalResponse('messsage'); stubMspIdentity.isValid.returns(false); - const result = channel.verifyProposalResponse(proposalResponse); - + const result = await channel.verifyProposalResponse(proposalResponse); expect(result).to.be.false; }); - it('returns false if signature not valid', () => { + it('returns false if signature not valid', async () => { const proposalResponse = createProposalResponse('messsage'); stubMspIdentity.verify.returns(false); - const result = channel.verifyProposalResponse(proposalResponse); - + const result = await channel.verifyProposalResponse(proposalResponse); expect(result).to.be.false; }); - it('returns false if signature verification errors', () => { + it('returns false if signature verification errors', async () => { const proposalResponse = createProposalResponse('messsage'); stubMspIdentity.verify.throws('VerifyError', 'test'); - const result = channel.verifyProposalResponse(proposalResponse); - + const result = await channel.verifyProposalResponse(proposalResponse); expect(result).to.be.false; }); - it('returns true for valid proposal response', () => { + it('returns true for valid proposal response', async () => { const proposalResponse = createProposalResponse('messsage'); - const result = channel.verifyProposalResponse(proposalResponse); + const result = await channel.verifyProposalResponse(proposalResponse); expect(result).to.be.true; }); - it('returns false if the proposal response is an error', () => { + it('returns false if the proposal response is an error', async () => { const proposalResponse = new Error('sadface'); - const result = channel.verifyProposalResponse(proposalResponse); + const result = await channel.verifyProposalResponse(proposalResponse); expect(result).to.be.false; }); }); diff --git a/fabric-client/test/Client.js b/fabric-client/test/Client.js index 307bf074c1..962fafd349 100644 --- a/fabric-client/test/Client.js +++ b/fabric-client/test/Client.js @@ -107,10 +107,10 @@ describe('Client', () => { }); describe('loadFromConfig', () => { - it('should create a Client instance and call loadFromConfig', () => { + it('should create a Client instance and call loadFromConfig', async () => { const loadConfigStub = sinon.stub(); revert.push(Client.__set__('Client.prototype.loadFromConfig', loadConfigStub)); - const client = Client.loadFromConfig('config'); + const client = await Client.loadFromConfig('config'); sinon.assert.calledWith(loadConfigStub, 'config'); client.should.be.an.instanceof(Client); }); @@ -137,27 +137,27 @@ describe('Client', () => { _addConnectionOptionsFromConfig = sinon.stub(client, '_addConnectionOptionsFromConfig'); }); - it('should get additional network config and set _network_config to it', () => { + it('should get additional network config and set _network_config to it', async () => { mock_network_config.hasClient.returns(false); client._network_config = null; - client.loadFromConfig('config'); + await client.loadFromConfig('config'); sinon.assert.calledWith(_getNetworkConfigStub, 'config', client); sinon.assert.called(mock_network_config.hasClient); }); - it('should get additional network config and merge it with the existing config', () => { + it('should get additional network config and merge it with the existing config', async () => { mock_network_config.hasClient.returns(false); client._network_config = mock_network_config; - client.loadFromConfig('config'); + await client.loadFromConfig('config'); sinon.assert.calledWith(_getNetworkConfigStub, 'config', client); sinon.assert.calledWith(mock_network_config.mergeSettings, mock_network_config); sinon.assert.called(mock_network_config.hasClient); }); - it('should get additional network config and set adming and set mspid', () => { + it('should get additional network config and set adming and set mspid', async () => { mock_network_config.hasClient.returns(true); client._network_config = null; - client.loadFromConfig('config'); + await client.loadFromConfig('config'); sinon.assert.calledWith(_getNetworkConfigStub, 'config', client); sinon.assert.called(mock_network_config.hasClient); sinon.assert.called(_setAdminFromConfigStub); @@ -463,11 +463,11 @@ describe('Client', () => { }); describe('#getPeersForOrg', () => { - it('returns peers for specified org', () => { + it('returns peers for specified org', async () => { const clientOrg = connectionProfile.client.organization; const mspId = connectionProfile.organizations[clientOrg].mspid; const orgPeerNames = connectionProfile.organizations[clientOrg].peers; - const client = Client.loadFromConfig(connectionProfile); + const client = await Client.loadFromConfig(connectionProfile); const peers = client.getPeersForOrg(mspId); @@ -475,10 +475,10 @@ describe('Client', () => { peerNames.should.deep.equal(orgPeerNames); }); - it('returns peers for client org in connection profile if no org specified', () => { + it('returns peers for client org in connection profile if no org specified', async () => { const clientOrg = connectionProfile.client.organization; const orgPeerNames = connectionProfile.organizations[clientOrg].peers; - const client = Client.loadFromConfig(connectionProfile); + const client = await Client.loadFromConfig(connectionProfile); const peers = client.getPeersForOrg(); @@ -492,17 +492,17 @@ describe('Client', () => { peers.should.be.empty; }); - it('returns empty list if organisation not in config', () => { - const client = Client.loadFromConfig(connectionProfile); + it('returns empty list if organisation not in config', async () => { + const client = await Client.loadFromConfig(connectionProfile); const peers = client.getPeersForOrg('NON_EXISTENT_MSP_ID'); peers.should.be.empty; }); - it('returns peers for user context MSP ID if no org specified and no client org in connection profile', () => { + it('returns peers for user context MSP ID if no org specified and no client org in connection profile', async () => { delete connectionProfile.client; const userOrg = Object.values(connectionProfile.organizations).find((org) => org.mspid === userMspId); const userPeerNames = userOrg.peers; - const client = Client.loadFromConfig(connectionProfile); + const client = await Client.loadFromConfig(connectionProfile); client.setUserContext(fakeUser, true); const peers = client.getPeersForOrg(); @@ -764,11 +764,11 @@ describe('Client', () => { should.not.exist(actual); }); - it('MSP ID of the org in the client section of the connection profile if loaded from config', () => { + it('MSP ID of the org in the client section of the connection profile if loaded from config', async () => { const clientOrg = connectionProfile.client.organization; const expected = connectionProfile.organizations[clientOrg].mspid; - const client = Client.loadFromConfig(connectionProfile); + const client = await Client.loadFromConfig(connectionProfile); const actual = client.getMspid(); actual.should.equal(expected); @@ -782,8 +782,8 @@ describe('Client', () => { actual.should.equal(userMspId); }); - it('MSP ID of the user context in preference to value from connection profile', () => { - const client = Client.loadFromConfig(connectionProfile); + it('MSP ID of the user context in preference to value from connection profile', async () => { + const client = await Client.loadFromConfig(connectionProfile); client.setUserContext(fakeUser, true); const actual = client.getMspid(); @@ -1817,11 +1817,15 @@ describe('Client', () => { let setCryptoKeyStoreStub; let newCryptoKeyStoreStub; let setCryptoSuiteStub; + let keyValStub; let client; beforeEach(() => { + keyValStub = { + init: sinon.stub() + }; getClientConfigStub = sinon.stub(); - newDefaultKeyValueStoreStub = sinon.stub().returns(Promise.resolve('key-val-store')); + newDefaultKeyValueStoreStub = sinon.stub().returns(keyValStub); revert.push(Client.__set__('BaseClient.newDefaultKeyValueStore', newDefaultKeyValueStoreStub)); setStateStoreStub = sinon.stub(); setCryptoKeyStoreStub = sinon.stub(); @@ -1856,20 +1860,18 @@ describe('Client', () => { } }); - it('should return true and set the cryptokeystore', async () => { + it('should set the cryptokeystore', async () => { getClientConfigStub.returns({credentialStore: {cryptoStore: 'store'}}); client._network_config = {getClientConfig: getClientConfigStub}; newCryptoKeyStoreStub.returns('new-crypto'); - const success = await client.initCredentialStores(); - success.should.be.true; + await client.initCredentialStores(); sinon.assert.called(getClientConfigStub); sinon.assert.calledWith(newDefaultKeyValueStoreStub, {cryptoStore: 'store'}); - sinon.assert.calledWith(setStateStoreStub, 'key-val-store'); + sinon.assert.calledWith(setStateStoreStub, keyValStub); sinon.assert.called(cryptoSuiteStub); sinon.assert.calledWith(setCryptoKeyStoreStub, 'new-crypto'); sinon.assert.calledWith(newCryptoKeyStoreStub, 'store'); sinon.assert.calledWith(setCryptoSuiteStub, {setCryptoKeyStore: setCryptoKeyStoreStub}); - }); }); @@ -1933,6 +1935,7 @@ describe('Client', () => { let getCryptoSuiteStub; let newCryptoSuiteStub; let importKeyStub; + let createKeyFromRawStub; let SigningIdentityStub; let SignerStub; @@ -1940,7 +1943,11 @@ describe('Client', () => { beforeEach(() => { getCryptoSuiteStub = sinon.stub(); importKeyStub = sinon.stub(); - newCryptoSuiteStub = sinon.stub().returns({importKey: importKeyStub}); + createKeyFromRawStub = sinon.stub(); + newCryptoSuiteStub = sinon.stub().returns({ + importKey: importKeyStub, + createKeyFromRaw: createKeyFromRawStub + }); revert.push(Client.__set__('BaseClient.newCryptoSuite', newCryptoSuiteStub)); SigningIdentityStub = sinon.stub(); revert.push(Client.__set__('SigningIdentity', SigningIdentityStub)); @@ -1951,79 +1958,68 @@ describe('Client', () => { client.getCryptoSuite = getCryptoSuiteStub; }); - it('should throw if no private key is given', () => { - (() => { - client.setAdminSigningIdentity(undefined, 'certificate', 'mspid'); - }).should.throw('Invalid parameter. Must have a valid private key.'); + it('should reject if no private key is given', async () => { + await client.setAdminSigningIdentity(undefined, 'certificate', 'mspid').should.be.rejectedWith('Invalid parameter. Must have a valid private key.'); }); - it('should throw if private key is null', () => { - (() => { - client.setAdminSigningIdentity(null, 'certificate', 'mspid'); - }).should.throw('Invalid parameter. Must have a valid private key.'); + it('should throw if private key is null', async () => { + await client.setAdminSigningIdentity(null, 'certificate', 'mspid').should.be.rejectedWith('Invalid parameter. Must have a valid private key.'); }); - it('should throw if private key is empty string', () => { - (() => { - client.setAdminSigningIdentity('', 'certificate', 'mspid'); - }).should.throw('Invalid parameter. Must have a valid private key.'); + it('should throw if private key is empty string', async () => { + await client.setAdminSigningIdentity('', 'certificate', 'mspid').should.be.rejectedWith('Invalid parameter. Must have a valid private key.'); }); - it('should throw if no certificate key is given', () => { - (() => { - client.setAdminSigningIdentity('private-key', undefined, 'mspid'); - }).should.throw('Invalid parameter. Must have a valid certificate.'); + it('should throw if no certificate key is given', async () => { + await client.setAdminSigningIdentity('private-key', undefined, 'mspid').should.be.rejectedWith('Invalid parameter. Must have a valid certificate.'); }); - it('should throw if certificate is null', () => { - (() => { - client.setAdminSigningIdentity('private-key', null, 'mspid'); - }).should.throw('Invalid parameter. Must have a valid certificate.'); + it('should throw if certificate is null', async () => { + await client.setAdminSigningIdentity('private-key', null, 'mspid').should.be.rejectedWith('Invalid parameter. Must have a valid certificate.'); }); - it('should throw if certificate is empty string', () => { - (() => { - client.setAdminSigningIdentity('private-key', '', 'mspid'); - }).should.throw('Invalid parameter. Must have a valid certificate.'); + it('should throw if certificate is empty string', async () => { + await client.setAdminSigningIdentity('private-key', '', 'mspid').should.be.rejectedWith('Invalid parameter. Must have a valid certificate.'); }); - it('should throw if no mspid is given', () => { - (() => { - client.setAdminSigningIdentity('private-key', 'certificate', undefined); - }).should.throw('Invalid parameter. Must have a valid mspid.'); + it('should throw if no mspid is given', async () => { + await client.setAdminSigningIdentity('private-key', 'certificate', undefined).should.be.rejectedWith('Invalid parameter. Must have a valid mspid.'); }); - it('should throw if certificate is null', () => { - (() => { - client.setAdminSigningIdentity('private-key', 'certificate', null); - }).should.throw('Invalid parameter. Must have a valid mspid.'); + it('should throw if certificate is null', async () => { + await client.setAdminSigningIdentity('private-key', 'certificate', null).should.be.rejectedWith('Invalid parameter. Must have a valid mspid.'); }); - it('should throw if certificate is empty string', () => { - (() => { - client.setAdminSigningIdentity('private-key', 'certificate', ''); - }).should.throw('Invalid parameter. Must have a valid mspid.'); + it('should throw if certificate is empty string', async () => { + await client.setAdminSigningIdentity('private-key', 'certificate', '').should.be.rejectedWith('Invalid parameter. Must have a valid mspid.'); }); - it('should retrieve CryptoSuite and import the public and private keys before creating an identity', () => { - getCryptoSuiteStub.returns({importKey: importKeyStub}); - importKeyStub.onCall(0).returns('private_key'); - importKeyStub.onCall(1).returns('public_key'); - client.setAdminSigningIdentity('private-key', 'certificate', 'mspid'); + it('should retrieve CryptoSuite and import the public and private keys before creating an identity', async () => { + getCryptoSuiteStub.returns({ + importKey: importKeyStub, + createKeyFromRaw: createKeyFromRawStub + }); + createKeyFromRawStub.onCall(0).returns('private_key'); + createKeyFromRawStub.onCall(1).returns('public_key'); + + await client.setAdminSigningIdentity('private-key', 'certificate', 'mspid'); + sinon.assert.called(getCryptoSuiteStub); - sinon.assert.calledWith(importKeyStub, 'private-key', {ephemeral: true}); - sinon.assert.calledWith(importKeyStub, 'certificate', {ephemeral: true}); + sinon.assert.calledWith(createKeyFromRawStub, 'private-key'); + sinon.assert.calledWith(createKeyFromRawStub, 'certificate'); sinon.assert.calledWith(SigningIdentityStub, 'certificate', 'public_key', 'mspid', getCryptoSuiteStub(), new SignerStub()); sinon.assert.calledWith(SignerStub, getCryptoSuiteStub(), 'private_key'); client._adminSigningIdentity.should.deep.equal(new SigningIdentityStub()); }); - it('should create a new CryptoSuite and import the public and private keys before creating an identity', () => { - importKeyStub.onCall(0).returns('private_key'); - importKeyStub.onCall(1).returns('public_key'); - client.setAdminSigningIdentity('private-key', 'certificate', 'mspid'); - sinon.assert.calledWith(importKeyStub, 'private-key', {ephemeral: true}); - sinon.assert.calledWith(importKeyStub, 'certificate', {ephemeral: true}); + it('should create a new CryptoSuite and import the public and private keys before creating an identity', async () => { + createKeyFromRawStub.onCall(0).returns('private_key'); + createKeyFromRawStub.onCall(1).returns('public_key'); + + await client.setAdminSigningIdentity('private-key', 'certificate', 'mspid'); + + sinon.assert.calledWith(createKeyFromRawStub, 'private-key'); + sinon.assert.calledWith(createKeyFromRawStub, 'certificate'); sinon.assert.calledWith(SigningIdentityStub, 'certificate', 'public_key', 'mspid', newCryptoSuiteStub(), new SignerStub()); sinon.assert.calledWith(SignerStub, newCryptoSuiteStub(), 'private_key'); client._adminSigningIdentity.should.deep.equal(new SigningIdentityStub()); @@ -2055,16 +2051,16 @@ describe('Client', () => { client.setAdminSigningIdentity = getAdminSigningIdentityStub; }); - it('should throw an error if no network config is present', () => { - (() => { - client._setAdminFromConfig(); - }).should.throw('No common connection profile has been loaded'); + it('should throw an error if no network config is present', async () => { + await client._setAdminFromConfig().should.be.rejectedWith('No common connection profile has been loaded'); }); - it('should not call anything if client config is null', () => { + it('should not call anything if client config is null', async () => { getClientConfigStub.returns(null); client._network_config = {getClientConfig: getClientConfigStub}; - client._setAdminFromConfig(); + + await client._setAdminFromConfig(); + sinon.assert.notCalled(getOrganizationStub); sinon.assert.notCalled(getMspidStub); sinon.assert.notCalled(getAdminPrivateKeyStub); @@ -2613,6 +2609,7 @@ describe('Client', () => { let readFileStub; let getCryptoSuiteStub; let importKeyStub; + let createKeyFromRawStub; let setCryptoSuiteStub; let setEnrollmentStub; let setUserContextStub; @@ -2629,7 +2626,8 @@ describe('Client', () => { FakeUser.prototype.setEnrollment = setEnrollmentStub; readFileStub = sinon.stub().returns(Promise.resolve(1)); getCryptoSuiteStub = sinon.stub(); - importKeyStub = sinon.stub(); + importKeyStub = sinon.stub().returns(Promise.resolve('imported-key')); + createKeyFromRawStub = sinon.stub().returns(Promise.resolve('created-key')); setUserContextStub = sinon.stub().returns(Promise.resolve()); setCryptoKeyStoreStub = sinon.stub(); @@ -2637,7 +2635,10 @@ describe('Client', () => { revert.push(Client.__set__('User', FakeUser)); client = new Client(); - client.getCryptoSuite = getCryptoSuiteStub.returns({importKey: importKeyStub.returns(Promise.resolve('imported-key'))}); + client.getCryptoSuite = getCryptoSuiteStub.returns({ + importKey: importKeyStub, + createKeyFromRaw: createKeyFromRawStub + }); client.setUserContext = setUserContextStub; }); @@ -2698,7 +2699,7 @@ describe('Client', () => { sinon.assert.calledWith(FakeLogger.debug, 'then signedCertPEM data'); sinon.assert.calledWith(setCryptoSuiteStub, getCryptoSuiteStub()); sinon.assert.called(getCryptoSuiteStub); - sinon.assert.calledWith(setEnrollmentStub, 'imported-key', 'privateKeyPEM', '1', true); + sinon.assert.calledWith(setEnrollmentStub, 'created-key', 'privateKeyPEM', '1', true); sinon.assert.calledWith(FakeLogger.debug, 'then setUserContext'); sinon.assert.calledWith(setUserContextStub, new FakeUser(), true); sinon.assert.calledWith(FakeLogger.debug, 'then user'); @@ -2716,7 +2717,7 @@ describe('Client', () => { sinon.assert.calledWith(FakeLogger.debug, 'then signedCertPEM data'); sinon.assert.calledWith(setCryptoSuiteStub, getCryptoSuiteStub()); sinon.assert.called(getCryptoSuiteStub); - sinon.assert.calledWith(setEnrollmentStub, 'imported-key', 'privateKeyPEM', '1', true); + sinon.assert.calledWith(setEnrollmentStub, 'created-key', 'privateKeyPEM', '1', true); sinon.assert.calledWith(FakeLogger.debug, 'then setUserContext'); sinon.assert.calledWith(setUserContextStub, new FakeUser(), true); sinon.assert.calledWith(FakeLogger.debug, 'then user'); @@ -2724,9 +2725,23 @@ describe('Client', () => { }); it('should return a user if getCryptoSuite does not return null', async () => { - getCryptoSuiteStub.returns({setCryptoKeyStore: setCryptoKeyStoreStub, importKey: importKeyStub, _cryptoKeyStore: {}}); + getCryptoSuiteStub.returns({ + setCryptoKeyStore: setCryptoKeyStoreStub, + createKeyFromRaw: createKeyFromRawStub, + importKey: importKeyStub, + _cryptoKeyStore: {} + }); readFileStub.returns(Promise.resolve('privateKeyPEM')); - const user = await client.createUser({username: 'name', mspid: '1', cryptoContent: {privateKey: 'private-key', signedCert: 'signed-cert', signedCertPEM: 'signed-cert-PEM'}, skipPersistence: true}); + const user = await client.createUser({ + username: 'name', + mspid: '1', + cryptoContent: { + privateKey: 'private-key', + signedCert: 'signed-cert', + signedCertPEM: 'signed-cert-PEM' + }, + skipPersistence: true + }); sinon.assert.calledWith(readFileStub, 'private-key'); sinon.assert.calledWith(FakeLogger.debug, 'then privateKeyPEM data'); sinon.assert.calledWith(readFileStub, 'signed-cert'); @@ -2734,7 +2749,7 @@ describe('Client', () => { sinon.assert.calledWith(setCryptoSuiteStub, getCryptoSuiteStub()); sinon.assert.called(getCryptoSuiteStub); sinon.assert.calledWith(FakeLogger.debug, 'cryptoSuite has a cryptoKeyStore'); - sinon.assert.calledWith(setEnrollmentStub, 'imported-key', 'privateKeyPEM', '1', true); + sinon.assert.calledWith(setEnrollmentStub, 'created-key', 'privateKeyPEM', '1', true); sinon.assert.calledWith(FakeLogger.debug, 'then setUserContext'); sinon.assert.calledWith(setUserContextStub, new FakeUser(), true); sinon.assert.calledWith(FakeLogger.debug, 'then user'); diff --git a/fabric-client/test/User.js b/fabric-client/test/User.js index b9fa8cfac8..c19dfe140d 100644 --- a/fabric-client/test/User.js +++ b/fabric-client/test/User.js @@ -189,7 +189,7 @@ describe('User', () => { const FakeIdentity = sandbox.stub(); const FakeSigningIdentity = sandbox.stub(); - const returnStub = sandbox.stub({setCryptoKeyStore: () => {}, importKey: () => {}}); + const returnStub = sandbox.stub({setCryptoKeyStore: () => {}, importKey: () => {}, createKeyFromRaw: () => {}}); const newCryptoSuiteStub = sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(returnStub); UserRewire.__set__('sdkUtils', FakeSdkUtils); @@ -216,6 +216,7 @@ describe('User', () => { const cryptoSuite = { setCryptoKeyStore: () => {}, importKey: () => {}, + createKeyFromRaw: () => {} }; const FakeSetCryptoKeyStore = sandbox.stub(cryptoSuite, 'setCryptoKeyStore'); @@ -281,9 +282,10 @@ describe('User', () => { const cryptoSuite = { setCryptoKeyStore: () => {}, importKey: () => {}, + createKeyFromRaw: () => {} }; - const FakeImportKey = sandbox.stub(cryptoSuite, 'importKey'); + const FakeCreateKey = sandbox.stub(cryptoSuite, 'createKeyFromRaw'); sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(cryptoSuite); sandbox.stub(FakeSdkUtils, 'newCryptoKeyStore').returns('test_cryptoKeyStore'); @@ -296,8 +298,8 @@ describe('User', () => { const obj = new UserRewire('my_cfg'); await obj.setEnrollment('test_privateKey', 'test_certificate', 'test_mspId', true); - sinon.assert.calledOnce(FakeImportKey); - sinon.assert.calledWith(FakeImportKey, 'test_certificate', {ephemeral: true}); + sinon.assert.calledOnce(FakeCreateKey); + sinon.assert.calledWith(FakeCreateKey, 'test_certificate'); }); it('should set the users identity', async() => { @@ -313,12 +315,14 @@ describe('User', () => { const FakeCryptoSuite = { setCryptoKeyStore: () => {}, importKey: () => {}, + createKeyFromRaw: () => {}, + _cryptoKeyStore: true }; sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(FakeCryptoSuite); sandbox.stub(FakeSdkUtils, 'newCryptoKeyStore').returns('test_cryptoKeyStore'); - sandbox.stub(FakeCryptoSuite, 'importKey').returns('test_key'); - + sandbox.stub(FakeCryptoSuite, 'importKey').returns('import_key'); + sandbox.stub(FakeCryptoSuite, 'createKeyFromRaw').returns('raw_key'); UserRewire.__set__('sdkUtils', FakeSdkUtils); UserRewire.__set__('Identity', FakeIdentity); @@ -328,7 +332,7 @@ describe('User', () => { await obj.setEnrollment('test_privateKey', 'test_certificate', 'test_mspId', false); sinon.assert.calledOnce(FakeIdentity); - sinon.assert.calledWith(FakeIdentity, 'test_certificate', 'test_key', 'test_mspId', FakeCryptoSuite); + sinon.assert.calledWith(FakeIdentity, 'test_certificate', 'import_key', 'test_mspId', FakeCryptoSuite); }); it('should set the users signingIdentity', async() => { @@ -344,11 +348,13 @@ describe('User', () => { const FakeCryptoSuite = { setCryptoKeyStore: () => {}, importKey: () => {}, + createKeyFromRaw: () => {} }; sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(FakeCryptoSuite); sandbox.stub(FakeSdkUtils, 'newCryptoKeyStore').returns('test_cryptoKeyStore'); - sandbox.stub(FakeCryptoSuite, 'importKey').returns('test_key'); + sandbox.stub(FakeCryptoSuite, 'importKey').returns('import_key'); + sandbox.stub(FakeCryptoSuite, 'createKeyFromRaw').returns('raw_key'); UserRewire.__set__('sdkUtils', FakeSdkUtils); @@ -359,7 +365,7 @@ describe('User', () => { await obj.setEnrollment('test_privateKey', 'test_certificate', 'test_mspId', false); sinon.assert.calledOnce(FakeSigningIdentity); - sinon.assert.calledWith(FakeIdentity, 'test_certificate', 'test_key', 'test_mspId', FakeCryptoSuite); + sinon.assert.calledWith(FakeIdentity, 'test_certificate', 'raw_key', 'test_mspId', FakeCryptoSuite); }); }); @@ -396,7 +402,7 @@ describe('User', () => { sandbox.restore(); }); - it('should log and set the users state, name, roles, afilliation and enrollmentSecret', () => { + it('should log and set the users state, name, roles, afilliation and enrollmentSecret', async () => { const FakeLogger = { debug: () => {} }; @@ -406,26 +412,31 @@ describe('User', () => { newCryptoKeyStore: () => {}, }; + const privateStub = { + isPrivate: () => { + return true; + } + }; + const FakeCryptoSuite = { setCryptoKeyStore: () => {}, - importKey: () => {}, - getKey: () => {} + importKey: () => sinon.stub().resolves(), + getKey: sinon.stub().returns(privateStub), + createKeyFromRaw: () => {} }; const debugStub = sandbox.stub(FakeLogger, 'debug'); const FakeIdentity = sandbox.stub(); - const promise = new Promise(() => {}); sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(FakeCryptoSuite); sandbox.stub(FakeSdkUtils, 'newCryptoKeyStore').returns('test_cryptoKeyStore'); - sandbox.stub(FakeCryptoSuite, 'importKey').returns(promise); UserRewire.__set__('logger', FakeLogger); UserRewire.__set__('sdkUtils', FakeSdkUtils); UserRewire.__set__('Identity', FakeIdentity); const obj = new UserRewire('cfg'); - obj.fromString('{ "name":"cfg", "roles":"test_role", "affiliation":"test_affiliation", "enrollmentSecret":"test_enrollmentSecret", "mspid":"test_mspId", "enrollment":{"identity":{"certificate":"test_certificate"}}}', false); + await obj.fromString('{ "name":"cfg", "roles":"test_role", "affiliation":"test_affiliation", "enrollmentSecret":"test_enrollmentSecret", "mspid":"test_mspId", "enrollment":{"identity":{"certificate":"test_certificate"}}}', false); sinon.assert.calledOnce(debugStub); sinon.assert.calledWith(debugStub, 'fromString --start'); @@ -435,70 +446,33 @@ describe('User', () => { obj._enrollmentSecret.should.equal('test_enrollmentSecret'); }); - it('should throw error if state name is not the same as user name', () => { - (() => { - const FakeLogger = { - debug: () => {} - }; - - sandbox.stub(FakeLogger, 'debug'); - - UserRewire.__set__('logger', FakeLogger); - - const obj = new UserRewire('cfg'); - obj.fromString('{ "name":"wrong_name", "roles":"test_role", "affiliation":"test_affiliation", "enrollmentSecret":"test_enrollmentSecret", "mspid":"test_mspid", "enrollment":{"identity":{"certificate":"test_certificate"}}}', false); - }).should.throw(/name mismatch: 'wrong_name' does not equal 'cfg'/); - }); - - it('should throw error if the state has no mspid', () => { - (() => { - const FakeLogger = { - debug: () => {} - }; + it('should throw error if state name is not the same as user name', async () => { + const FakeLogger = { + debug: () => {} + }; - sandbox.stub(FakeLogger, 'debug'); + sandbox.stub(FakeLogger, 'debug'); - UserRewire.__set__('logger', FakeLogger); + UserRewire.__set__('logger', FakeLogger); - const obj = new UserRewire('cfg'); - obj.fromString('{ "name":"cfg", "roles":"test_role", "affiliation":"test_affiliation", "enrollmentSecret":"test_enrollmentSecret", "mspid":"", "enrollment":{"identity":{"certificate":"test_certificate"}}}', false); - }).should.throw(/Failed to find "mspid" in the deserialized state object for the user. Likely due to an outdated state store./); + const obj = new UserRewire('cfg'); + await obj.fromString('{ "name":"wrong_name", "roles":"test_role", "affiliation":"test_affiliation", "enrollmentSecret":"test_enrollmentSecret", "mspid":"test_mspid", "enrollment":{"identity":{"certificate":"test_certificate"}}}', false).should.be.rejectedWith(/name mismatch: 'wrong_name' does not equal 'cfg'/); }); - it('should set the users mspid to the state mspid', () => { + it('should throw error if the state has no mspid', async () => { const FakeLogger = { debug: () => {} }; - const FakeSdkUtils = { - newCryptoSuite: () => {}, - newCryptoKeyStore: () => {}, - }; - - const FakeCryptoSuite = { - setCryptoKeyStore: () => {}, - importKey: () => {}, - getKey: () => {} - }; - - const FakeIdentity = sandbox.stub(); - const promise = new Promise(() => {}); - - sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(FakeCryptoSuite); - sandbox.stub(FakeSdkUtils, 'newCryptoKeyStore').returns('test_cryptoKeyStore'); - sandbox.stub(FakeCryptoSuite, 'importKey').returns(promise); + sandbox.stub(FakeLogger, 'debug'); UserRewire.__set__('logger', FakeLogger); - UserRewire.__set__('sdkUtils', FakeSdkUtils); - UserRewire.__set__('Identity', FakeIdentity); const obj = new UserRewire('cfg'); - obj.fromString('{ "name":"cfg", "roles":"test_role", "affiliation":"test_affiliation", "enrollmentSecret":"test_enrollmentSecret", "mspid":"test_mspId", "enrollment":{"identity":{"certificate":"test_certificate"}}}', true); - - obj._mspId.should.equal('test_mspId'); + await obj.fromString('{ "name":"cfg", "roles":"test_role", "affiliation":"test_affiliation", "enrollmentSecret":"test_enrollmentSecret", "mspid":"", "enrollment":{"identity":{"certificate":"test_certificate"}}}', false).should.be.rejectedWith(/Failed to find "mspid" in the deserialized state object for the user. Likely due to an outdated state store./); }); - it('should set import_promise if no_save is true', async () => { + it('should set the users mspid to the state mspid', async () => { const FakeLogger = { debug: () => {} }; @@ -508,41 +482,39 @@ describe('User', () => { newCryptoKeyStore: () => {}, }; + const privateStub = { + isPrivate: () => { + return true; + } + }; + const FakeCryptoSuite = { setCryptoKeyStore: () => {}, importKey: () => {}, - getKey: () => {} - }; - - const FakeCryptoAlgorithms = { - X509Certificate: 'X509Certificate', + getKey: sinon.stub().returns(privateStub), + createKeyFromRaw: () => {} }; const FakeIdentity = sandbox.stub(); - const FakeImportKey = sandbox.stub(FakeCryptoSuite, 'importKey').returns('key'); - const promise = new Promise((resolve) => { - resolve({isPrivate() { - return true; - }}); - }); + const promise = new Promise(() => {}); sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(FakeCryptoSuite); sandbox.stub(FakeSdkUtils, 'newCryptoKeyStore').returns('test_cryptoKeyStore'); - sandbox.stub(FakeCryptoSuite, 'getKey').returns(promise); + sandbox.stub(FakeCryptoSuite, 'importKey').returns(promise); + sandbox.stub(FakeCryptoSuite, 'createKeyFromRaw').returns('create_pem'); UserRewire.__set__('logger', FakeLogger); UserRewire.__set__('sdkUtils', FakeSdkUtils); UserRewire.__set__('Identity', FakeIdentity); - UserRewire.__set__('CryptoAlgorithms', FakeCryptoAlgorithms); const obj = new UserRewire('cfg'); await obj.fromString('{ "name":"cfg", "roles":"test_role", "affiliation":"test_affiliation", "enrollmentSecret":"test_enrollmentSecret", "mspid":"test_mspId", "enrollment":{"identity":{"certificate":"test_certificate"}}}', true); - sinon.assert.calledOnce(FakeImportKey); - sinon.assert.calledWith(FakeImportKey, 'test_certificate', {algorithm: 'X509Certificate', ephemeral: true}); + obj._mspId.should.equal('test_mspId'); }); - it('should throw an error if key is not set if no_save is true', async () => { + + it('should throw an error if resulting key is not private', async () => { const FakeLogger = { debug: () => {} }; @@ -552,35 +524,33 @@ describe('User', () => { newCryptoKeyStore: () => {}, }; + const privateStub = { + isPrivate: () => { + return false; + } + }; + const FakeCryptoSuite = { setCryptoKeyStore: () => {}, importKey: () => {}, - getKey: () => {} - }; - - const FakeCryptoAlgorithms = { - X509Certificate: 'X509Certificate', + getKey: sinon.stub().returns(privateStub), + createKeyFromRaw: () => {} }; const FakeIdentity = sandbox.stub(); - sandbox.stub(FakeCryptoSuite, 'importKey').returns(null); - const promise = new Promise((resolve) => { - resolve({isPrivate() { - return true; - }}); - }); + const promise = new Promise(() => {}); sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(FakeCryptoSuite); sandbox.stub(FakeSdkUtils, 'newCryptoKeyStore').returns('test_cryptoKeyStore'); - sandbox.stub(FakeCryptoSuite, 'getKey').returns(promise); + sandbox.stub(FakeCryptoSuite, 'importKey').returns(promise); + sandbox.stub(FakeCryptoSuite, 'createKeyFromRaw').returns('create_pem'); UserRewire.__set__('logger', FakeLogger); UserRewire.__set__('sdkUtils', FakeSdkUtils); UserRewire.__set__('Identity', FakeIdentity); - UserRewire.__set__('CryptoAlgorithms', FakeCryptoAlgorithms); const obj = new UserRewire('cfg'); - await obj.fromString('{ "name":"cfg", "roles":"test_role", "affiliation":"test_affiliation", "enrollmentSecret":"test_enrollmentSecret", "mspid":"test_mspId", "enrollment":{"identity":{"certificate":"test_certificate"}}}', true).should.be.rejectedWith(/Import of saved user has failed/); + await obj.fromString('{ "name":"cfg", "roles":"test_role", "affiliation":"test_affiliation", "enrollmentSecret":"test_enrollmentSecret", "mspid":"test_mspId", "enrollment":{"identity":{"certificate":"test_certificate"}}}', true).should.be.rejectedWith(/Private key missing from key store. Can not establish the signing identity for user/); }); it('should set import_promise if no_save is false', async () => { @@ -596,7 +566,8 @@ describe('User', () => { const FakeCryptoSuite = { setCryptoKeyStore: () => {}, importKey: () => {}, - getKey: () => {} + getKey: () => {}, + createKeyFromRaw: () => {} }; const FakeCryptoAlgorithms = { @@ -640,7 +611,8 @@ describe('User', () => { const FakeCryptoSuite = { setCryptoKeyStore: () => {}, importKey: () => {}, - getKey: () => {} + getKey: () => {}, + createKeyFromRaw: () => {} }; const FakeCryptoAlgorithms = { @@ -686,7 +658,8 @@ describe('User', () => { const FakeCryptoSuite = { setCryptoKeyStore: () => {}, importKey: () => {}, - getKey: () => {} + getKey: () => {}, + createKeyFromRaw: () => {} }; const FakeKey = { @@ -732,7 +705,8 @@ describe('User', () => { const FakeCryptoSuite = { setCryptoKeyStore: () => {}, importKey: () => {}, - getKey: () => {} + getKey: () => {}, + createKeyFromRaw: () => {} }; const FakeIdentity = sandbox.stub(); diff --git a/fabric-client/test/impl/CouchDBKeyValueStore.js b/fabric-client/test/impl/CouchDBKeyValueStore.js new file mode 100644 index 0000000000..c047360ac9 --- /dev/null +++ b/fabric-client/test/impl/CouchDBKeyValueStore.js @@ -0,0 +1,200 @@ +/** + * Copyright 2019 IBM All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +'use strict'; + +const rewire = require('rewire'); +const CouchDBKeyValueStoreRW = rewire('../../lib/impl/CouchDBKeyValueStore'); +const CouchDBKeyValueStore = require('../../lib/impl/CouchDBKeyValueStore'); +const CouchdbMock = require('mock-couch'); + +const chai = require('chai'); +const should = chai.should(); +const sinon = require('sinon'); + +describe('CouchDBKeyValueStore', () => { + + let couchDB; + const listenPort = 5489; + const badPort = 9999; + const baseURL = 'http://localhost:'; + + beforeEach(() => { + couchDB = CouchdbMock.createServer(); + couchDB.listen(listenPort); + }); + + afterEach(() => { + couchDB.close(); + }); + + describe('#constructor', () => { + + it('should throw if instantiated without options', () => { + (() => { + new CouchDBKeyValueStore(); + }).should.throw(/Must provide the CouchDB database url to store membership data/); + }); + + it('should throw if instantiated with incorrect options', () => { + (() => { + new CouchDBKeyValueStore({droids: 'not the ones looked for'}); + }).should.throw(/Must provide the CouchDB database url to store membership data/); + }); + + it('should set a default member_db name if not passed', () => { + const myStore = new CouchDBKeyValueStore({url: 'anything'}); + myStore._name.should.equal('member_db'); + }); + + it('should set the member_db name if not passed', () => { + const myStore = new CouchDBKeyValueStore({url: 'anything', name: 'pingu'}); + myStore._name.should.equal('pingu'); + }); + }); + + describe('#init', () => { + + let revert; + const errorStub = sinon.stub(); + const debugStub = sinon.stub(); + const fakeLogger = { + debug: debugStub, + error: errorStub + }; + + beforeEach(() => { + errorStub.resetHistory(); + debugStub.resetHistory(); + + revert = CouchDBKeyValueStoreRW.__set__('logger', fakeLogger); + }); + + afterEach(() => { + revert(); + }); + + it('should throw if unable to connect to URL', async () => { + const store = new CouchDBKeyValueStoreRW({url: baseURL + badPort}); + await store.initialize().should.be.rejectedWith(/Error initializing database to store membership data: member_db Error: connect ECONNREFUSED 127.0.0.1:9999/); + }); + + it('should connect to and create a store if one does not exist', async () => { + const store = new CouchDBKeyValueStoreRW({url: baseURL + listenPort}); + await store.initialize(); + + sinon.assert.calledWith(debugStub, 'Created %s database', 'member_db'); + }); + + it('should connect to a store if one does exist', async () => { + const store = new CouchDBKeyValueStoreRW({url: baseURL + listenPort}); + await store.initialize(); + + debugStub.resetHistory(); + + await store.initialize(); + sinon.assert.calledWith(debugStub, '%s already exists', 'member_db'); + }); + + }); + + + describe('#getValue', () => { + + const myFakeError = function(a, b) { + throw new Error('Forced Test Error'); + }; + + const myFakeReturn = function(a, b) { + b(null, {member: 'Fake Test Value'}); + }; + + it('should return an error', async() => { + const store = new CouchDBKeyValueStore({url: baseURL + listenPort}); + await store.initialize(); + + const fakeGet = { + get: sinon.stub().callsFake(myFakeError) + }; + store._database = fakeGet; + + await store.getValue('bert').should.be.rejectedWith(/Forced Test Error/); + }); + + it('should return null if item not found', async () => { + const store = new CouchDBKeyValueStore({url: baseURL + listenPort}); + await store.initialize(); + const val = await store.getValue('bert'); + should.not.exist(val); + }); + + it('should return the item value if item found', async () => { + const store = new CouchDBKeyValueStore({url: baseURL + listenPort}); + await store.initialize(); + + const fakeGet = { + get: sinon.stub().callsFake(myFakeReturn) + }; + store._database = fakeGet; + + const value = await store.getValue('test'); + value.should.equal('Fake Test Value'); + }); + }); + + describe('#setValue', () => { + + const myGetError = function(a, b) { + const error = new Error('Forced Error'); + error.error = 'not_found'; + b(error, null); + }; + const myGet = function(a, b) { + b(null, {_rev: 101}); + }; + + const myInsert = function(a, b) { + b(null, 'Success'); + }; + + it('should create a value if it does not exist already', async () => { + const store = new CouchDBKeyValueStore({url: baseURL + listenPort}); + await store.initialize(); + + const insertStub = sinon.stub().callsFake(myInsert); + const fakeDB = { + get: sinon.stub().callsFake(myGet), + insert: insertStub + }; + store._database = fakeDB; + + await store.setValue('myKey', 'myValue'); + + // insert should have been called once with known value + sinon.assert.calledOnce(insertStub); + sinon.assert.calledWith(insertStub, {_id: 'myKey', _rev: 101, member: 'myValue'}); + + }); + + it('should update the revision number if it already exists', async () => { + const store = new CouchDBKeyValueStore({url: baseURL + listenPort}); + await store.initialize(); + + const insertStub = sinon.stub().callsFake(myInsert); + const fakeDB = { + get: sinon.stub().callsFake(myGetError), + insert: insertStub + }; + store._database = fakeDB; + + await store.setValue('myKey', 'myValue'); + + // insert should ah e been called once with known value + sinon.assert.calledOnce(insertStub); + sinon.assert.calledWith(insertStub, {_id: 'myKey', member: 'myValue'}); + }); + }); +}); diff --git a/fabric-client/test/impl/FileKeyValueStore.js b/fabric-client/test/impl/FileKeyValueStore.js new file mode 100644 index 0000000000..2c8f0e3bc7 --- /dev/null +++ b/fabric-client/test/impl/FileKeyValueStore.js @@ -0,0 +1,183 @@ +/** + * Copyright 2019 IBM All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +'use strict'; + +const rewire = require('rewire'); +const FileKeyValueStoreRW = rewire('../../lib/impl/FileKeyValueStore'); + +const chai = require('chai'); +const should = chai.should(); +const sinon = require('sinon'); + +describe('FileKeyValueStore', () => { + + let revert; + let mkDirsStub; + let readFileStub; + let writeFileStub; + let fsStub; + let errorStub; + let debugStub; + let fakeLogger; + + + beforeEach(() => { + revert = []; + mkDirsStub = sinon.stub().returns(); + readFileStub = sinon.stub().resolves(); + writeFileStub = sinon.stub().resolves(); + fsStub = { + mkdirs: mkDirsStub, + readFile: readFileStub, + writeFile: writeFileStub + }; + + errorStub = sinon.stub(); + debugStub = sinon.stub(); + fakeLogger = { + debug: debugStub, + error: errorStub + }; + + revert.push(FileKeyValueStoreRW.__set__('fs', fsStub)); + revert.push(FileKeyValueStoreRW.__set__('logger', fakeLogger)); + }); + + afterEach(() => { + if (revert.length) { + revert.forEach(Function.prototype.call, Function.prototype.call); + } + }); + + describe('Constructor', () => { + + it('should throw when no options are given', () => { + (() => { + new FileKeyValueStoreRW(); + }).should.throw(/Must provide the path to the directory to hold files for the store./); + }); + + it('should throw when no required options are given', () => { + (() => { + new FileKeyValueStoreRW({penguin: 'unrequired'}); + }).should.throw(/Must provide the path to the directory to hold files for the store./); + }); + + + it('should set the file path from the passed options', () => { + const store = new FileKeyValueStoreRW({path: '/my/path/is/amazing'}); + store._dir.should.equal('/my/path/is/amazing'); + }); + }); + + describe('#init', () => { + it('should throw on error when creating dir', async () => { + + const err = new Error('fake error'); + err.code = 42; + mkDirsStub.throws(err); + const store = new FileKeyValueStoreRW({path: '/such_path'}); + await store.initialize().should.be.rejectedWith(/fake error/); + sinon.assert.calledOnce(errorStub); + sinon.assert.calledWith(errorStub, 'constructor, error creating directory, code: %s', 42); + }); + + it('should not throw on error when creating dir that exists', async () => { + const err = new Error('fake error'); + err.code = 'EEXIST'; + mkDirsStub.throws(err); + const store = new FileKeyValueStoreRW({path: '/such_path'}); + const response = await store.initialize(); + should.not.exist(response); + }); + + it('should call fs.mkdirs with constructor directory', () => { + const store = new FileKeyValueStoreRW({path: '/such_path'}); + store.initialize(); + + // Check correct calls + sinon.assert.calledOnce(mkDirsStub); + sinon.assert.notCalled(readFileStub); + sinon.assert.notCalled(writeFileStub); + + // check passed args + mkDirsStub.getCall(0).args[0].should.equal('/such_path'); + + // debug logging + sinon.assert.calledOnce(debugStub); + sinon.assert.calledWith(debugStub, 'constructor', {options: {path: '/such_path'}}); + + }); + }); + + describe('#getValue()', () => { + it('should call fs.readFile with passed directory key and utf8 opts', async () => { + const store = await new FileKeyValueStoreRW({path: '/such_path'}); + store.initialize(); + store.getValue('myKey'); + + // Check correct calls + sinon.assert.calledOnce(mkDirsStub); + sinon.assert.calledOnce(readFileStub); + sinon.assert.notCalled(writeFileStub); + + // check passed args + readFileStub.getCall(0).args[0].should.equal('/such_path/myKey'); + readFileStub.getCall(0).args[1].should.equal('utf8'); + + // debug logging + sinon.assert.calledWith(debugStub, 'getValue', {key: 'myKey'}); + }); + + it('should resolve null if the error returned is ENOENT', async () => { + const err = new Error('fake error'); + err.code = 'ENOENT'; + readFileStub.rejects(err); + + const myKeys = await new FileKeyValueStoreRW({path: '/such_path'}); + const resp = await myKeys.getValue('myKey'); + should.not.exist(resp); + }); + + it('should reject if the error returned is not ENOENT', async () => { + const err = new Error('fake rethrow error'); + err.code = 'NOT ENOENT'; + readFileStub.rejects(err); + const myKeys = await new FileKeyValueStoreRW({path: '/such_path'}); + await myKeys.getValue('myKey').should.be.rejectedWith(/fake rethrow error/); + }); + }); + + describe('#setValue()', () => { + + it('should call fs.writeFile with default path and passed value', async () => { + const store = await new FileKeyValueStoreRW({path: '/such_path'}); + store.initialize(); + await store.setValue('myKey', 'myValue'); + + // Check correct calls + sinon.assert.calledOnce(mkDirsStub); + sinon.assert.notCalled(readFileStub); + sinon.assert.calledOnce(writeFileStub); + + // check passed args + writeFileStub.getCall(0).args[0].should.equal('/such_path/myKey'); + writeFileStub.getCall(0).args[1].should.equal('myValue'); + + // debug logging + sinon.assert.calledWith(debugStub, 'setValue', {key: 'myKey'}); + }); + + it('should reject if the error returned is not ENOENT', async () => { + const err = new Error('fake rethrow error'); + writeFileStub.rejects(err); + const myKeys = await new FileKeyValueStoreRW({path: '/such_path'}); + await myKeys.setValue('myKey').should.be.rejectedWith(/fake rethrow error/); + }); + }); + +}); diff --git a/fabric-client/test/msp/msp.js b/fabric-client/test/msp/msp.js index 3be599d989..a7a0d6a424 100644 --- a/fabric-client/test/msp/msp.js +++ b/fabric-client/test/msp/msp.js @@ -13,9 +13,8 @@ const utils = require('../../lib/utils'); const path = require('path'); const fs = require('fs'); -const chai = require('chai'); +require('chai'); const sinon = require('sinon'); -const should = chai.should(); const rewire = require('rewire'); const MspRewire = rewire('../../lib/msp/msp'); @@ -286,11 +285,11 @@ describe('MSP', () => { } }); - it('should call cryptoSuite.importKey with ephemeral: true if passed a false flag', () => { + it('should call cryptoSuite.createKeyFromRaw if passed a false flag', () => { const importStub = sinon.stub(); const cryptoStub = { - importKey: importStub + createKeyFromRaw: importStub }; const decoded = { @@ -318,10 +317,9 @@ describe('MSP', () => { const args = importStub.getCall(0).args; args[0].should.equal('binary'); args[1].algorithm.should.equal('X509Certificate'); - args[1].ephemeral.should.equal(true); }); - it('should not call cryptoSuite.importKey with ephemeral: true if not passed a false flag', async () => { + it('should call cryptoSuite.importKey if not passed a false flag', async () => { const importStub = sinon.stub().resolves('key'); const cryptoStub = { importKey: importStub @@ -352,7 +350,6 @@ describe('MSP', () => { const args = importStub.getCall(0).args; args[0].should.equal('binary'); args[1].algorithm.should.equal('X509Certificate'); - should.not.exist(args[1].ephemeral); }); it('should deserialise a serialized identity', async () => { @@ -378,7 +375,7 @@ describe('MSP', () => { const serializedID = identity.serialize(); // Verify non-promise based route - let deserializedID = msp.deserializeIdentity(serializedID, false); + let deserializedID = await msp.deserializeIdentity(serializedID, false); deserializedID.getMSPId().should.equal('testMSP'); deserializedID = await msp.deserializeIdentity(serializedID); diff --git a/fabric-client/test/utils.js b/fabric-client/test/utils.js index 07d4ba1ed9..277c9eb14b 100644 --- a/fabric-client/test/utils.js +++ b/fabric-client/test/utils.js @@ -113,7 +113,7 @@ describe('Utils', () => { describe('#newKeyValueStore', () => { it('should create a new key value store', async() => { - const MockKeyValStore = sandbox.stub().returns(new Object({'value': 1})); + const MockKeyValStore = sandbox.stub().returns(new Object({'value': 1, initialize: sinon.stub().resolves})); requireStub = sandbox.stub().returns(MockKeyValStore); const getConfigSettingStub = sandbox.stub().returns('kvs'); revert.push(Utils.__set__('exports.getConfigSetting', getConfigSettingStub)); @@ -122,7 +122,7 @@ describe('Utils', () => { const kvs = await Utils.newKeyValueStore('options'); sinon.assert.calledWith(requireStub, 'kvs'); sinon.assert.calledWith(MockKeyValStore, 'options'); - kvs.should.deep.equal({value: 1}); + kvs.value.should.equal(1); }); }); @@ -408,10 +408,11 @@ describe('Utils', () => { keyStore = new CryptoKeyStore((value) => value); }); - it('should return a promise to the this._store', async() => { - requireStub.returns(() => Promise.resolve('keystore')); + it('should return a store to the this._store', async() => { + const fakeKeystore = new Object({name:'keystore', initialize: sinon.stub()}); + requireStub.returns(() => Promise.resolve(fakeKeystore)); const result = await keyStore._getKeyStore(); - result.should.equal('keystore'); + result.name.should.equal('keystore'); }); it('should return a promise to the this._store if this._store is set', async() => { diff --git a/fabric-common/lib/CryptoSuite.js b/fabric-common/lib/CryptoSuite.js index fd9e406f7a..48f98e9450 100644 --- a/fabric-common/lib/CryptoSuite.js +++ b/fabric-common/lib/CryptoSuite.js @@ -29,16 +29,16 @@ class CryptoSuite { /** - * Generate a key using the options in opts. If the opts.ephemeral - * parameter is false, the method, in addition to returning the imported {@link Key} - * instance, also persists the generated key in the key store as PEM files that can be + * Generate a key using the options in opts and persist it in the key store as PEM files that can be * retrieved using the getKey() method * + * @async * @param {KeyOpts} opts Optional - * @returns {module:api.Key} Promise for an instance of the Key class + * @returns {Promise} Promise for an instance of the Key class * @throws Will throw an error if not implemented */ generateKey(opts) { + throw new Error('Unimplemented abstract method'); } /** @@ -48,6 +48,7 @@ class CryptoSuite { * @throws Will throw an error if not implemented */ generateEphemeralKey() { + throw new Error('Unimplemented abstract method'); } /** @@ -62,18 +63,26 @@ class CryptoSuite { } /** - * Imports a {@link Key} from its raw representation using opts. If the opts.ephemeral - * parameter is false, the method, in addition to returning the imported {@link Key} - * instance, also saves the imported key in the key store as PEM files that can be + * Creates a {@link Key} from its raw representation + * @param {*} pem PEM string of the key to create + * @param {KeyOpts} opts Options for the concrete implementation + * @returns {module:api.Key} The created key + */ + createKeyFromRaw(pem, opts) { + throw new Error('Unimplemented abstract method'); + } + + /** + * Imports a {@link Key} from its raw representation using opts to the key store as PEM files that can be * retrieved using the 'getKey()' method * + * @async * @param {string} pem PEM string of the key to import - * @param {KeyOpts} opts Optional - * @returns {Key | Promise} If "opts.ephemeral" is true, returns the Key class synchronously. - * If "opts.ephemeral" not set or false, returns a Promise of an instance of the - * Key class. + * @param {KeyOpts} opts Options for the concrete implementation + * @returns {Promise} returns an instance of the Key class that was persisted. */ importKey(pem, opts) { + throw new Error('Unimplemented abstract method'); } /** diff --git a/fabric-common/lib/KeyValueStore.js b/fabric-common/lib/KeyValueStore.js index 9729901bc5..7732d7478d 100644 --- a/fabric-common/lib/KeyValueStore.js +++ b/fabric-common/lib/KeyValueStore.js @@ -27,6 +27,14 @@ */ class KeyValueStore { + /** + * Initialize the store + * + * @async + */ + initialize() { + } + /** * Get the value associated with name. * diff --git a/fabric-common/test/CryptoSuite.js b/fabric-common/test/CryptoSuite.js index 7de1a00ef8..9a3bfcc69c 100644 --- a/fabric-common/test/CryptoSuite.js +++ b/fabric-common/test/CryptoSuite.js @@ -27,15 +27,18 @@ describe('CryptoSuite', () => { }); describe('#generateKey', () => { - it('should return undefined', () => { - should.equal(cryptoSuite.generateKey(), undefined); - should.equal(cryptoSuite.generateKey({}), undefined); + it('should throw if unimplemented', () => { + (() => { + cryptoSuite.generateKey(); + }).should.throw(/Unimplemented abstract method/); }); }); describe('#generateEphemeralKey', () => { - it('should return undefined', () => { - should.equal(cryptoSuite.generateEphemeralKey(), undefined); + it('should throw if unimplemented', () => { + (() => { + cryptoSuite.generateEphemeralKey(); + }).should.throw(/Unimplemented abstract method/); }); }); @@ -48,10 +51,19 @@ describe('CryptoSuite', () => { }); describe('#importKey', () => { - it('should return undefined', () => { - should.equal(cryptoSuite.importKey(), undefined); - should.equal(cryptoSuite.importKey('name'), undefined); - should.equal(cryptoSuite.importKey('name', {}), undefined); + + it('should throw if unimplemented', () => { + (() => { + cryptoSuite.importKey(); + }).should.throw(/Unimplemented abstract method/); + }); + }); + + describe('#createKeyFromRaw', () => { + it('should throw if unimplemented', () => { + (() => { + cryptoSuite.createKeyFromRaw(); + }).should.throw(/Unimplemented abstract method/); }); }); diff --git a/fabric-common/test/KeyValueStore.js b/fabric-common/test/KeyValueStore.js index 4fb2d7ba69..1ec9a59fc2 100644 --- a/fabric-common/test/KeyValueStore.js +++ b/fabric-common/test/KeyValueStore.js @@ -26,6 +26,13 @@ describe('KeyValueStore', () => { keyValueStore = new KeyValueStore(); }); + describe('#initialize', () => { + it('should return undefined', async () => { + const result = await keyValueStore.initialize(); + should.equal(result, undefined); + }); + }); + describe('#getName', () => { it('should return undefined', () => { const value1 = keyValueStore.getValue('name'); diff --git a/fabric-network/lib/gateway.js b/fabric-network/lib/gateway.js index 013e1e81da..cb0af3ef14 100644 --- a/fabric-network/lib/gateway.js +++ b/fabric-network/lib/gateway.js @@ -168,7 +168,7 @@ class Gateway { if (!(config && config.constructor && config.constructor.name === 'Client')) { // still use a ccp for the discovery peer and ca information logger.debug('%s - loading client from ccp', method); - this.client = Client.loadFromConfig(config); + this.client = await Client.loadFromConfig(config); } else { // initialize from an existing Client object instance logger.debug('%s - using existing client object', method); diff --git a/fabric-network/lib/impl/wallet/couchdbwallet.js b/fabric-network/lib/impl/wallet/couchdbwallet.js index af3a71f325..245e9144c2 100644 --- a/fabric-network/lib/impl/wallet/couchdbwallet.js +++ b/fabric-network/lib/impl/wallet/couchdbwallet.js @@ -57,6 +57,7 @@ class CouchDBWallet extends BaseWallet { const method = 'getStateStore'; logger.debug('in %s, label = %s', method, label); const store = new CouchDBWalletKeyValueStore(this._createOptions()); + await store.initialize(); return store; } diff --git a/fabric-network/lib/impl/wallet/filesystemwallet.js b/fabric-network/lib/impl/wallet/filesystemwallet.js index eb068d61f3..2d6d2e8cf4 100644 --- a/fabric-network/lib/impl/wallet/filesystemwallet.js +++ b/fabric-network/lib/impl/wallet/filesystemwallet.js @@ -26,11 +26,11 @@ class FileSystemWallet extends BaseWallet { * * @static * @param {string} path the root path of the key value store - * @returns {Promise} a promise that is resolved when a new File KVS instance is recreated. + * @returns {FileKVS} a new File KVS instance. * @private */ - static async _createFileKVS(path) { - return await new FileKVS({path}); + static _createFileKVS(path) { + return new FileKVS({path}); } /** @@ -84,7 +84,9 @@ class FileSystemWallet extends BaseWallet { async getStateStore(label) { const partitionedPath = this._getPartitionedPath(label); - return FileSystemWallet._createFileKVS(partitionedPath); + const store = FileSystemWallet._createFileKVS(partitionedPath); + await store.initialize(); + return store; } async getCryptoSuite(label) { diff --git a/fabric-network/lib/impl/wallet/inmemorywallet.js b/fabric-network/lib/impl/wallet/inmemorywallet.js index 7c09009774..f75ad1bb4f 100644 --- a/fabric-network/lib/impl/wallet/inmemorywallet.js +++ b/fabric-network/lib/impl/wallet/inmemorywallet.js @@ -33,11 +33,10 @@ class InMemoryWallet extends BaseWallet { logger.debug('in InMemoryWallet constructor'); } - async getStateStore(label) { + getStateStore(label) { logger.debug('in getStateStore, label = %s', label); label = this.normalizeLabel(label); - const store = await new InMemoryKVS(label); - return store; + return new InMemoryKVS(label); } async getCryptoSuite(label) { @@ -89,7 +88,6 @@ class InMemoryKVS extends KeyValueStore { super(); logger.debug('in InMemoryKVS constructor, prefix = ' + prefix); this.partitionKey = prefix; - return Promise.resolve(this); } async getValue(name) { diff --git a/fabric-network/test/impl/wallet/couchdbwallet.js b/fabric-network/test/impl/wallet/couchdbwallet.js index 87d8fa3ea8..af50ab961d 100644 --- a/fabric-network/test/impl/wallet/couchdbwallet.js +++ b/fabric-network/test/impl/wallet/couchdbwallet.js @@ -26,11 +26,13 @@ describe('CouchDBWallet', () => { let deleteStub; let existsStub; let getAllLabelsStub; + let initStub; const CouchDBKeyValueStoreMock = class { constructor() { this.delete = deleteStub; this.exists = existsStub; this.getAllLabels = getAllLabelsStub; + this.initialize = initStub; } }; let FakeLogger; @@ -58,6 +60,7 @@ describe('CouchDBWallet', () => { getStub = sandbox.stub(); destroyStub = sandbox.stub(); getAllLabelsStub = sandbox.stub(); + initStub = sandbox.stub(); nanoStub.returns({db: { destroy: destroyStub, get: getStub, diff --git a/fabric-network/test/impl/wallet/filesystemwallet.js b/fabric-network/test/impl/wallet/filesystemwallet.js index 77b8a15fed..c6355c200a 100644 --- a/fabric-network/test/impl/wallet/filesystemwallet.js +++ b/fabric-network/test/impl/wallet/filesystemwallet.js @@ -14,7 +14,7 @@ const rewire = require('rewire'); const FileSystemWallet = rewire('../../../lib/impl/wallet/filesystemwallet'); const X509WalletMixin = require('../../../lib/impl/wallet/x509walletmixin'); const Client = require('fabric-client'); -const {CryptoSuite, KeyValueStore} = require('fabric-common'); +const {CryptoSuite} = require('fabric-common'); const fs = require('fs-extra'); const Path = require('path'); const rimraf = require('rimraf'); @@ -51,10 +51,22 @@ describe('FileSystemWallet', () => { }); describe('#_createFileKVS', () => { + let revert; + let instanceStub; + + beforeEach(() => { + instanceStub = sinon.stub(); + revert = FileSystemWallet.__set__('FileKVS', instanceStub); + }); + + afterEach(() => { + revert(); + }); + it('should create a File Key Value Store', async () => { - sandbox.stub(fs, 'mkdirs').callsArg(1); - const store = await FileSystemWallet._createFileKVS('test'); - store.should.be.an.instanceof(KeyValueStore); + await FileSystemWallet._createFileKVS('test'); + sinon.assert.calledWithNew(instanceStub); + sinon.assert.calledWith(instanceStub, {path: 'test'}); }); }); @@ -108,12 +120,12 @@ describe('FileSystemWallet', () => { describe('#getStateStore', () => { it('should create a KV store', async () => { // use Error as a class to be detected - sandbox.stub(FileSystemWallet, '_createFileKVS').resolves(new Error()); + sandbox.stub(FileSystemWallet, '_createFileKVS').returns(new Object({'value': 1, initialize: sinon.stub().resolves})); sinon.stub(testwallet, '_getPartitionedPath').returns('/partitioned/path'); const store = await testwallet.getStateStore('test'); sinon.assert.calledOnce(FileSystemWallet._createFileKVS); sinon.assert.calledWith(FileSystemWallet._createFileKVS, '/partitioned/path'); - store.should.be.an.instanceof(Error); + store.value.should.equal(1); }); }); diff --git a/package.json b/package.json index 8ebeb8e4b3..c96664c17b 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "jsrsasign": "^7.2.2", "log4js": "^2.6.1", "mocha": "5.2.0", - "mock-couch": "git+https://github.com/jimthematrix/mock-couch.git", + "mock-couch": "^0.1.11", "mockery": "^2.1.0", "nano": "^6.4.4", "nyc": "^12.0.2", diff --git a/release_notes/v2.0.0.txt b/release_notes/v2.0.0.txt new file mode 100644 index 0000000000..dfafb3215c --- /dev/null +++ b/release_notes/v2.0.0.txt @@ -0,0 +1,30 @@ +Release Notes +------------- + + +Bug fixes and documentation improvements. + +Breaking Changes +--------------------- + +CR https://gerrit.hyperledger.org/r/#/c/29360/ delivers breaking changes to the key-value stores and Cryptosuite classes: + - Key-value stores no longer initialize the store within the constructor; there is a seperate (async) `initialize` method that contains the initialize logic. + - Cryptosuite interface is now strictly followed. The `generateKey` method within implementations no longer accepts `ephemeral: true` as an option; the `generateEphemeralKey` method should be used to retrive an ephemeral key. + + +Known Vulnerabilities +--------------------- +none + + +Resolved Vulnerabilities +------------------------ +none + + +Known Issues & Workarounds +-------------------------- + + +Change Log +---------- diff --git a/test/fixtures/chaincode/goLang/src/github.com/example_cc/example_cc.go b/test/fixtures/chaincode/goLang/src/github.com/example_cc/example_cc.go index 6e12fc00cc..15656f2e9f 100644 --- a/test/fixtures/chaincode/goLang/src/github.com/example_cc/example_cc.go +++ b/test/fixtures/chaincode/goLang/src/github.com/example_cc/example_cc.go @@ -77,6 +77,9 @@ func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { function, args := stub.GetFunctionAndParameters() + logger.Info("function: ", function) + logger.Info("args: ", args) + if function == "delete" { // Deletes an entity from its state return t.delete(stub, args) diff --git a/test/fixtures/chaincode/node_cc/example_cc/chaincode.js b/test/fixtures/chaincode/node_cc/example_cc/chaincode.js index be0e972d75..666704591a 100644 --- a/test/fixtures/chaincode/node_cc/example_cc/chaincode.js +++ b/test/fixtures/chaincode/node_cc/example_cc/chaincode.js @@ -67,6 +67,7 @@ const Chaincode = class { const fcn = ret.fcn; const args = ret.params; + logger.info('-stub.getFunctionAndParameters(): ', JSON.stringify(ret)); try { if (fcn === 'delete') { return this.delete(stub, args); diff --git a/test/fixtures/docker-compose/docker-compose.yaml b/test/fixtures/docker-compose/docker-compose.yaml index cfcb75177d..c173164568 100644 --- a/test/fixtures/docker-compose/docker-compose.yaml +++ b/test/fixtures/docker-compose/docker-compose.yaml @@ -40,6 +40,8 @@ services: service: peer container_name: peer0.org1.example.com environment: + - CORE_LOGGING_LEVEL=debug + - CORE_CHAINCODE_LOGGING_LEVEL=debug - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock - CORE_PEER_ID=peer0.org1.example.com - CORE_PEER_ADDRESS=peer0.org1.example.com:7051 @@ -78,6 +80,8 @@ services: container_name: peer0.org2.example.com environment: + - CORE_LOGGING_LEVEL=debug + - CORE_CHAINCODE_LOGGING_LEVEL=debug - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock - CORE_PEER_ID=peer0.org2.example.com - CORE_PEER_ADDRESS=peer0.org2.example.com:8051 diff --git a/test/integration/e2e/e2eUtils.js b/test/integration/e2e/e2eUtils.js index 92cf3edd59..21d2160ea9 100644 --- a/test/integration/e2e/e2eUtils.js +++ b/test/integration/e2e/e2eUtils.js @@ -577,7 +577,7 @@ function invokeChaincode(userOrg, version, chaincodeId, t, useStore, fcn, args, t.comment('stop and start the peer event hub ---- N O W ----- you have ' + sleep_time + ' millis ' + (new Date()).toString()); t.comment('*****************************************************************************'); return exports.sleep(sleep_time); - }).then(() => { + }).then(async() => { const proposalResponses = pass_results[0]; const proposal = pass_results[1]; @@ -594,7 +594,7 @@ function invokeChaincode(userOrg, version, chaincodeId, t, useStore, fcn, args, logger.debug('invoke chaincode, proposal response: ' + util.inspect(proposal_response, {depth: null})); if (proposal_response.response && proposal_response.response.status === 200) { t.pass('transaction proposal has response status of good'); - one_good = channel.verifyProposalResponse(proposal_response); + one_good = await channel.verifyProposalResponse(proposal_response); if (one_good) { t.pass('transaction proposal signature and endorser are valid'); } diff --git a/test/integration/grpc.js b/test/integration/grpc.js index 5434718c7b..3662ef67c8 100644 --- a/test/integration/grpc.js +++ b/test/integration/grpc.js @@ -193,7 +193,7 @@ async function send(client, channel, url, cc, opts, megs, grpc_send_max, grpc_re } async function sendToConnectionProfile(client, channel, config, cc, megs) { - client.loadFromConfig(config); + await client.loadFromConfig(config); const peer = client.getPeersForOrg('Org1MSP')[0]; // will only be one const request = { diff --git a/test/integration/invoke.js b/test/integration/invoke.js index 77b91bcfcb..77afe70023 100644 --- a/test/integration/invoke.js +++ b/test/integration/invoke.js @@ -170,7 +170,7 @@ function invokeChaincode(userOrg, version, t, shouldFail, peersArray) { t.fail('Failed to enroll user \'admin\'. ' + err); throw new Error('Failed to enroll user \'admin\'. ' + err); - }).then((results) => { + }).then(async (results) => { const proposalResponses = results[0]; const proposal = results[1]; let all_good = true; @@ -180,7 +180,7 @@ function invokeChaincode(userOrg, version, t, shouldFail, peersArray) { const proposal_response = proposalResponses[i]; if (proposal_response.response && proposal_response.response.status === 200) { t.pass('transaction proposal has response status of good'); - one_good = channel.verifyProposalResponse(proposal_response); + one_good = await channel.verifyProposalResponse(proposal_response); if (one_good) { t.pass(' transaction proposal signature and endorser are valid'); } diff --git a/test/integration/memory.js b/test/integration/memory.js index 77e8bd5eef..1f50c887b1 100644 --- a/test/integration/memory.js +++ b/test/integration/memory.js @@ -99,7 +99,7 @@ async function createChannel(t) { // this network config does not have the client information, we will // load that later so that we can switch this client to be in a different // organization - const client = Client.loadFromConfig('test/fixtures/profiles/network.yaml'); + const client = await Client.loadFromConfig('test/fixtures/profiles/network.yaml'); t.pass('Successfully loaded a Common connection profile'); const channel_name = 'mychannel'; @@ -115,7 +115,7 @@ async function createChannel(t) { try { // lets load the client information for this organization // the file only has the client section - client.loadFromConfig('test/fixtures/profiles/org1.yaml'); + await client.loadFromConfig('test/fixtures/profiles/org1.yaml'); t.pass('Successfully loaded client section of network config'); // tell this client instance where the state and key stores are located @@ -229,7 +229,7 @@ async function createChannel(t) { /* * switch to organization org1 */ - client.loadFromConfig('test/fixtures/profiles/org1.yaml'); + await client.loadFromConfig('test/fixtures/profiles/org1.yaml'); t.pass('Successfully loaded \'admin\' for org1'); await client.initCredentialStores(); @@ -280,7 +280,7 @@ async function createChannel(t) { * switch to organization org2 */ - client.loadFromConfig('test/fixtures/profiles/org2.yaml'); + await client.loadFromConfig('test/fixtures/profiles/org2.yaml'); await client.initCredentialStores(); t.pass('Successfully created the key value store and crypto store based on the config and network config'); @@ -367,7 +367,7 @@ async function actions(t) { // this network config does not have the client information, we will // load that later so that we can switch this client to be in a different // organization - const client = Client.loadFromConfig('test/fixtures/profiles/network.yaml'); + const client = await Client.loadFromConfig('test/fixtures/profiles/network.yaml'); t.pass('Successfully loaded a common connection profile'); const channel_name = 'mychannel'; @@ -381,7 +381,7 @@ async function actions(t) { try { // lets load the client information for this organization // the file only has the client section - client.loadFromConfig('test/fixtures/profiles/org1.yaml'); + await client.loadFromConfig('test/fixtures/profiles/org1.yaml'); // tell this client instance where the state and key stores are located await client.initCredentialStores(); t.pass('Successfully created the key value store and crypto store based on the config and network config'); diff --git a/test/integration/network-config.js b/test/integration/network-config.js index 3308cac063..4d956978e2 100644 --- a/test/integration/network-config.js +++ b/test/integration/network-config.js @@ -22,12 +22,12 @@ const path = require('path'); const testUtil = require('../unit/util.js'); -test('\n\n***** clean up the connection profile testing stores *****\n\n', (t) => { +test('\n\n***** clean up the connection profile testing stores *****\n\n', async (t) => { /* * The following is just testing housekeeping... cleanup from the last time * this test was run, a real application would not do this. */ - const client = Client.loadFromConfig('test/fixtures/profiles/org1.yaml'); + const client = await Client.loadFromConfig('test/fixtures/profiles/org1.yaml'); let client_config = client.getClientConfig(); let store_path = client_config.credentialStore.path; @@ -38,7 +38,7 @@ test('\n\n***** clean up the connection profile testing stores *****\n\n', (t) logger.debug('removing org1 cryptoStore %s', crypto_path); fsx.removeSync(crypto_path); - client.loadFromConfig('test/fixtures/profiles/org2.yaml'); + await client.loadFromConfig('test/fixtures/profiles/org2.yaml'); client_config = client.getClientConfig(); store_path = client_config.credentialStore.path; @@ -62,8 +62,8 @@ test('\n\n***** use the connection profile file *****\n\n', async (t) => { // this connection profile does not have the client information, we will // load that later so that we can switch this client to be in a different // organization. - const client_org1 = Client.loadFromConfig('test/fixtures/profiles/network2.yaml'); - const client_org2 = Client.loadFromConfig('test/fixtures/profiles/network2.yaml'); + const client_org1 = await Client.loadFromConfig('test/fixtures/profiles/network2.yaml'); + const client_org2 = await Client.loadFromConfig('test/fixtures/profiles/network2.yaml'); t.pass('Successfully loaded a connection profile'); let config = null; @@ -77,8 +77,8 @@ test('\n\n***** use the connection profile file *****\n\n', async (t) => { // Load the client information for an organization. // The file only has the client section. // A real application might do this when a new user logs in. - client_org1.loadFromConfig('test/fixtures/profiles/org1.yaml'); - client_org2.loadFromConfig('test/fixtures/profiles/org2.yaml'); + await client_org1.loadFromConfig('test/fixtures/profiles/org1.yaml'); + await client_org2.loadFromConfig('test/fixtures/profiles/org2.yaml'); try { // tell this client instance where the state and key stores are located @@ -672,8 +672,8 @@ test('\n\n***** Enroll user and set user context using a specified caName *****\ // Build a 'Client' instance that knows the network // then load org1.yaml to the same instance - const client_org1 = Client.loadFromConfig('test/fixtures/profiles/network.yaml'); - client_org1.loadFromConfig('test/fixtures/profiles/org1.yaml'); + const client_org1 = await Client.loadFromConfig('test/fixtures/profiles/network.yaml'); + await client_org1.loadFromConfig('test/fixtures/profiles/org1.yaml'); t.pass('Successfully loaded client section of network config'); // tell this client instance where the state and key stores are located @@ -736,8 +736,8 @@ test('\n\n***** Enroll user and set user context using a bad caName *****\n\n', // Build a 'Client' instance that knows the network // then load org1.yaml to the same instance - const client_org1 = Client.loadFromConfig('test/fixtures/profiles/network.yaml'); - client_org1.loadFromConfig('test/fixtures/profiles/org1.yaml'); + const client_org1 = await Client.loadFromConfig('test/fixtures/profiles/network.yaml'); + await client_org1.loadFromConfig('test/fixtures/profiles/org1.yaml'); t.pass('Successfully loaded client section of network config'); // tell this client instance where the state and key stores are located diff --git a/test/integration/network-e2e/idmanager.js b/test/integration/network-e2e/idmanager.js index 2563e18aad..bed714322f 100644 --- a/test/integration/network-e2e/idmanager.js +++ b/test/integration/network-e2e/idmanager.js @@ -10,8 +10,8 @@ const User = require('fabric-client/lib/User'); class IDManager { - initialize(ccp) { - this.client = Client.loadFromConfig(ccp); + async initialize(ccp) { + this.client = await Client.loadFromConfig(ccp); } async registerUser(userID, options, issuerWallet, issuerId) { diff --git a/test/integration/only-admin.js b/test/integration/only-admin.js index 7259ec4c74..43963534e0 100644 --- a/test/integration/only-admin.js +++ b/test/integration/only-admin.js @@ -55,7 +55,7 @@ async function manually(t, client) { const pem = Buffer.from(data).toString(); t.pass('Successfully read all crypto material'); - client.setAdminSigningIdentity(key, cert, 'OrdererMSP'); + await client.setAdminSigningIdentity(key, cert, 'OrdererMSP'); t.pass('Successfully set the client with admin signing identity'); const sys_channel = client.newChannel('testchainid'); diff --git a/test/integration/orderer-channel-tests.js b/test/integration/orderer-channel-tests.js index 581d61b5ac..1aa85d6bec 100644 --- a/test/integration/orderer-channel-tests.js +++ b/test/integration/orderer-channel-tests.js @@ -33,7 +33,7 @@ const org = 'org1'; // before the orderer URL was set. Verify that an error is reported when tying // to send the request. // -test('\n\n** TEST ** orderer via member missing orderer', (t) => { +test('\n\n** TEST ** orderer via member missing orderer', async (t) => { testUtil.resetDefaults(); utils.setConfigSetting('key-value-store', 'fabric-ca-client/lib/impl/FileKeyValueStore.js');// force for 'gulp test' Client.addConfigFile(path.join(__dirname, 'e2e', 'config.json')); @@ -48,12 +48,12 @@ test('\n\n** TEST ** orderer via member missing orderer', (t) => { cryptoSuite.setCryptoKeyStore(Client.newCryptoKeyStore({path: testUtil.storePathForOrg(orgName)})); client.setCryptoSuite(cryptoSuite); - Client.newDefaultKeyValueStore({ + const store = await Client.newDefaultKeyValueStore({ path: testUtil.KVS - }).then((store) => { - client.setStateStore(store); - return testUtil.getSubmitter(client, t, org); - }).then( + }); + + client.setStateStore(store); + testUtil.getSubmitter(client, t, org).then( () => { t.pass('Successfully enrolled user \'admin\''); diff --git a/test/scenario/features/lib/channel.js b/test/scenario/features/lib/channel.js index 2f8a455759..5c260a8d7b 100644 --- a/test/scenario/features/lib/channel.js +++ b/test/scenario/features/lib/channel.js @@ -138,7 +138,7 @@ async function existing_channels(ccp, tls) { cryptoSuite.setCryptoKeyStore(Client.newCryptoKeyStore({path: testUtil.storePathForOrg(org)})); client.setCryptoSuite(cryptoSuite); - client.loadFromConfig(ccp.getProfile()); + await client.loadFromConfig(ccp.getProfile()); // Run this to set the required identity on the client object await testUtil.getSubmitter(client, true, orgName, ccp); diff --git a/test/typescript/test.ts b/test/typescript/test.ts index e0f0ce9883..367a3eec0c 100644 --- a/test/typescript/test.ts +++ b/test/typescript/test.ts @@ -86,11 +86,11 @@ test('test-crypto-key-store', (t: any) => { t.end(); }); -test('use the connection profile file', (t: any) => { - let client = Client.loadFromConfig(configNetwork); +test('use the connection profile file', async (t: any) => { + let client = await Client.loadFromConfig(configNetwork); t.pass('Successfully load config from network.yaml'); - client.loadFromConfig(configOrg1); + await client.loadFromConfig(configOrg1); let config: Buffer; const signatures: any[] = []; @@ -182,7 +182,7 @@ test('use the connection profile file', (t: any) => { txId, }; return channel.joinChannel(request); //admin from org2 - }).then((results: ProposalResponse[]) => { + }).then(async (results: ProposalResponse[]) => { // first of the results should not have good status as submitter does not have permission if (results && results[0] && results[0].response && results[0].response.status === 200) { t.fail('Successfully had peer in organization org1 join the channel'); @@ -202,9 +202,9 @@ test('use the connection profile file', (t: any) => { /* * switch to organization org1 (recreate client) */ - client = Client.loadFromConfig(configNetwork); + client = await Client.loadFromConfig(configNetwork); - client.loadFromConfig(configOrg1); + await client.loadFromConfig(configOrg1); t.pass('Successfully loaded \'admin\' for org1'); return client.initCredentialStores(); }).then(() => { @@ -258,7 +258,7 @@ test('use the connection profile file', (t: any) => { }; return client.installChaincode(request); - }).then((results: ProposalResponseObject) => { + }).then(async (results: ProposalResponseObject) => { const firstResponse = results[0][0]; if (firstResponse instanceof Error || firstResponse.response.status !== 200) { t.fail(' Failed to install chaincode on org1'); @@ -267,7 +267,7 @@ test('use the connection profile file', (t: any) => { } t.pass('Successfully installed chain code on org1'); - client.loadFromConfig(configOrg2); + await client.loadFromConfig(configOrg2); t.pass('Successfully loaded \'admin\' for org2'); return client.initCredentialStores(); }).then(() => { @@ -284,7 +284,7 @@ test('use the connection profile file', (t: any) => { }; return client.installChaincode(request); - }).then((results: ProposalResponseObject) => { + }).then(async (results: ProposalResponseObject) => { const firstResponse = results[0][0]; if (firstResponse instanceof Error || firstResponse.response.status !== 200) { t.fail(' Failed to install chaincode on org2'); @@ -294,7 +294,7 @@ test('use the connection profile file', (t: any) => { t.pass('Successfully installed chain code on org2'); // Back to org1 for instantiation - client.loadFromConfig(configOrg1); + await client.loadFromConfig(configOrg1); t.pass('Successfully loaded \'admin\' for org1'); return client.initCredentialStores(); }).then(() => { @@ -340,7 +340,7 @@ test('use the connection profile file', (t: any) => { t.fail('Failed to order the transaction to instantiate the chaincode. Error code: ' + response.status); throw new Error('Failed to order the transaction to instantiate the chaincode. Error code: ' + response.status); } - }).then(() => { + }).then(async () => { t.pass('Successfully waited for chaincode to startup'); /* @@ -350,10 +350,10 @@ test('use the connection profile file', (t: any) => { * switch to organization org2 */ - client.loadFromConfig('test/fixtures/profiles/org2.yaml'); + await client.loadFromConfig('test/fixtures/profiles/org2.yaml'); return client.initCredentialStores(); - }).then(() => { + }).then(async () => { t.pass('Successfully created the key value store and crypto store based on the config and connection profile'); const ca: FabricCAServices = client.getCertificateAuthority(); @@ -366,7 +366,7 @@ test('use the connection profile file', (t: any) => { /* * switch to organization org1 */ - client.loadFromConfig('test/fixtures/profiles/org1.yaml'); + await client.loadFromConfig('test/fixtures/profiles/org1.yaml'); t.pass('Successfully loaded config for org1'); return client.initCredentialStores(); diff --git a/test/unit/couchdb-key-value-store.js b/test/unit/couchdb-key-value-store.js deleted file mode 100644 index 29ad3fda83..0000000000 --- a/test/unit/couchdb-key-value-store.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright 2017 IBM All Rights Reserved. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict'; - -const tape = require('tape'); -const _test = require('tape-promise').default; -const test = _test(tape); -const CouchdbMock = require('mock-couch'); - -const CDBKVS = require('fabric-client/lib/impl/CouchDBKeyValueStore.js'); - -test('\n\n** CouchDBKeyValueStore tests', (t) => { - t.throws( - () => { - new CDBKVS(); - }, - /Must provide the CouchDB database url to store membership data/, - 'Error checking in the constructor: missing opts' - ); - - t.throws( - () => { - new CDBKVS({dummy: 'value'}); - }, - /Must provide the CouchDB database url to store membership data/, - 'Error checking in the constructor: opts object missing required "url"' - ); - - let store; - - new CDBKVS({url: 'http://localhost:9999'}) - .then(() => { - t.fail('Should not have been able to successfully construct a store from an invalid URL'); - throw new Error('Failed'); - }, (err) => { - if (err.message && err.message.indexOf('ECONNREFUSED') > 0) { - t.pass('Successfully rejected the construction request due to invalid URL'); - } else { - t.fail('Store construction failed for unknown reason: ' + err.stack ? err.stack : err); - throw new Error('Failed'); - } - - const couchdb = CouchdbMock.createServer(); - couchdb.listen(5985); - - // override t.end function so it'll always disconnect the event hub - t.end = ((context, mockdb, f) => { - return function() { - if (mockdb) { - t.comment('Disconnecting the mock couchdb server'); - mockdb.close(); - } - - f.apply(context, arguments); - }; - })(t, couchdb, t.end); - - return new CDBKVS({url: 'http://localhost:5985'}); - }).then((st) => { - store = st; - t.pass('Successfully connected the key value store to couchdb at localhost:5985'); - - t.notEqual(store._database, undefined, 'Check "_database" value of the constructed store object'); - - return store.setValue('someKey', 'someValue'); - }).then((value) => { - t.equal(value, 'someValue', 'Check result of setValue()'); - - return store.getValue('someKey'); - }).then((value) => { - t.equal(value, 'someValue', 'Check result of getValue()'); - t.end(); - }).catch((err) => { - t.fail(err.stack ? err.stack : err); - t.end(); - }); -}); diff --git a/test/unit/crypto-key-store.js b/test/unit/crypto-key-store.js index 655d24723c..007ec57fb7 100644 --- a/test/unit/crypto-key-store.js +++ b/test/unit/crypto-key-store.js @@ -63,28 +63,9 @@ test('\n\n** CryptoKeyStore tests **\n\n', (t) => { const keystorePath = path.join(testutil.getTempDir(), 'crypto-key-store'); - t.throws( - () => { - CKS(); - }, - /Must provide the path to the directory to hold files for the store/, - 'Test invalid constructor calls: missing options parameter' - ); - - t.throws( - () => { - CKS({something: 'useless'}); - }, - /Must provide the path to the directory to hold files for the store/, - 'Test invalid constructor calls: missing "path" property in the "options" parameter' - ); - - let store; - CKS({path: keystorePath}) - .then((st) => { - store = st; - return store.putKey(testPrivKey); - }).then(() => { + const store = CKS({path: keystorePath}); + return store.initialize().then(() => { + store.putKey(testPrivKey).then(() => { t.pass('Successfully saved private key in store'); t.equal(fs.existsSync(path.join(keystorePath, testPrivKey.getSKI() + '-priv')), true, @@ -116,6 +97,7 @@ test('\n\n** CryptoKeyStore tests **\n\n', (t) => { t.fail(err.stack ? err.stack : err); t.end(); }); + }); }); @@ -137,8 +119,9 @@ test('\n\n** CryptoKeyStore tests - couchdb based store tests - use configSettin }; })(t, couchdb, t.end); - CKS({name: dbname, url: 'http://localhost:5985'}) - .then((store) => { + const store = CKS({name: dbname, url: 'http://localhost:5985'}); + store.initialize() + .then(() => { return testKeyStore(store, t); }).catch((err) => { t.fail(err.stack ? err.stack : err); @@ -164,8 +147,9 @@ test('\n\n** CryptoKeyStore tests - couchdb based store tests - use constructor }; })(t, couchdb, t.end); - CKS(CouchDBKeyValueStore, {name: dbname, url: 'http://localhost:5985'}) - .then((store) => { + const store = CKS(CouchDBKeyValueStore, {name: dbname, url: 'http://localhost:5985'}); + store.initialize() + .then(() => { return testKeyStore(store, t); }).catch((err) => { t.fail(err.stack ? err.stack : err); diff --git a/test/unit/cryptosuite-ecdsa-aes.js b/test/unit/cryptosuite-ecdsa-aes.js index 0cb7c431a0..e6ba633bc1 100644 --- a/test/unit/cryptosuite-ecdsa-aes.js +++ b/test/unit/cryptosuite-ecdsa-aes.js @@ -95,24 +95,29 @@ test('\n\n** utils.newCryptoSuite tests **\n\n', (t) => { t.end(); }); -test('\n\n ** CryptoSuite_ECDSA_AES - error tests **\n\n', (t) => { +test('\n\n ** CryptoSuite_ECDSA_AES - error tests **\n\n', async (t) => { testutil.resetDefaults(); const cryptoUtils = utils.newCryptoSuite(); - t.throws( - () => { - cryptoUtils.importKey(TEST_CERT_PEM); - }, - /importKey opts.ephemeral is false, which requires CryptoKeyStore to be set./, - 'Test missing cryptoKeyStore: cryptoSuite.importKey' - ); - cryptoUtils.generateKey().catch(err => { + + try { + await cryptoUtils.importKey(TEST_CERT_PEM); + t.fail('Import key did not fail when testing missing cryptoKeyStore'); + } catch (err) { t.ok(err.toString() - .includes('generateKey opts.ephemeral is false, which requires CryptoKeyStore to be set.'), - 'Test missing cryptoKeyStore: cryptoSuite.generateKey'); + .includes('importKey requires CryptoKeyStore to be set.'), + 'Test missing cryptoKeyStore: cryptoSuite.importKey'); + } - t.end(); - }); + try { + await cryptoUtils.generateKey(); + t.fail('generateKey did not fail when testing missing cryptoKeyStore'); + } catch (err) { + t.ok(err.toString() + .includes('generateKey requires CryptoKeyStore to be set.'), + 'Test missing cryptoKeyStore: cryptoSuite.generateKey'); + } + t.end(); }); test('\n\n ** CryptoSuite_ECDSA_AES - generateEphemeralKey tests **\n\n', (t) => { @@ -129,29 +134,15 @@ test('\n\n ** CryptoSuite_ECDSA_AES - generateEphemeralKey tests **\n\n', (t) => }); -test('\n\n ** CryptoSuite_ECDSA_AES - ephemeral true tests **\n\n', (t) => { +test('\n\n ** CryptoSuite_ECDSA_AES - createKeyFromRaw **\n\n', async (t) => { testutil.resetDefaults(); const cryptoUtils = utils.newCryptoSuite(); - const key = cryptoUtils.importKey(TEST_KEY_PRIVATE_PEM, {ephemeral: true}); + const key = cryptoUtils.createKeyFromRaw(TEST_KEY_PRIVATE_PEM); if (key && key._key && key._key.type === 'EC') { t.pass('importKey returned key using ephemeral true'); } else { t.fail('importKey did not return key using ephemeral true'); } - - return cryptoUtils.generateKey({ephemeral: true}) - .then((generatedKey) => { - if (generatedKey && generatedKey._key && generatedKey._key.type === 'EC') { - t.pass('generateKey returned key using ephemeral true'); - t.end(); - } else { - t.fail('generateKey did not return key using ephemeral true'); - t.end(); - } - }, (err) => { - t.fail('Failed to generateKey. Can not progress any further. Exiting. ' + err.stack ? err.stack : err); - t.end(); - }); }); test('\n\n ** CryptoSuite_ECDSA_AES - function tests **\n\n', (t) => { @@ -432,31 +423,3 @@ test('\n\n ** CryptoSuite_ECDSA_AES - function tests **\n\n', (t) => { t.end(); }); }); - -// function cleanupFileKeyValueStore(keyValStorePath) { -// var absPath = getAbsolutePath(keyValStorePath); -// var exists = testutil.existsSync(absPath); -// if (exists) { -// fs.removeSync(absPath); -// } -// } - -// prepend absolute path where this test is running, then join to the relative path -// function getAbsolutePath(dir) { -// return path.join(process.cwd(), getRelativePath(dir)); -// } - -// get relative file path for either Unix or Windows -// unix relative path does not start with '/' -// windows relative path starts with '/' -// function getRelativePath(dir /*string*/) { -// if (/^win/.test(process.platform)) { -// if (!(dir.toString().substr(0, 1) === '/')) dir = '/' + dir; -// dir = path.resolve(dir); -// dir = dir.replace(/([A-Z]:[\\\/]).*?/gi, ''); -// return dir; -// } else { -// if (dir.toString().substr(0, 1) === '/') dir = dir.substr(1); -// return dir; -// } -// } diff --git a/test/unit/file-key-value-store.js b/test/unit/file-key-value-store.js deleted file mode 100644 index c5c1540b54..0000000000 --- a/test/unit/file-key-value-store.js +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright 2016 IBM All Rights Reserved. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict'; - -const tape = require('tape'); -const _test = require('tape-promise').default; -const test = _test(tape); - -const path = require('path'); -const testUtil = require('./util.js'); -const fs = require('fs-extra'); -const utils = require('fabric-client/lib/utils.js'); - -const FileKeyValueStore = require('fabric-client/lib/impl/FileKeyValueStore.js'); - -const keyValStorePath = path.join(testUtil.getTempDir(), 'kvsTemp'); -const testKey = 'keyValFileStoreName'; -const testValue = 'secretKeyValue'; -let store1 = ''; - -test('\n\n ** FileKeyValueStore - read and write test **\n\n', (t) => { - testUtil.resetDefaults(); - - // clean up - if (testUtil.existsSync(keyValStorePath)) { - fs.removeSync(keyValStorePath); - } - utils.newKeyValueStore({ - path: keyValStorePath - }) - .then( - (store) => { - if (testUtil.existsSync(keyValStorePath)) { - t.pass('FileKeyValueStore read and write test: Successfully created new directory for testValueStore'); - } else { - t.fail('FileKeyValueStore read and write test: failed to create new directory for testValueStore'); - t.end(); - } - store1 = store; - return store.setValue(testKey, testValue); - } - ).then( - (result) => { - if (result === testValue) { - t.pass('FileKeyValueStore read and write test: Successfully set value'); - } else { - t.fail('FileKeyValueStore read and write test: set value ' + result + 'does not match testValue ' + testValue); - t.end(); - } - if (testUtil.existsSync(path.join(keyValStorePath, testKey))) { - t.pass('FileKeyValueStore read and write test: Verified the file for key ' + testKey + ' does exist'); - - return store1.getValue(testKey); - } else { - t.fail('FileKeyValueStore read and write test: Failed to create file for key ' + testKey); - t.end(); - } - }, - (reason) => { - t.fail('FileKeyValueStore read and write test: Failed to set value, reason: ' + reason); - t.end(); - } - ).then( - // Log the fulfillment value - (val) => { - if (val !== testValue) { - t.fail('FileKeyValueStore read and write test: get value ' + val + ' does not equal testValue of ' + testValue); - } else { - t.pass('FileKeyValueStore read and write test: Successfully retrieved value'); - } - - // now test getValue() when the underlying directory get deleted - fs.removeSync(keyValStorePath); - return store1.getValue(testKey); - }, - // Log the rejection reason - (reason) => { - t.fail('FileKeyValueStore read and write test: Failed getValue, reason: ' + reason); - t.end(); - } - ).then( - // Log the fulfillment value - (val) => { - if (val === null) { - t.pass('FileKeyValueStore error check tests: Delete store & getValue test. getValue() returns null as expected'); - } else { - t.fail('FileKeyValueStore error check tests: Delete store & getValue test. getValue() should not have returned value: ' + val); - } - - return new FileKeyValueStore({path: keyValStorePath}); - }, - (reason) => { - t.fail('FileKeyValueStore error check tests: Delete store & getValue test. getValue caught unexpected error: ' + reason); - } - ) - .then( - (store) => { - // now test setValue() when the underlying directory get deleted - fs.removeSync(keyValStorePath); - return store.setValue(testKey, testValue); - }) - .then( - () => { - t.fail('FileKeyValueStore error check tests: Delete store & setValue test. setValue() should have failed.'); - t.end(); - }, - (reason) => { - t.pass('FileKeyValueStore error check tests: Delete store & setValue test. setValue() failed as expected: ' + reason); - t.end(); - }) - .catch( - (err) => { - t.fail('Failed with unexpected error: ' + err.stack ? err.stack : err); - t.end(); - }); -}); - -test('\n\n** FileKeyValueStore error check tests **\n\n', (t) => { - - t.throws( - () => { - new FileKeyValueStore(); - }, - /^Error: Must provide the path/, - 'FileKeyValueStore error check tests: new FileKeyValueStore with no options should throw ' + - '"Must provide the path to the directory to hold files for the store."' - ); - - t.throws( - () => { - new FileKeyValueStore({dir: keyValStorePath}); - }, - /^Error: Must provide the path/, - 'FileKeyValueStore error check tests: new FileKeyValueStore with no options.path should throw ' + - '"Must provide the path to the directory to hold files for the store."' - ); - - t.end(); -}); diff --git a/test/unit/network-config.js b/test/unit/network-config.js index a4ff73a868..a0aa6c47fc 100644 --- a/test/unit/network-config.js +++ b/test/unit/network-config.js @@ -27,18 +27,18 @@ const NetworkConfig = require('fabric-client/lib/impl/NetworkConfig_1_0.js'); const testutil = require('./util.js'); -test('\n\n ** configuration testing **\n\n', (t) => { +test('\n\n ** configuration testing **\n\n', async (t) => { testutil.resetDefaults(); t.doesNotThrow( - () => { + async () => { const config_loc = path.resolve('test/fixtures/profiles/network.yaml'); const file_data = fs.readFileSync(config_loc); const network_data = yaml.safeLoad(file_data); - const client = Client.loadFromConfig(network_data); + const client = await Client.loadFromConfig(network_data); client.setCryptoSuite(Client.newCryptoSuite()); client.setUserContext(new User('testUser'), true); - client.loadFromConfig(network_data); + await client.loadFromConfig(network_data); const channel = client.getChannel('mychannel'); t.equals(channel.getPeers()[0].getUrl(), 'grpcs://localhost:7051', ' check to see that the peer has been added to the channel'); t.equals(channel.getPeers()[1].getUrl(), 'grpcs://localhost:8051', ' check to see that the peer has been added to the channel'); @@ -63,16 +63,16 @@ test('\n\n ** configuration testing **\n\n', (t) => { } }; - t.doesNotThrow( - () => { - const client = new Client(); - client.setCryptoSuite(Client.newCryptoSuite()); - client.setUserContext(new User('testUser'), true); - client._network_config = new NetworkConfig(network_config, client); - const channel = client.newChannel('mychannel'); - t.equals('mychannel', channel.getName(), 'Channel should be named'); - }, - 'Should be able to instantiate a new instance of "Channel" with an empty channel definition in the common connection profile' + await testutil.tapeAsyncNoThrow(t, async () => { + const client = new Client(); + const suite = await Client.newCryptoSuite(); + client.setCryptoSuite(suite); + client.setUserContext(new User('testUser'), true); + client._network_config = new NetworkConfig(network_config, client); + const channel = client.newChannel('mychannel'); + t.equals('mychannel', channel.getName(), 'Channel should be named'); + }, + 'able to instantiate a new instance of "Channel" with an empty channel definition in the common connection profile' ); network_config.channels = { @@ -90,23 +90,23 @@ test('\n\n ** configuration testing **\n\n', (t) => { } }; - t.doesNotThrow( - () => { - const client = new Client(); - client.setCryptoSuite(Client.newCryptoSuite()); - client.setUserContext(new User('testUser'), true); - client._network_config = new NetworkConfig(network_config, client); - const channel = client.getChannel('mychannel'); - t.equals('mychannel', channel.getName(), 'Channel should be named'); - const orderer = channel.getOrderers()[0]; - if (orderer instanceof Orderer) { - t.pass('Successfully got an orderer'); - } else { - t.fail('Failed to get an orderer'); - } - t.equals('orderer0', orderer.getName(), 'Orderer should be named'); - }, - 'Should be able to instantiate a new instance of "Channel" with only orderer definition in the common connection profile' + await testutil.tapeAsyncNoThrow(t, async () => { + const client = new Client(); + const suite = await Client.newCryptoSuite(); + client.setCryptoSuite(suite); + client.setUserContext(new User('testUser'), true); + client._network_config = new NetworkConfig(network_config, client); + const channel = client.getChannel('mychannel'); + t.equals('mychannel', channel.getName(), 'Channel should be named'); + const orderer = channel.getOrderers()[0]; + if (orderer instanceof Orderer) { + t.pass('Successfully got an orderer'); + } else { + t.fail('Failed to get an orderer'); + } + t.equals('orderer0', orderer.getName(), 'Orderer should be named'); + }, + 'able to instantiate a new instance of "Channel" with only orderer definition in the common connection profile' ); network_config.channels = { @@ -122,23 +122,23 @@ test('\n\n ** configuration testing **\n\n', (t) => { }; network_config.orgainizations = {'org1': {}}; - t.doesNotThrow( - () => { - const client = new Client(); - client.setCryptoSuite(Client.newCryptoSuite()); - client.setUserContext(new User('testUser'), true); - client._network_config = new NetworkConfig(network_config, client); - const channel = client.getChannel('mychannel'); - t.equals('mychannel', channel.getName(), 'Channel should be named'); - t.equals(channel.getPeers().length, 0, 'Peers should be empty'); - const orderer = channel.getOrderers()[0]; - if (orderer instanceof Orderer) { - t.pass('Successfully got an orderer'); - } else { - t.fail('Failed to get an orderer'); - } - }, - 'Should be able to instantiate a new instance of "Channel" with org that does not exist in the common connection profile' + await testutil.tapeAsyncNoThrow(t, async () => { + const client = new Client(); + const suite = await Client.newCryptoSuite(); + client.setCryptoSuite(suite); + await client.setUserContext(new User('testUser'), true); + client._network_config = new NetworkConfig(network_config, client); + const channel = client.getChannel('mychannel'); + t.equals('mychannel', channel.getName(), 'Channel should be named'); + t.equals(channel.getPeers().length, 0, 'Peers should be empty'); + const orderer = channel.getOrderers()[0]; + if (orderer instanceof Orderer) { + t.pass('Successfully got an orderer'); + } else { + t.fail('Failed to get an orderer'); + } + }, + 'able to instantiate a new instance of "Channel" with org that does not exist in the common connection profile' ); network_config.organizations = { @@ -154,23 +154,23 @@ test('\n\n ** configuration testing **\n\n', (t) => { } }; - t.doesNotThrow( - () => { - const client = new Client(); - client.setCryptoSuite(Client.newCryptoSuite()); - client.setUserContext(new User('testUser'), true); - client._network_config = new NetworkConfig(network_config, client); - const channel = client.getChannel('mychannel'); - t.equals('mychannel', channel.getName(), 'Channel should be named'); - t.equals(channel.getPeers().length, 0, 'Peers should be empty'); - const orderer = channel.getOrderers()[0]; - if (orderer instanceof Orderer) { - t.pass('Successfully got an orderer'); - } else { - t.fail('Failed to get an orderer'); - } - }, - 'Should be able to instantiate a new instance of "Channel" with a peer in the org that does not exist in the common connection profile' + await testutil.tapeAsyncNoThrow(t, async () => { + const client = new Client(); + const suite = await Client.newCryptoSuite(); + client.setCryptoSuite(suite); + await client.setUserContext(new User('testUser'), true); + client._network_config = new NetworkConfig(network_config, client); + const channel = client.getChannel('mychannel'); + t.equals('mychannel', channel.getName(), 'Channel should be named'); + t.equals(channel.getPeers().length, 0, 'Peers should be empty'); + const orderer = channel.getOrderers()[0]; + if (orderer instanceof Orderer) { + t.pass('Successfully got an orderer'); + } else { + t.fail('Failed to get an orderer'); + } + }, + 'able to instantiate a new instance of "Channel" with a peer in the org that does not exist in the common connection profile' ); network_config.peers = { @@ -218,84 +218,85 @@ test('\n\n ** configuration testing **\n\n', (t) => { registrar: {enrollId: 'admin2', enrollSecret: 'adminpw2'} } }; - t.doesNotThrow( - () => { - const client = new Client(); - client.setCryptoSuite(Client.newCryptoSuite()); - client.setUserContext(new User('testUser'), true); - client._network_config = new NetworkConfig(network_config, client); - const channel = client.getChannel('mychannel'); - t.equals('mychannel', channel.getName(), 'Channel should be named'); - t.equals(channel.getPeers().length, 4, 'Peers should be four'); - const peer = channel.getPeers()[0]; - if (peer && peer.constructor && peer.constructor.name === 'ChannelPeer') { - t.pass('Successfully got a channel peer'); - } else { - t.fail('Failed to get a channel peer'); - } - }, - 'Should be able to instantiate a new instance of "Channel" with orderer, org and peer defined in the common connection profile' + + await testutil.tapeAsyncNoThrow(t, async () => { + const client = new Client(); + const suite = await Client.newCryptoSuite(); + client.setCryptoSuite(suite); + client.setUserContext(new User('testUser'), true); + client._network_config = new NetworkConfig(network_config, client); + const channel = client.getChannel('mychannel'); + t.equals('mychannel', channel.getName(), 'Channel should be named'); + t.equals(channel.getPeers().length, 4, 'Peers should be four'); + const peer = channel.getPeers()[0]; + if (peer && peer.constructor && peer.constructor.name === 'ChannelPeer') { + t.pass('Successfully got a channel peer'); + } else { + t.fail('Failed to get a channel peer'); + } + }, + 'able to instantiate a new instance of "Channel" with orderer, org and peer defined in the common connection profile' ); const peer1 = new Peer('grpcs://localhost:9999', {pem: '-----BEGIN CERTIFICATE-----MIIB8TCC5l-----END CERTIFICATE-----'}); - t.doesNotThrow( - () => { - const client = new Client(); - client.setCryptoSuite(Client.newCryptoSuite()); - client.setUserContext(new User('testUser'), true); - client._network_config = new NetworkConfig(network_config, client); + await testutil.tapeAsyncNoThrow(t, async () => { + const client = new Client(); + const suite = await Client.newCryptoSuite(); + client.setCryptoSuite(suite); + client.setUserContext(new User('testUser'), true); + client._network_config = new NetworkConfig(network_config, client); - let targets = client.getTargetPeers('peer1', client); - if (Array.isArray(targets)) { - t.pass('targets is an array'); - } else { - t.fail('targets is not an array'); - } - if (targets[0] instanceof Peer) { - t.pass('targets has a peer '); - } else { - t.fail('targets does not have a peer'); - } + let targets = client.getTargetPeers('peer1', client); + if (Array.isArray(targets)) { + t.pass('targets is an array'); + } else { + t.fail('targets is not an array'); + } + if (targets[0] instanceof Peer) { + t.pass('targets has a peer '); + } else { + t.fail('targets does not have a peer'); + } - targets = client.getTargetPeers(['peer1'], client); - if (Array.isArray(targets)) { - t.pass('targets is an array'); - } else { - t.fail('targets is not an array'); - } - if (targets[0] instanceof Peer) { - t.pass('targets has a peer '); - } else { - t.fail('targets does not have a peer'); - } + targets = client.getTargetPeers(['peer1'], client); + if (Array.isArray(targets)) { + t.pass('targets is an array'); + } else { + t.fail('targets is not an array'); + } + if (targets[0] instanceof Peer) { + t.pass('targets has a peer '); + } else { + t.fail('targets does not have a peer'); + } - targets = client.getTargetPeers(peer1, client); - if (Array.isArray(targets)) { - t.pass('targets is an array'); - } else { - t.fail('targets is not an array'); - } - if (targets[0] instanceof Peer) { - t.pass('targets has a peer '); - } else { - t.fail('targets does not have a peer'); - } + targets = client.getTargetPeers(peer1, client); + if (Array.isArray(targets)) { + t.pass('targets is an array'); + } else { + t.fail('targets is not an array'); + } + if (targets[0] instanceof Peer) { + t.pass('targets has a peer '); + } else { + t.fail('targets does not have a peer'); + } - targets = client.getTargetPeers([peer1], client); - if (Array.isArray(targets)) { - t.pass('targets is an array'); - } else { - t.fail('targets is not an array'); - } - if (targets[0] instanceof Peer) { - t.pass('targets has a peer '); - } else { - t.fail('targets does not have a peer'); - } + targets = client.getTargetPeers([peer1], client); + if (Array.isArray(targets)) { + t.pass('targets is an array'); + } else { + t.fail('targets is not an array'); + } + if (targets[0] instanceof Peer) { + t.pass('targets has a peer '); + } else { + t.fail('targets does not have a peer'); + } - }, - 'Should be able to get targets for peer' + }, + 'able to get targets for peer' ); t.doesNotThrow( @@ -358,37 +359,37 @@ test('\n\n ** configuration testing **\n\n', (t) => { 'Should not get an error when working with credentialStore settings' ); - t.doesNotThrow( - () => { - const client = new Client(); - client.setCryptoSuite(Client.newCryptoSuite()); - client.setUserContext(new User('testUser'), true); - client._network_config = new NetworkConfig(network_config, client); - const organizations = client._network_config.getOrganizations(); - if (Array.isArray(organizations)) { - t.pass('organizations is an array'); - } else { - t.fail('organizations is not an array'); - } - if (organizations[0] instanceof Organization) { - t.pass('organizations has a organization '); - } else { - t.fail('organizations does not have a organization'); - } + await testutil.tapeAsyncNoThrow(t, async () => { + const client = new Client(); + const suite = await Client.newCryptoSuite(); + client.setCryptoSuite(suite); + client.setUserContext(new User('testUser'), true); + client._network_config = new NetworkConfig(network_config, client); + const organizations = client._network_config.getOrganizations(); + if (Array.isArray(organizations)) { + t.pass('organizations is an array'); + } else { + t.fail('organizations is not an array'); + } + if (organizations[0] instanceof Organization) { + t.pass('organizations has a organization '); + } else { + t.fail('organizations does not have a organization'); + } - let organization = client._network_config.getOrganization(organizations[0].getName()); - let ca = organization.getCertificateAuthorities()[0]; - t.equals('ca1', ca.getName(), 'check the ca name'); + let organization = client._network_config.getOrganization(organizations[0].getName()); + let ca = organization.getCertificateAuthorities()[0]; + t.equals('ca1', ca.getName(), 'check the ca name'); - organization = client._network_config.getOrganization(organizations[1].getName()); - ca = organization.getCertificateAuthorities()[0]; - t.equals('ca2', ca.getName(), 'check the ca name'); + organization = client._network_config.getOrganization(organizations[1].getName()); + ca = organization.getCertificateAuthorities()[0]; + t.equals('ca2', ca.getName(), 'check the ca name'); - organization = client._network_config.getOrganizationByMspId(organizations[0].getMspid()); - ca = organization.getCertificateAuthorities()[0]; - t.equals('ca1', ca.getName(), 'check the ca name'); - }, - 'Should be able to get organizations' + organization = client._network_config.getOrganizationByMspId(organizations[0].getMspid()); + ca = organization.getCertificateAuthorities()[0]; + t.equals('ca1', ca.getName(), 'check the ca name'); + }, + 'able to get organizations' ); network_config.channels = { @@ -403,33 +404,33 @@ test('\n\n ** configuration testing **\n\n', (t) => { } }; - t.doesNotThrow( - () => { - let client = Client.loadFromConfig(network_config); - client.setCryptoSuite(Client.newCryptoSuite()); - client.setUserContext(new User('testUser'), true); - let channel = client.getChannel('mychannel'); - - checkTarget(channel._getTargetForQuery(), '7053', 'finding a default ledger query', t); - checkTarget(channel._getTargets(null, 'ledgerQuery'), '7053', 'finding a default ledger query', t); - checkTarget(channel._getTargetForQuery('peer1'), '7051', 'finding a string target for ledger query', t); - checkTarget(channel._getTargets('peer1'), '7051', 'finding a string target', t); - checkTarget(channel._getTargetForQuery(peer1), '9999', 'should get back the same target if a good peer', t); - checkTarget(channel._getTargets(peer1), '9999', 'should get back the same target if a good peer', t); - client = new Client(); - channel = client.newChannel('mychannel'); - channel.addPeer(peer1); - checkTarget(channel._getTargetForQuery(), '9999', 'finding a default ledger query without networkconfig', t); - checkTarget(channel._getTargets(undefined, 'ANY'), '9999', 'finding a default targets without networkconfig', t); - }, - 'Should be able to run channel target methods' + await testutil.tapeAsyncNoThrow(t, async () => { + let client = await Client.loadFromConfig(network_config); + const suite = await Client.newCryptoSuite(); + client.setCryptoSuite(suite); + client.setUserContext(new User('testUser'), true); + let channel = client.getChannel('mychannel'); + + checkTarget(channel._getTargetForQuery(), '7053', 'finding a default ledger query', t); + checkTarget(channel._getTargets(null, 'ledgerQuery'), '7053', 'finding a default ledger query', t); + checkTarget(channel._getTargetForQuery('peer1'), '7051', 'finding a string target for ledger query', t); + checkTarget(channel._getTargets('peer1'), '7051', 'finding a string target', t); + checkTarget(channel._getTargetForQuery(peer1), '9999', 'should get back the same target if a good peer', t); + checkTarget(channel._getTargets(peer1), '9999', 'should get back the same target if a good peer', t); + client = new Client(); + channel = client.newChannel('mychannel'); + channel.addPeer(peer1); + checkTarget(channel._getTargetForQuery(), '9999', 'finding a default ledger query without networkconfig', t); + checkTarget(channel._getTargets(undefined, 'ANY'), '9999', 'finding a default targets without networkconfig', t); + }, + 'able to run channel target methods' ); - - t.throws( - () => { - const client = Client.loadFromConfig(network_config); - client.setCryptoSuite(Client.newCryptoSuite()); + await testutil.tapeAsyncThrow(t, + async () => { + const client = await Client.loadFromConfig(network_config); + const suite = await Client.newCryptoSuite(); + client.setCryptoSuite(suite); client.setUserContext(new User('testUser'), true); const channel = client.getChannel('mychannel'); channel._getTargetForQuery(['peer1']); @@ -438,10 +439,11 @@ test('\n\n ** configuration testing **\n\n', (t) => { 'Should get an error back when passing an array' ); - t.throws( - () => { - const client = Client.loadFromConfig(network_config); - client.setCryptoSuite(Client.newCryptoSuite()); + await testutil.tapeAsyncThrow(t, + async () => { + const client = await Client.loadFromConfig(network_config); + const suite = await Client.newCryptoSuite(); + client.setCryptoSuite(suite); client.setUserContext(new User('testUser'), true); const channel = client.getChannel('mychannel'); channel._getTargets('bad'); @@ -480,63 +482,63 @@ test('\n\n ** configuration testing **\n\n', (t) => { 'Should get an error when the request orderer is not defined and the channel does not have any orderers' ); - t.doesNotThrow( - () => { - const client = new Client(); - client.setCryptoSuite(Client.newCryptoSuite()); - client.setUserContext(new User('testUser'), true); - client._network_config = new NetworkConfig(network_config, client); + await testutil.tapeAsyncNoThrow(t, async () => { + const client = new Client(); + const suite = await Client.newCryptoSuite(); + client.setCryptoSuite(suite); + client.setUserContext(new User('testUser'), true); + client._network_config = new NetworkConfig(network_config, client); - let orderer = client.getTargetOrderer('orderer0'); - if (orderer instanceof Orderer) { - t.pass('orderer has a orderer '); - } else { - t.fail('orderer does not have a orderer'); - } + let orderer = client.getTargetOrderer('orderer0'); + if (orderer instanceof Orderer) { + t.pass('orderer has a orderer '); + } else { + t.fail('orderer does not have a orderer'); + } - const orderer1 = new Orderer('grpcs://localhost:9999', {pem: '-----BEGIN CERTIFICATE-----MIIB8TCC5l-----END CERTIFICATE-----'}); + const orderer1 = new Orderer('grpcs://localhost:9999', {pem: '-----BEGIN CERTIFICATE-----MIIB8TCC5l-----END CERTIFICATE-----'}); - orderer = client.getTargetOrderer(orderer1); - if (orderer instanceof Orderer) { - t.pass('orderer has a orderer '); - } else { - t.fail('orderer does not have a orderer'); - } + orderer = client.getTargetOrderer(orderer1); + if (orderer instanceof Orderer) { + t.pass('orderer has a orderer '); + } else { + t.fail('orderer does not have a orderer'); + } - orderer = client.getTargetOrderer(null, null, 'mychannel'); - if (orderer instanceof Orderer) { - t.pass('orderer has a orderer '); - } else { - t.fail('orderer does not have a orderer'); - } + orderer = client.getTargetOrderer(null, null, 'mychannel'); + if (orderer instanceof Orderer) { + t.pass('orderer has a orderer '); + } else { + t.fail('orderer does not have a orderer'); + } - orderer = client.getTargetOrderer(null, [orderer1]); - if (orderer instanceof Orderer) { - t.pass('orderer has a orderer '); - } else { - t.fail('orderer does not have a orderer'); - } - }, - 'Should be able to get orderer' + orderer = client.getTargetOrderer(null, [orderer1]); + if (orderer instanceof Orderer) { + t.pass('orderer has a orderer '); + } else { + t.fail('orderer does not have a orderer'); + } + }, + 'able to get orderer' ); - t.doesNotThrow( - () => { - const client = Client.loadFromConfig(network_config); - client.setCryptoSuite(Client.newCryptoSuite()); - client.setUserContext(new User('testUser'), true); - client.getChannel('mychannel'); - client.loadFromConfig({ - version: '1.0.0', - channels: { - 'otherchannel': { - orderers: ['orderer0'] - } + await testutil.tapeAsyncNoThrow(t, async () => { + const client = await Client.loadFromConfig(network_config); + const suite = await Client.newCryptoSuite(); + client.setCryptoSuite(suite); + client.setUserContext(new User('testUser'), true); + client.getChannel('mychannel'); + await client.loadFromConfig({ + version: '1.0.0', + channels: { + 'otherchannel': { + orderers: ['orderer0'] } - }); - client.getChannel('otherchannel'); - }, - 'Should be able to load additional configurations' + } + }); + client.getChannel('otherchannel'); + }, + 'able to load additional configurations' ); t.doesNotThrow( @@ -590,40 +592,36 @@ test('\n\n ** configuration testing **\n\n', (t) => { } ); - t.throws( - () => { - const client = new Client(); - client._setAdminFromConfig(); - }, - /No common connection profile has been loaded/, - 'Should get an error No common connection profile has been loaded' + await testutil.tapeAsyncThrow(t, async () => { + const client = new Client(); + await client._setAdminFromConfig(); + }, + /No common connection profile has been loaded/, + 'Should get an error No common connection profile has been loaded' ); - t.throws( - () => { - const client = new Client(); - client.setAdminSigningIdentity(); - }, - /Invalid parameter. Must have a valid private key./, - 'Should get an error Invalid parameter. Must have a valid private key.' + await testutil.tapeAsyncThrow(t, async () => { + const client = new Client(); + await client.setAdminSigningIdentity(); + }, + /Invalid parameter. Must have a valid private key./, + 'Should get an error Invalid parameter. Must have a valid private key.' ); - t.throws( - () => { - const client = new Client(); - client.setAdminSigningIdentity('privateKey'); - }, - /Invalid parameter. Must have a valid certificate./, - 'Should get an error Invalid parameter. Must have a valid certificate.' + await testutil.tapeAsyncThrow(t, async () => { + const client = new Client(); + await client.setAdminSigningIdentity('privateKey'); + }, + /Invalid parameter. Must have a valid certificate./, + 'Should get an error Invalid parameter. Must have a valid certificate.' ); - t.throws( - () => { - const client = new Client(); - client.setAdminSigningIdentity('privateKey', 'cert'); - }, - /Invalid parameter. Must have a valid mspid./, - 'Should get an error Invalid parameter. Must have a valid mspid.' + await testutil.tapeAsyncThrow(t, async () => { + const client = new Client(); + await client.setAdminSigningIdentity('privateKey', 'cert'); + }, + /Invalid parameter. Must have a valid mspid./, + 'Should get an error Invalid parameter. Must have a valid mspid.' ); t.throws( @@ -636,11 +634,11 @@ test('\n\n ** configuration testing **\n\n', (t) => { ); try { - const client = Client.loadFromConfig('test/fixtures/profiles/network.yaml'); + const client = await Client.loadFromConfig('test/fixtures/profiles/network.yaml'); t.pass('Successfully loaded a common connection profile'); t.pass('Should be able to try to load an admin from the config'); - client.loadFromConfig('test/fixtures/profiles/org1.yaml'); + await client.loadFromConfig('test/fixtures/profiles/org1.yaml'); t.pass('Should be able to load an additional config ...this one has the client section'); t.pass('Should be able to try to load an admin from the config'); // check to see if we were able to load a setting from the yaml into @@ -650,14 +648,14 @@ test('\n\n ** configuration testing **\n\n', (t) => { t.fail('Fail - caught an error while trying to load a config and run the set admin'); } - const clientp1 = Client.loadFromConfig('test/fixtures/profiles/network.yaml'); + const clientp1 = await Client.loadFromConfig('test/fixtures/profiles/network.yaml'); t.pass('Successfully loaded a common connection profile'); - clientp1.loadFromConfig('test/fixtures/profiles/org1.yaml'); + await clientp1.loadFromConfig('test/fixtures/profiles/org1.yaml'); t.pass('Should be able to load an additional config ...this one has the client section'); - const p1 = clientp1.initCredentialStores().then(() => { + const p1 = clientp1.initCredentialStores().then(async() => { t.pass('Should be able to load the stores from the config'); - clientp1._setAdminFromConfig(); + await clientp1._setAdminFromConfig(); t.pass('Should be able to load an admin from the config'); clientp1._getSigningIdentity(true); t.pass('Should be able to get the loaded admin identity'); @@ -665,9 +663,9 @@ test('\n\n ** configuration testing **\n\n', (t) => { t.fail(util.format('Should not get an error when doing get signer: %O', err)); }); - const clientp2 = Client.loadFromConfig('test/fixtures/profiles/network.yaml'); + const clientp2 = await Client.loadFromConfig('test/fixtures/profiles/network.yaml'); t.pass('Successfully loaded a common connection profile'); - clientp2.loadFromConfig('test/fixtures/profiles/org1.yaml'); + await clientp2.loadFromConfig('test/fixtures/profiles/org1.yaml'); t.pass('Should be able to load an additional config ...this one has the client section'); const p2 = clientp2.initCredentialStores().then(() => { t.pass('Should be able to load the stores from the config'); @@ -684,9 +682,9 @@ test('\n\n ** configuration testing **\n\n', (t) => { } }); - const clientp3 = Client.loadFromConfig('test/fixtures/profiles/network.yaml'); + const clientp3 = await Client.loadFromConfig('test/fixtures/profiles/network.yaml'); t.pass('Successfully loaded a common connection profile'); - clientp3.loadFromConfig('test/fixtures/profiles/org1.yaml'); + await clientp3.loadFromConfig('test/fixtures/profiles/org1.yaml'); t.pass('Should be able to load an additional config ...this one has the client section'); const p3 = clientp3.initCredentialStores().then(() => { t.pass('Should be able to load the stores from the config'); @@ -703,7 +701,7 @@ test('\n\n ** configuration testing **\n\n', (t) => { } }); - const clientp4 = Client.loadFromConfig('test/fixtures/profiles/network.yaml'); + const clientp4 = await Client.loadFromConfig('test/fixtures/profiles/network.yaml'); t.pass('Successfully loaded a common connection profile'); const p4 = clientp4._setUserFromConfig({username: 'username', password: 'password'}).then(() => { t.fail('Should not be able to load an user based on the config'); @@ -732,18 +730,19 @@ test('\n\n ** configuration testing **\n\n', (t) => { t.end(); }); -test('\n\n ** channel testing **\n\n', (t) => { +test('\n\n ** channel testing **\n\n', async (t) => { const client = new Client(); - client.setCryptoSuite(Client.newCryptoSuite()); + const suite = await Client.newCryptoSuite(); + client.setCryptoSuite(suite); client.setUserContext(new User('testUser'), true); - client.loadFromConfig('test/fixtures/profiles/network.yaml'); + await client.loadFromConfig('test/fixtures/profiles/network.yaml'); const channel = client.getChannel('mychannel'); let channelEventHubs = channel.getChannelEventHubsForOrg('bad'); t.equals(channelEventHubs.length, 0, 'Checking that we got the correct number of peers in the list'); channelEventHubs = channel.getChannelEventHubsForOrg('Org2MSP'); t.equals(channelEventHubs[0].getName(), 'peer0.org2.example.com', 'Checking that we got the correct peer in the list'); - client.loadFromConfig('test/fixtures/profiles/org1.yaml'); + await client.loadFromConfig('test/fixtures/profiles/org1.yaml'); channelEventHubs = channel.getChannelEventHubsForOrg(); t.equals(channelEventHubs[0].getName(), 'peer0.org1.example.com', 'Checking that we got the correct peer in the list'); diff --git a/test/unit/util.js b/test/unit/util.js index dac3f1b8e2..c28017c5f0 100644 --- a/test/unit/util.js +++ b/test/unit/util.js @@ -317,12 +317,12 @@ module.exports.getClientForOrg = async function(t, org) { // this network config does not have the client information, we will // load that later so that we can switch this client to be in a different // organization - const client = Client.loadFromConfig('test/fixtures/profiles/network-ad.yaml'); + const client = await Client.loadFromConfig('test/fixtures/profiles/network-ad.yaml'); t.pass('Successfully loaded a common connection profile'); // load the client information for this organization // this file only has the client section - client.loadFromConfig('test/fixtures/profiles/' + org + '.yaml'); + await client.loadFromConfig('test/fixtures/profiles/' + org + '.yaml'); t.pass('Successfully loaded client section of network config for organization:' + org); if (client._adminSigningIdentity) { t.pass('Successfully assigned an admin idenity to this client'); @@ -757,6 +757,7 @@ module.exports.queryClientAsAdmin = async function(t, client, channel, peer) { module.exports.sleep = function(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }; + exports.tapeAsyncThrow = async (t, asyncFun, regx, message) => { try { await asyncFun(); @@ -770,3 +771,12 @@ exports.tapeAsyncThrow = async (t, asyncFun, regx, message) => { } } }; + +exports.tapeAsyncNoThrow = async (t, asyncFun, msg) => { + try { + await asyncFun(); + t.pass('Successfully ' + msg); + } catch (err) { + t.fail('Should be ' + msg); + } +};