diff --git a/packages/composer-admin/lib/adminconnection.js b/packages/composer-admin/lib/adminconnection.js index 3321674139..b32b2061d4 100644 --- a/packages/composer-admin/lib/adminconnection.js +++ b/packages/composer-admin/lib/adminconnection.js @@ -21,6 +21,7 @@ const fs = require('fs'); const FSConnectionProfileStore = require('composer-common').FSConnectionProfileStore; const Logger = require('composer-common').Logger; const Util = require('composer-common').Util; +const uuid = require('uuid'); const LOG = Logger.getLog('AdminConnection'); @@ -392,7 +393,9 @@ class AdminConnection { const method = 'activate'; LOG.entry(method); const json = { - $class: 'org.hyperledger.composer.system.ActivateCurrentIdentity' + $class: 'org.hyperledger.composer.system.ActivateCurrentIdentity', + transactionId: uuid.v4(), + timestamp: new Date().toISOString() }; return Util.invokeChainCode(this.securityContext, 'submitTransaction', ['default', JSON.stringify(json)]) .then(() => { diff --git a/packages/composer-admin/test/adminconnection.js b/packages/composer-admin/test/adminconnection.js index c642c64737..31d46854f9 100644 --- a/packages/composer-admin/test/adminconnection.js +++ b/packages/composer-admin/test/adminconnection.js @@ -22,6 +22,7 @@ const ConnectionManager = require('composer-common').ConnectionManager; const FSConnectionProfileStore = require('composer-common').FSConnectionProfileStore; const SecurityContext = require('composer-common').SecurityContext; const Util = require('composer-common').Util; +const uuid = require('uuid'); const version = require('../package.json').version; @@ -38,6 +39,7 @@ describe('AdminConnection', () => { let mockSecurityContext; let adminConnection; let sandbox; + let clock; const config = { @@ -84,11 +86,13 @@ describe('AdminConnection', () => { sinon.stub(adminConnection.connectionProfileStore, 'delete').withArgs('testprofile').resolves(); delete process.env.COMPOSER_CONFIG; sandbox = sinon.sandbox.create(); + clock = sinon.useFakeTimers(); }); afterEach(() => { delete process.env.COMPOSER_CONFIG; sandbox.restore(); + clock.restore(); }); describe('#constructor', () => { @@ -348,13 +352,14 @@ describe('AdminConnection', () => { mockConnection.ping.onSecondCall().resolves(Buffer.from(JSON.stringify({ version: version }))); - mockConnection.invokeChainCode.withArgs(mockSecurityContext, 'submitTransaction', ['default', '{"$class":"org.hyperledger.composer.system.ActivateCurrentIdentity"}']).resolves(); + sandbox.stub(uuid, 'v4').returns('c89291eb-969f-4b04-b653-82deb5ee0ba1'); + mockConnection.invokeChainCode.resolves(); adminConnection.connection = mockConnection; return adminConnection.ping() .then(() => { sinon.assert.calledTwice(mockConnection.ping); sinon.assert.calledOnce(mockConnection.invokeChainCode); - sinon.assert.calledWith(mockConnection.invokeChainCode, mockSecurityContext, 'submitTransaction', ['default', '{"$class":"org.hyperledger.composer.system.ActivateCurrentIdentity"}']); + sinon.assert.calledWith(mockConnection.invokeChainCode, mockSecurityContext, 'submitTransaction', ['default', '{"$class":"org.hyperledger.composer.system.ActivateCurrentIdentity","transactionId":"c89291eb-969f-4b04-b653-82deb5ee0ba1","timestamp":"1970-01-01T00:00:00.000Z"}']); }); }); @@ -391,7 +396,8 @@ describe('AdminConnection', () => { it('should perform a security check', () => { sandbox.stub(Util, 'securityCheck'); - mockConnection.invokeChainCode.withArgs(mockSecurityContext, 'submitTransaction', ['default', '{"$class":"org.hyperledger.composer.system.ActivateCurrentIdentity"}']).resolves(); + sandbox.stub(uuid, 'v4').returns('c89291eb-969f-4b04-b653-82deb5ee0ba1'); + mockConnection.invokeChainCode.resolves(); adminConnection.connection = mockConnection; return adminConnection.activate() .then(() => { @@ -400,12 +406,13 @@ describe('AdminConnection', () => { }); it('should submit a request to the chaincode for activation', () => { - mockConnection.invokeChainCode.withArgs(mockSecurityContext, 'submitTransaction', ['default', '{"$class":"org.hyperledger.composer.system.ActivateCurrentIdentity"}']).resolves(); + sandbox.stub(uuid, 'v4').returns('c89291eb-969f-4b04-b653-82deb5ee0ba1'); + mockConnection.invokeChainCode.resolves(); adminConnection.connection = mockConnection; return adminConnection.activate() .then(() => { sinon.assert.calledOnce(mockConnection.invokeChainCode); - sinon.assert.calledWith(mockConnection.invokeChainCode, mockSecurityContext, 'submitTransaction', ['default', '{"$class":"org.hyperledger.composer.system.ActivateCurrentIdentity"}']); + sinon.assert.calledWith(mockConnection.invokeChainCode, mockSecurityContext, 'submitTransaction', ['default', '{"$class":"org.hyperledger.composer.system.ActivateCurrentIdentity","transactionId":"c89291eb-969f-4b04-b653-82deb5ee0ba1","timestamp":"1970-01-01T00:00:00.000Z"}']); }); }); diff --git a/packages/composer-client/lib/businessnetworkconnection.js b/packages/composer-client/lib/businessnetworkconnection.js index 7779b77c8c..a06c91e9d0 100644 --- a/packages/composer-client/lib/businessnetworkconnection.js +++ b/packages/composer-client/lib/businessnetworkconnection.js @@ -601,7 +601,9 @@ class BusinessNetworkConnection extends EventEmitter { const method = 'activate'; LOG.entry(method); const json = { - $class: 'org.hyperledger.composer.system.ActivateCurrentIdentity' + $class: 'org.hyperledger.composer.system.ActivateCurrentIdentity', + transactionId: uuid.v4(), + timestamp: new Date().toISOString() }; return Util.invokeChainCode(this.securityContext, 'submitTransaction', ['default', JSON.stringify(json)]) .then(() => { diff --git a/packages/composer-client/test/businessnetworkconnection.js b/packages/composer-client/test/businessnetworkconnection.js index 8fb6bd6c7f..0440cfa8fe 100644 --- a/packages/composer-client/test/businessnetworkconnection.js +++ b/packages/composer-client/test/businessnetworkconnection.js @@ -45,6 +45,7 @@ const sinon = require('sinon'); describe('BusinessNetworkConnection', () => { let sandbox; + let clock; let businessNetworkConnection; let mockSecurityContext; let mockConnection; @@ -58,6 +59,7 @@ describe('BusinessNetworkConnection', () => { beforeEach(() => { sandbox = sinon.sandbox.create(); + clock = sinon.useFakeTimers(); mockSecurityContext = sinon.createStubInstance(SecurityContext); mockConnection = sinon.createStubInstance(Connection); mockSecurityContext.getConnection.returns(mockConnection); @@ -96,6 +98,7 @@ describe('BusinessNetworkConnection', () => { afterEach(() => { sandbox.restore(); + clock.restore(); delete process.env.COMPOSER_CONFIG; }); @@ -857,13 +860,14 @@ describe('BusinessNetworkConnection', () => { mockConnection.ping.onSecondCall().resolves(Buffer.from(JSON.stringify({ version: version }))); - mockConnection.invokeChainCode.withArgs(mockSecurityContext, 'submitTransaction', ['default', '{"$class":"org.hyperledger.composer.system.ActivateCurrentIdentity"}']).resolves(); + sandbox.stub(uuid, 'v4').returns('c89291eb-969f-4b04-b653-82deb5ee0ba1'); + mockConnection.invokeChainCode.resolves(); businessNetworkConnection.connection = mockConnection; return businessNetworkConnection.ping() .then(() => { sinon.assert.calledTwice(mockConnection.ping); sinon.assert.calledOnce(mockConnection.invokeChainCode); - sinon.assert.calledWith(mockConnection.invokeChainCode, mockSecurityContext, 'submitTransaction', ['default', '{"$class":"org.hyperledger.composer.system.ActivateCurrentIdentity"}']); + sinon.assert.calledWith(mockConnection.invokeChainCode, mockSecurityContext, 'submitTransaction', ['default', '{"$class":"org.hyperledger.composer.system.ActivateCurrentIdentity","transactionId":"c89291eb-969f-4b04-b653-82deb5ee0ba1","timestamp":"1970-01-01T00:00:00.000Z"}']); }); }); @@ -900,7 +904,7 @@ describe('BusinessNetworkConnection', () => { it('should perform a security check', () => { sandbox.stub(Util, 'securityCheck'); - mockConnection.invokeChainCode.withArgs(mockSecurityContext, 'submitTransaction', ['default', '{"$class":"org.hyperledger.composer.system.ActivateCurrentIdentity"}']).resolves(); + mockConnection.invokeChainCode.resolves(); businessNetworkConnection.connection = mockConnection; return businessNetworkConnection.activate() .then(() => { @@ -909,12 +913,13 @@ describe('BusinessNetworkConnection', () => { }); it('should submit a request to the chaincode for activation', () => { - mockConnection.invokeChainCode.withArgs(mockSecurityContext, 'submitTransaction', ['default', '{"$class":"org.hyperledger.composer.system.ActivateCurrentIdentity"}']).resolves(); + sandbox.stub(uuid, 'v4').returns('c89291eb-969f-4b04-b653-82deb5ee0ba1'); + mockConnection.invokeChainCode.resolves(); businessNetworkConnection.connection = mockConnection; return businessNetworkConnection.activate() .then(() => { sinon.assert.calledOnce(mockConnection.invokeChainCode); - sinon.assert.calledWith(mockConnection.invokeChainCode, mockSecurityContext, 'submitTransaction', ['default', '{"$class":"org.hyperledger.composer.system.ActivateCurrentIdentity"}']); + sinon.assert.calledWith(mockConnection.invokeChainCode, mockSecurityContext, 'submitTransaction', ['default', '{"$class":"org.hyperledger.composer.system.ActivateCurrentIdentity","transactionId":"c89291eb-969f-4b04-b653-82deb5ee0ba1","timestamp":"1970-01-01T00:00:00.000Z"}']); }); }); diff --git a/packages/composer-connector-hlfv1/lib/hlfconnectionmanager.js b/packages/composer-connector-hlfv1/lib/hlfconnectionmanager.js index cb930abc18..a747d2efea 100644 --- a/packages/composer-connector-hlfv1/lib/hlfconnectionmanager.js +++ b/packages/composer-connector-hlfv1/lib/hlfconnectionmanager.js @@ -174,29 +174,27 @@ class HLFConnectionManager extends ConnectionManager { * @param {string} peer The peer URL. * @param {number} timeout the request timeout * @param {string} globalCert if provided use this unless cert is provided + * @param {array} peers Array to store any created peers * @param {array} eventHubDefs Array to store any created event hubs - * @return {Peer} A new peer. */ - static parsePeer(peer, timeout, globalCert, eventHubDefs) { + static parsePeer(peer, timeout, globalCert, peers, eventHubDefs) { const method = 'parsePeer'; const opts = HLFConnectionManager._createOpts(timeout, peer.cert, peer.hostnameOverride, globalCert); if (!peer.requestURL && !peer.eventURL) { throw new Error('peer incorrectly defined'); } - if (peer.requestURL && !peer.eventURL) { - throw new Error(`The peer at requestURL ${peer.requestURL} has no eventURL defined`); + + if (peer.requestURL) { + const hfc_peer = HLFConnectionManager.createPeer(peer.requestURL, opts); + LOG.debug(method, 'Adding peer URL', peer.requestURL); + peers.push(hfc_peer); } - if (!peer.requestURL && peer.eventURL) { - throw new Error(`The peer at eventURL ${peer.eventURL} has no requestURL defined`); + if (peer.eventURL) { + const eventHub = HLFConnectionManager.createEventHubDefinition(peer.eventURL, opts); + LOG.debug(method, 'Setting event hub URL', peer.eventURL); + eventHubDefs.push(eventHub); } - - const hfc_peer = HLFConnectionManager.createPeer(peer.requestURL, opts); - // extract and save event hub definitions for later. - const eventHub = HLFConnectionManager.createEventHubDefinition(peer.eventURL, opts); - LOG.debug(method, 'Setting event hub URL', peer.eventURL); - eventHubDefs.push(eventHub); - return hfc_peer; } /** @@ -450,13 +448,27 @@ class HLFConnectionManager extends ConnectionManager { channel.addOrderer(HLFConnectionManager.parseOrderer(orderer, connectOptions.timeout, connectOptions.globalCert)); }); + // Parse all of the peers. + let peers = []; let eventHubDefs = []; - // Load all of the peers into the client. connectOptions.peers.forEach((peer) => { + HLFConnectionManager.parsePeer(peer, connectOptions.timeout, connectOptions.globalCert, peers, eventHubDefs); + }); + + // Check for at least one peer and at least one event hub. + if (peers.length === 0) { + throw new Error('You must specify at least one peer with a valid requestURL for submitting transactions'); + } else if (eventHubDefs.length === 0) { + throw new Error('You must specify at least one peer with a valid eventURL for receiving events'); + } + + // Load all of the peers into the client. + peers.forEach((peer) => { LOG.debug(method, 'Adding peer URL', peer); - channel.addPeer(HLFConnectionManager.parsePeer(peer, connectOptions.timeout, connectOptions.globalCert, eventHubDefs)); + channel.addPeer(peer); }); + // Set up the wallet. return this._setupWallet(client, wallet, connectOptions.keyValStore) .then(() => { diff --git a/packages/composer-connector-hlfv1/test/hlfconnectionmanager.js b/packages/composer-connector-hlfv1/test/hlfconnectionmanager.js index 625a7c0a97..f2a536d8c9 100644 --- a/packages/composer-connector-hlfv1/test/hlfconnectionmanager.js +++ b/packages/composer-connector-hlfv1/test/hlfconnectionmanager.js @@ -249,33 +249,22 @@ describe('HLFConnectionManager', () => { }); it('should throw if peer definition is incorrect', () => { - let peer = { - requestURL: 'grpc://localhost:7051' - }; - (() => { - HLFConnectionManager.parsePeer(peer); - }).should.throw('The peer at requestURL grpc://localhost:7051 has no eventURL defined'); - - peer = { - eventURL: 'grpc://localhost:7053' - }; - (() => { - HLFConnectionManager.parsePeer(peer); - }).should.throw('The peer at eventURL grpc://localhost:7053 has no requestURL defined'); - peer = { + peerDef = { rurl: 'grpc://localhost:7051', eURL: 'grpc://localhost:7051' }; (() => { - HLFConnectionManager.parsePeer(peer); + HLFConnectionManager.parsePeer(peerDef); }).should.throw('peer incorrectly defined'); }); it('should create a new peer and eventHub with no tls', () => { - let eventHubDefs = []; - HLFConnectionManager.parsePeer(peerDef, 10, undefined, eventHubDefs); + let peers = [], eventHubDefs = []; + HLFConnectionManager.parsePeer(peerDef, 10, undefined, peers, eventHubDefs); sinon.assert.calledOnce(HLFConnectionManager.createPeer); sinon.assert.calledWith(HLFConnectionManager.createPeer, peerDef.requestURL); + peers.should.have.lengthOf(1); + peers[0].should.equal(mockPeer); eventHubDefs.length.should.equal(1); eventHubDefs.should.be.an.instanceof(Object); eventHubDefs[0].should.deep.equal({ @@ -290,14 +279,16 @@ describe('HLFConnectionManager', () => { peerDef.cert = embeddedCert; peerDef.hostnameOverride = 'localhost'; - let eventHubDefs = []; - HLFConnectionManager.parsePeer(peerDef, 9, undefined, eventHubDefs); + let peers = [], eventHubDefs = []; + HLFConnectionManager.parsePeer(peerDef, 9, undefined, peers, eventHubDefs); sinon.assert.calledOnce(HLFConnectionManager.createPeer); sinon.assert.calledWith(HLFConnectionManager.createPeer, peerDef.requestURL, { 'request-timeout': 9000, pem: embeddedCert, 'ssl-target-name-override': peerDef.hostnameOverride }); + peers.should.have.lengthOf(1); + peers[0].should.equal(mockPeer); eventHubDefs.length.should.equal(1); eventHubDefs.should.be.an.instanceof(Object); eventHubDefs[0].should.deep.equal({ @@ -315,15 +306,16 @@ describe('HLFConnectionManager', () => { peerDef.cert = '/some/path/to/some/file'; - let eventHubDefs = []; - HLFConnectionManager.parsePeer(peerDef, 7, undefined, eventHubDefs); + let peers = [], eventHubDefs = []; + HLFConnectionManager.parsePeer(peerDef, 7, undefined, peers, eventHubDefs); sinon.assert.calledOnce(HLFConnectionManager.createPeer); sinon.assert.calledWith(fs.readFileSync, peerDef.cert); sinon.assert.calledWith(HLFConnectionManager.createPeer, peerDef.requestURL, { 'request-timeout': 7000, pem: 'acert' }); - + peers.should.have.lengthOf(1); + peers[0].should.equal(mockPeer); eventHubDefs.length.should.equal(1); eventHubDefs.should.be.an.instanceof(Object); eventHubDefs[0].should.deep.equal({ @@ -338,14 +330,16 @@ describe('HLFConnectionManager', () => { it('should create a new peer with tls and embedded certificate from global cert', () => { peerDef.hostnameOverride = 'localhost'; - let eventHubDefs = []; - HLFConnectionManager.parsePeer(peerDef, 9, embeddedCert, eventHubDefs); + let peers = [], eventHubDefs = []; + HLFConnectionManager.parsePeer(peerDef, 9, embeddedCert, peers, eventHubDefs); sinon.assert.calledOnce(HLFConnectionManager.createPeer); sinon.assert.calledWith(HLFConnectionManager.createPeer, peerDef.requestURL, { 'request-timeout': 9000, pem: embeddedCert, 'ssl-target-name-override': peerDef.hostnameOverride }); + peers.should.have.lengthOf(1); + peers[0].should.equal(mockPeer); eventHubDefs.length.should.equal(1); eventHubDefs.should.be.an.instanceof(Object); eventHubDefs[0].should.deep.equal({ @@ -360,15 +354,16 @@ describe('HLFConnectionManager', () => { it('should create a new peer with tls and global file system certificate', () => { sandbox.stub(fs,'readFileSync').returns(new Buffer('acert')); - let eventHubDefs = []; - HLFConnectionManager.parsePeer(peerDef, 7, '/some/path/to/some/file', eventHubDefs); + let peers = [], eventHubDefs = []; + HLFConnectionManager.parsePeer(peerDef, 7, '/some/path/to/some/file', peers, eventHubDefs); sinon.assert.calledOnce(HLFConnectionManager.createPeer); sinon.assert.calledWith(fs.readFileSync, '/some/path/to/some/file'); sinon.assert.calledWith(HLFConnectionManager.createPeer, peerDef.requestURL, { 'request-timeout': 7000, pem: 'acert' }); - + peers.should.have.lengthOf(1); + peers[0].should.equal(mockPeer); eventHubDefs.length.should.equal(1); eventHubDefs.should.be.an.instanceof(Object); eventHubDefs[0].should.deep.equal({ @@ -384,14 +379,16 @@ describe('HLFConnectionManager', () => { peerDef.hostnameOverride = 'localhost'; peerDef.cert = overrideCert; - let eventHubDefs = []; - HLFConnectionManager.parsePeer(peerDef, 9, embeddedCert, eventHubDefs); + let peers = [], eventHubDefs = []; + HLFConnectionManager.parsePeer(peerDef, 9, embeddedCert, peers, eventHubDefs); sinon.assert.calledOnce(HLFConnectionManager.createPeer); sinon.assert.calledWith(HLFConnectionManager.createPeer, peerDef.requestURL, { 'request-timeout': 9000, pem: overrideCert, 'ssl-target-name-override': peerDef.hostnameOverride }); + peers.should.have.lengthOf(1); + peers[0].should.equal(mockPeer); eventHubDefs.length.should.equal(1); eventHubDefs.should.be.an.instanceof(Object); eventHubDefs[0].should.deep.equal({ @@ -592,25 +589,43 @@ describe('HLFConnectionManager', () => { it('should throw if peer configuration not correct', () => { connectOptions.peers = [{ - requestURL: 'grpc://localhost:7051' + rurl: 'grpc://localhost:7051', + eURL: 'grpc://localhost:7053' }]; (() => { connectionManager.connect('hlfabric1', 'org-acme-biznet', connectOptions); - }).should.throw('The peer at requestURL grpc://localhost:7051 has no eventURL defined'); + }).should.throw('peer incorrectly defined'); + }); + + it('should permit one peer with a requestURL and one peer with an eventURL', () => { + connectOptions.peers = [{ + requestURL: 'grpc://localhost:7051' + }, { + eventURL: 'grpc://localhost:7053' + }]; + return connectionManager.connect('hlfabric1', 'org-acme-biznet', connectOptions); + }); + it('should throw if no peers with a requestURL are specified', () => { connectOptions.peers = [{ eventURL: 'grpc://localhost:7053' + }, { + eventURL: 'grpc://localhost:8053' }]; (() => { connectionManager.connect('hlfabric1', 'org-acme-biznet', connectOptions); - }).should.throw('The peer at eventURL grpc://localhost:7053 has no requestURL defined'); + }).should.throw(/You must specify at least one peer with a valid requestURL for submitting transactions/); + }); + + it('should throw if no peers with an eventURL are specified', () => { connectOptions.peers = [{ - rurl: 'grpc://localhost:7051', - eURL: 'grpc://localhost:7051' + requestURL: 'grpc://localhost:7051' + }, { + requestURL: 'grpc://localhost:8051' }]; (() => { connectionManager.connect('hlfabric1', 'org-acme-biznet', connectOptions); - }).should.throw('peer incorrectly defined'); + }).should.throw(/You must specify at least one peer with a valid eventURL for receiving events/); }); it('should throw if ca is not specified', () => { diff --git a/packages/composer-runtime/lib/api.js b/packages/composer-runtime/lib/api.js index b0f1f0e7c3..18f2e5fb63 100644 --- a/packages/composer-runtime/lib/api.js +++ b/packages/composer-runtime/lib/api.js @@ -245,6 +245,7 @@ class Api { const method = 'emit'; LOG.entry(method, event); event.setIdentifier(context.getTransaction().getIdentifier() + '#' + context.getEventNumber()); + event.timestamp = context.getTransaction().timestamp; let serializedEvent = serializer.toJSON(event, { convertResourcesToRelationships: true }); diff --git a/packages/composer-runtime/test/api.js b/packages/composer-runtime/test/api.js index a7e25ce92d..96e9185946 100644 --- a/packages/composer-runtime/test/api.js +++ b/packages/composer-runtime/test/api.js @@ -23,6 +23,7 @@ const DataService = require('../lib/dataservice'); const EventService = require('../lib/eventservice'); const HTTPService = require('../lib/httpservice'); const Factory = require('../lib/api/factory'); +const ModelManager = require('composer-common').ModelManager; const ParticipantRegistry = require('../lib/api/participantregistry'); const Query = require('../lib/api/query'); const realFactory = require('composer-common').Factory; @@ -42,8 +43,9 @@ const sinon = require('sinon'); describe('Api', () => { let mockContext; - let mockFactory; - let mockSerializer; + let modelManager; + let factory; + let serializer; let mockParticipant; let mockRegistryManager; let mockEventService; @@ -55,10 +57,23 @@ describe('Api', () => { beforeEach(() => { mockContext = sinon.createStubInstance(Context); - mockFactory = sinon.createStubInstance(realFactory); - mockContext.getFactory.returns(mockFactory); - mockSerializer = sinon.createStubInstance(realSerializer); - mockContext.getSerializer.returns(mockSerializer); + modelManager = new ModelManager(); + modelManager.addModelFile(` + namespace org.doge + transaction DogeTransaction { + } + event DogeEvent { + }`); + modelManager.addModelFile(` + namespace org.acme.sample + asset SampleAsset identified by assetId { + o String assetId + o String value + }`); + factory = new realFactory(modelManager); + mockContext.getFactory.returns(factory); + serializer = new realSerializer(factory, modelManager); + mockContext.getSerializer.returns(serializer); mockParticipant = sinon.createStubInstance(Resource); mockContext.getParticipant.returns(mockParticipant); mockRegistryManager = sinon.createStubInstance(RegistryManager); @@ -157,57 +172,55 @@ describe('Api', () => { }); describe('#post', () => { - let mockTransaction; + let transaction; + let spy; beforeEach(() => { - mockTransaction = sinon.createStubInstance(Resource); - mockTransaction.getFullyQualifiedType.returns('much.wow'); + transaction = factory.newResource('org.doge', 'DogeTransaction', 'doge1'); + transaction.timestamp = new Date(545184000000); mockHTTPService.post.resolves({foo : 'bar'}); - mockSerializer.toJSON.withArgs(mockTransaction).onFirstCall().returns({ - $class: 'org.doge.DogeTransaction', - assetId: 'doge1' - }); + spy = sinon.spy(serializer, 'toJSON'); }); it('should make an POST request using the HTTP service', () => { - return api.post('url', mockTransaction) + return api.post('url', transaction) .should.eventually.have.property('foo') .then(() => { - sinon.assert.calledOnce(mockSerializer.toJSON); - sinon.assert.calledWith(mockSerializer.toJSON, mockTransaction, { convertResourcesToRelationships: true, permitResourcesForRelationships: true }); + sinon.assert.calledWith(spy, transaction, { convertResourcesToRelationships: true, permitResourcesForRelationships: true, validate: true }); sinon.assert.calledOnce(mockHTTPService.post); sinon.assert.calledWith(mockHTTPService.post, 'url', { $class: 'org.doge.DogeTransaction', - assetId: 'doge1' + timestamp: '1987-04-12T00:00:00.000Z', + transactionId: 'doge1' }); }); }); }); describe('#emit', () => { - let mockTransaction; - let mockEvent; + let transaction; + let event; + let spy; beforeEach(() => { - mockTransaction = sinon.createStubInstance(Resource); - mockEvent = sinon.createStubInstance(Resource); - mockTransaction.getIdentifier.returns('much.wow'); - mockContext.getTransaction.returns(mockTransaction); + transaction = factory.newResource('org.doge', 'DogeTransaction', 'doge1'); + transaction.timestamp = new Date(545184000000); + event = factory.newResource('org.doge', 'DogeEvent', 'doge1'); + event.timestamp = new Date(545184000000); + mockContext.getTransaction.returns(transaction); mockContext.getEventNumber.returns(0); - mockSerializer.toJSON.withArgs(mockEvent).onFirstCall().returns({ - $class: 'org.doge.DogeEvent', - assetId: 'doge1' - }); + spy = sinon.spy(serializer, 'toJSON'); }); it('should emit the event using the event service', () => { - api.emit(mockEvent); - sinon.assert.calledOnce(mockSerializer.toJSON); - sinon.assert.calledWith(mockSerializer.toJSON, mockEvent, { convertResourcesToRelationships: true }); + api.emit(event); + sinon.assert.calledOnce(spy); + sinon.assert.calledWith(spy, event, { convertResourcesToRelationships: true, validate: true }); sinon.assert.calledOnce(mockEventService.emit); sinon.assert.calledWith(mockEventService.emit, { $class: 'org.doge.DogeEvent', - assetId: 'doge1' + timestamp: '1987-04-12T00:00:00.000Z', + eventId: 'doge1#0' }); }); }); @@ -231,30 +244,26 @@ describe('Api', () => { param1: 'hello 1', param2: 100.56 }; - let mockResources; + let resources; beforeEach(() => { const mockObjects = []; - mockResources = []; + resources = []; for (let i = 0; i < 5; i++) { const object = { $registryType: 'Asset', $registryId: 'org.acme.sample.SampleAsset', $class: 'org.acme.sample.SampleAsset', + assetId: 'ASSET_' + i, value: 'the value ' + i }; mockObjects.push(object); - const filteredObject = { - $class: 'org.acme.sample.SampleAsset', - value: 'the value ' + i - }; - const resource = sinon.createStubInstance(Resource); - resource.$identifier = 'id' + i; - mockSerializer.fromJSON.withArgs(filteredObject).returns(resource); + const resource = factory.newResource('org.acme.sample', 'SampleAsset', 'ASSET_' + i); + resource.value = 'the value ' + i; if (i % 2 === 0) { mockAccessController.check.withArgs(resource, 'READ').rejects(new Error('access denied')); } else { - mockResources.push(resource); + resources.push(resource); } } mockCompiledQueryBundle.execute.withArgs(mockDataService, queryID).resolves(mockObjects); @@ -271,22 +280,20 @@ describe('Api', () => { it('should perform a query using a named query', () => { return api.query(queryID) - .should.eventually.be.deep.equal(mockResources) + .should.eventually.be.deep.equal(resources) .then(() => { sinon.assert.calledOnce(mockCompiledQueryBundle.execute); sinon.assert.calledWith(mockCompiledQueryBundle.execute, mockDataService, queryID); - sinon.assert.callCount(mockSerializer.fromJSON, 5); sinon.assert.callCount(mockAccessController.check, 5); }); }); it('should perform a query using a named query and parameters', () => { return api.query(queryID, queryParams) - .should.eventually.be.deep.equal(mockResources) + .should.eventually.be.deep.equal(resources) .then(() => { sinon.assert.calledOnce(mockCompiledQueryBundle.execute); sinon.assert.calledWith(mockCompiledQueryBundle.execute, mockDataService, queryID, queryParams); - sinon.assert.callCount(mockSerializer.fromJSON, 5); sinon.assert.callCount(mockAccessController.check, 5); }); }); @@ -294,11 +301,10 @@ describe('Api', () => { it('should perform a query using a built query', () => { const query = new Query(queryHash); return api.query(query) - .should.eventually.be.deep.equal(mockResources) + .should.eventually.be.deep.equal(resources) .then(() => { sinon.assert.calledOnce(mockCompiledQueryBundle.execute); sinon.assert.calledWith(mockCompiledQueryBundle.execute, mockDataService, queryHash); - sinon.assert.callCount(mockSerializer.fromJSON, 5); sinon.assert.callCount(mockAccessController.check, 5); }); }); @@ -306,11 +312,10 @@ describe('Api', () => { it('should perform a query using a built query and parameters', () => { const query = new Query(queryHash); return api.query(query, queryParams) - .should.eventually.be.deep.equal(mockResources) + .should.eventually.be.deep.equal(resources) .then(() => { sinon.assert.calledOnce(mockCompiledQueryBundle.execute); sinon.assert.calledWith(mockCompiledQueryBundle.execute, mockDataService, queryHash, queryParams); - sinon.assert.callCount(mockSerializer.fromJSON, 5); sinon.assert.callCount(mockAccessController.check, 5); }); }); diff --git a/packages/composer-systests/hlfv1/composer-channel.tx b/packages/composer-systests/hlfv1/composer-channel.tx index d17515c471..b5ad16ab87 100644 Binary files a/packages/composer-systests/hlfv1/composer-channel.tx and b/packages/composer-systests/hlfv1/composer-channel.tx differ diff --git a/packages/composer-systests/hlfv1/composer-genesis.block b/packages/composer-systests/hlfv1/composer-genesis.block index cc6638b647..b7480ade59 100644 Binary files a/packages/composer-systests/hlfv1/composer-genesis.block and b/packages/composer-systests/hlfv1/composer-genesis.block differ diff --git a/packages/composer-systests/hlfv1/configtx.yaml b/packages/composer-systests/hlfv1/configtx.yaml index a4b49a3a61..2f1ede7f27 100644 --- a/packages/composer-systests/hlfv1/configtx.yaml +++ b/packages/composer-systests/hlfv1/configtx.yaml @@ -23,12 +23,14 @@ Profiles: ComposerConsortium: Organizations: - *Org1 + - *Org2 ComposerChannel: Consortium: ComposerConsortium Application: <<: *ApplicationDefaults Organizations: - *Org1 + - *Org2 ################################################################################ # @@ -76,6 +78,26 @@ Organizations: - Host: peer0.org1.example.com Port: 7051 + - &Org2 + # DefaultOrg defines the organization which is used in the sampleconfig + # of the fabric.git development environment + Name: Org2 + + # ID to load the MSP definition as + ID: Org2MSP + + MSPDir: crypto-config/peerOrganizations/org2.example.com/msp + + # turn off security for the peer + AdminPrincipal: Role.ADMIN + + AnchorPeers: + # AnchorPeers defines the location of peers which can be used + # for cross org gossip communication. Note, this value is only + # encoded in the genesis block in the Application section context + - Host: peer0.org2.example.com + Port: 7051 + ################################################################################ # # SECTION: Orderer diff --git a/packages/composer-systests/hlfv1/crypto-config.tar.gz b/packages/composer-systests/hlfv1/crypto-config.tar.gz index dc0a1568d8..baed7a68d3 100644 Binary files a/packages/composer-systests/hlfv1/crypto-config.tar.gz and b/packages/composer-systests/hlfv1/crypto-config.tar.gz differ diff --git a/packages/composer-systests/hlfv1/crypto-config.yaml b/packages/composer-systests/hlfv1/crypto-config.yaml index c6413cb5dc..a526982fb3 100644 --- a/packages/composer-systests/hlfv1/crypto-config.yaml +++ b/packages/composer-systests/hlfv1/crypto-config.yaml @@ -70,3 +70,52 @@ PeerOrgs: # --------------------------------------------------------------------------- Users: Count: 10 + # --------------------------------------------------------------------------- + # Org2 + # --------------------------------------------------------------------------- + - Name: Org2 + Domain: org2.example.com + # --------------------------------------------------------------------------- + # "Specs" + # --------------------------------------------------------------------------- + # Uncomment this section to enable the explicit definition of hosts in your + # configuration. Most users will want to use Template, below + # + # Specs is an array of Spec entries. Each Spec entry consists of two fields: + # - Hostname: (Required) The desired hostname, sans the domain. + # - CommonName: (Optional) Specifies the template or explicit override for + # the CN. By default, this is the template: + # + # "{{.Hostname}}.{{.Domain}}" + # + # which obtains its values from the Spec.Hostname and + # Org.Domain, respectively. + # --------------------------------------------------------------------------- + # Specs: + # - Hostname: foo # implicitly "foo.org1.example.com" + # CommonName: foo27.org5.example.com # overrides Hostname-based FQDN set above + # - Hostname: bar + # - Hostname: baz + # --------------------------------------------------------------------------- + # "Template" + # --------------------------------------------------------------------------- + # Allows for the definition of 1 or more hosts that are created sequentially + # from a template. By default, this looks like "peer%d" from 0 to Count-1. + # You may override the number of nodes (Count), the starting index (Start) + # or the template used to construct the name (Hostname). + # + # Note: Template and Specs are not mutually exclusive. You may define both + # sections and the aggregate nodes will be created for you. Take care with + # name collisions + # --------------------------------------------------------------------------- + Template: + Count: 1 + # Start: 5 + # Hostname: {{.Prefix}}{{.Index}} # default + # --------------------------------------------------------------------------- + # "Users" + # --------------------------------------------------------------------------- + # Count: The number of user accounts _in addition_ to Admin + # --------------------------------------------------------------------------- + Users: + Count: 10 diff --git a/packages/composer-systests/hlfv1/docker-compose.tls.yml b/packages/composer-systests/hlfv1/docker-compose.tls.yml index c0895a64e1..0d03257348 100644 --- a/packages/composer-systests/hlfv1/docker-compose.tls.yml +++ b/packages/composer-systests/hlfv1/docker-compose.tls.yml @@ -8,15 +8,31 @@ services: - FABRIC_CA_SERVER_CA_NAME=ca.org1.example.com - FABRIC_CA_SERVER_TLS_ENABLED=true - FABRIC_CA_SERVER_TLS_CERTFILE=/etc/hyperledger/fabric-ca-server-tls/tlsca.org1.example.com-cert.pem - - FABRIC_CA_SERVER_TLS_KEYFILE=/etc/hyperledger/fabric-ca-server-tls/0ee6960861c967d4869ae0c4892a10fdfe03e44f5bf514f5f85796bb99f5f556_sk + - FABRIC_CA_SERVER_TLS_KEYFILE=/etc/hyperledger/fabric-ca-server-tls/key.pem ports: - "7054:7054" - command: sh -c 'fabric-ca-server start -b admin:adminpw -d --ca.certfile /etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem --ca.keyfile /etc/hyperledger/fabric-ca-server-config/d4968fe9364eaf1fcd938ade6253554619caff5300d4ee6d2661e3093c58e340_sk' + command: sh -c 'fabric-ca-server start -b admin:adminpw -d --ca.certfile /etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem --ca.keyfile /etc/hyperledger/fabric-ca-server-config/key.pem' volumes: - ./crypto-config/peerOrganizations/org1.example.com/ca/:/etc/hyperledger/fabric-ca-server-config - ./crypto-config/peerOrganizations/org1.example.com/tlsca/:/etc/hyperledger/fabric-ca-server-tls container_name: ca.org1.example.com + ca.org2.example.com: + image: hyperledger/fabric-ca:x86_64-1.0.0 + environment: + - FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server + - FABRIC_CA_SERVER_CA_NAME=ca.org2.example.com + - FABRIC_CA_SERVER_TLS_ENABLED=true + - FABRIC_CA_SERVER_TLS_CERTFILE=/etc/hyperledger/fabric-ca-server-tls/tlsca.org2.example.com-cert.pem + - FABRIC_CA_SERVER_TLS_KEYFILE=/etc/hyperledger/fabric-ca-server-tls/key.pem + ports: + - "8054:7054" + command: sh -c 'fabric-ca-server start -b admin:adminpw -d --ca.certfile /etc/hyperledger/fabric-ca-server-config/ca.org2.example.com-cert.pem --ca.keyfile /etc/hyperledger/fabric-ca-server-config/key.pem' + volumes: + - ./crypto-config/peerOrganizations/org2.example.com/ca/:/etc/hyperledger/fabric-ca-server-config + - ./crypto-config/peerOrganizations/org2.example.com/tlsca/:/etc/hyperledger/fabric-ca-server-tls + container_name: ca.org2.example.com + orderer.example.com: container_name: orderer.example.com image: hyperledger/fabric-orderer:x86_64-1.0.0 @@ -30,7 +46,6 @@ services: - ORDERER_GENERAL_TLS_ENABLED=true - ORDERER_GENERAL_TLS_PRIVATEKEY=/etc/hyperledger/msp/orderer/tls/server.key - ORDERER_GENERAL_TLS_CERTIFICATE=/etc/hyperledger/msp/orderer/tls/server.crt -# - ORDERER_GENERAL_TLS_ROOTCAS=[/etc/hyperledger/msp/orderer/cacerts/example.com-cert.pem, /etc/hyperledger/msp/peerOrg1/cacerts/org1.example.com-cert.pem] - ORDERER_GENERAL_TLS_ROOTCAS=[/etc/hyperledger/msp/orderer/tls/ca.crt] working_dir: /opt/gopath/src/github.com/hyperledger/fabric command: orderer @@ -39,8 +54,6 @@ services: volumes: - ./:/etc/hyperledger/configtx - ./crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/:/etc/hyperledger/msp/orderer -# - ./crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/:/etc/hyperledger/msp/peerOrg1 - peer0.org1.example.com: container_name: peer0.org1.example.com @@ -59,7 +72,7 @@ services: - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/peer/tls/server.crt - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/peer/tls/ca.crt - CORE_LEDGER_STATE_STATEDATABASE=CouchDB - - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb:5984 + - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb.org1.example.com:5984 working_dir: /opt/gopath/src/github.com/hyperledger/fabric command: peer node start --peer-defaultchain=false ports: @@ -74,14 +87,56 @@ services: - ./crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls:/etc/hyperledger/peer/tls depends_on: - orderer.example.com - - couchdb + - couchdb.org1.example.com - couchdb: - container_name: couchdb + couchdb.org1.example.com: + container_name: couchdb.org1.example.com image: hyperledger/fabric-couchdb:x86_64-1.0.0 ports: - 5984:5984 environment: DB_URL: http://localhost:5984/member_db + peer0.org2.example.com: + container_name: peer0.org2.example.com + image: hyperledger/fabric-peer:x86_64-1.0.0 + environment: + - CORE_LOGGING_PEER=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:7051 + - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=hlfv1_default + - CORE_PEER_LOCALMSPID=Org2MSP + - CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/peer/msp + - CORE_PEER_TLS_ENABLED=true + - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/peer/tls/server.key + - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/peer/tls/server.crt + - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/peer/tls/ca.crt + - CORE_LEDGER_STATE_STATEDATABASE=CouchDB + - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb.org2.example.com:5984 + working_dir: /opt/gopath/src/github.com/hyperledger/fabric + command: peer node start --peer-defaultchain=false + ports: + - 8051:7051 + - 8053:7053 + volumes: + - /var/run/:/host/var/run/ + - ./:/etc/hyperledger/configtx + - ./crypto-config/peerOrganizations/org2.example.com/users:/etc/hyperledger/msp/users + - ./crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls:/etc/hyperledger/orderer/tls + - ./crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/msp:/etc/hyperledger/peer/msp + - ./crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls:/etc/hyperledger/peer/tls + depends_on: + - orderer.example.com + - couchdb.org2.example.com + + couchdb.org2.example.com: + container_name: couchdb.org2.example.com + image: hyperledger/fabric-couchdb:x86_64-1.0.0 + ports: + - 6984:5984 + environment: + DB_URL: http://localhost:5984/member_db + diff --git a/packages/composer-systests/hlfv1/docker-compose.yml b/packages/composer-systests/hlfv1/docker-compose.yml index 5858d521aa..510bdfd71b 100644 --- a/packages/composer-systests/hlfv1/docker-compose.yml +++ b/packages/composer-systests/hlfv1/docker-compose.yml @@ -8,11 +8,23 @@ services: - FABRIC_CA_SERVER_CA_NAME=ca.org1.example.com ports: - "7054:7054" - command: sh -c 'fabric-ca-server start --ca.certfile /etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem --ca.keyfile /etc/hyperledger/fabric-ca-server-config/d4968fe9364eaf1fcd938ade6253554619caff5300d4ee6d2661e3093c58e340_sk -b admin:adminpw -d' + command: sh -c 'fabric-ca-server start --ca.certfile /etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem --ca.keyfile /etc/hyperledger/fabric-ca-server-config/key.pem -b admin:adminpw -d' volumes: - ./crypto-config/peerOrganizations/org1.example.com/ca/:/etc/hyperledger/fabric-ca-server-config container_name: ca.org1.example.com + ca.org2.example.com: + image: hyperledger/fabric-ca:x86_64-1.0.0 + environment: + - FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server + - FABRIC_CA_SERVER_CA_NAME=ca.org2.example.com + ports: + - "8054:7054" + command: sh -c 'fabric-ca-server start --ca.certfile /etc/hyperledger/fabric-ca-server-config/ca.org2.example.com-cert.pem --ca.keyfile /etc/hyperledger/fabric-ca-server-config/key.pem -b admin:adminpw -d' + volumes: + - ./crypto-config/peerOrganizations/org2.example.com/ca/:/etc/hyperledger/fabric-ca-server-config + container_name: ca.org2.example.com + orderer.example.com: container_name: orderer.example.com image: hyperledger/fabric-orderer:x86_64-1.0.0 @@ -44,7 +56,7 @@ services: - CORE_PEER_LOCALMSPID=Org1MSP - CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/peer/msp - CORE_LEDGER_STATE_STATEDATABASE=CouchDB - - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb:5984 + - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb.org1.example.com:5984 working_dir: /opt/gopath/src/github.com/hyperledger/fabric command: peer node start --peer-defaultchain=false ports: @@ -58,14 +70,51 @@ services: depends_on: - orderer.example.com - - couchdb + - couchdb.org1.example.com - couchdb: - container_name: couchdb + couchdb.org1.example.com: + container_name: couchdb.org1.example.com image: hyperledger/fabric-couchdb:x86_64-1.0.0 ports: - 5984:5984 environment: DB_URL: http://localhost:5984/member_db + peer0.org2.example.com: + container_name: peer0.org2.example.com + image: hyperledger/fabric-peer:x86_64-1.0.0 + environment: + - CORE_LOGGING_PEER=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:7051 + - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=hlfv1_default + - CORE_PEER_LOCALMSPID=Org2MSP + - CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/peer/msp + - CORE_LEDGER_STATE_STATEDATABASE=CouchDB + - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb.org2.example.com:5984 + working_dir: /opt/gopath/src/github.com/hyperledger/fabric + command: peer node start --peer-defaultchain=false + ports: + - 8051:7051 + - 8053:7053 + volumes: + - /var/run/:/host/var/run/ + - ./:/etc/hyperledger/configtx + - ./crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/msp:/etc/hyperledger/peer/msp + - ./crypto-config/peerOrganizations/org2.example.com/users:/etc/hyperledger/msp/users + + depends_on: + - orderer.example.com + - couchdb.org2.example.com + + couchdb.org2.example.com: + container_name: couchdb.org2.example.com + image: hyperledger/fabric-couchdb:x86_64-1.0.0 + ports: + - 6984:5984 + environment: + DB_URL: http://localhost:5984/member_db + diff --git a/packages/composer-systests/scripts/run-system-tests.sh b/packages/composer-systests/scripts/run-system-tests.sh index a9062c70a0..8b86a915dd 100755 --- a/packages/composer-systests/scripts/run-system-tests.sh +++ b/packages/composer-systests/scripts/run-system-tests.sh @@ -29,8 +29,8 @@ for SYSTEST in $(echo ${SYSTEST} | tr "," " "); do export COMPOSER_TIMEOUT_SECS=500 # Delete any existing configuration. - rm -rf ${HOME}/.composer-connection-profiles/composer-systests - rm -rf ${HOME}/.composer-credentials/composer-systests + rm -rf ${HOME}/.composer-connection-profiles/composer-systests* + rm -rf ${HOME}/.composer-credentials/composer-systests* # Pull any required Docker images. if [ "${SYSTEST}" = "hlf" ]; then @@ -52,11 +52,16 @@ for SYSTEST in $(echo ${SYSTEST} | tr "," " "); do docker pull hyperledger/fabric-ccenv:x86_64-1.0.0 docker pull hyperledger/fabric-orderer:x86_64-1.0.0 docker pull hyperledger/fabric-couchdb:x86_64-1.0.0 - if [ ! -d ./hlfv1/crypto-config ]; then - cd hlfv1 - tar -xvf crypto-config.tar.gz - cd .. + if [ -d ./hlfv1/crypto-config ]; then + rm -rf ./hlfv1/crypto-config fi + cd hlfv1 + tar -xvf crypto-config.tar.gz + # Rename all the keys so we don't have to maintain them in the code. + for KEY in $(find crypto-config -type f -name "*_sk"); do + KEY_DIR=$(dirname ${KEY}) + mv ${KEY} ${KEY_DIR}/key.pem + done fi # Start any required Docker images. @@ -75,11 +80,19 @@ for SYSTEST in $(echo ${SYSTEST} | tr "," " "); do docker exec -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org1.example.com/msp" peer0.org1.example.com peer channel create -o orderer.example.com:7050 -c composerchannel -f /etc/hyperledger/configtx/composer-channel.tx --tls true --cafile /etc/hyperledger/orderer/tls/ca.crt # Join peer0 to the channel. docker exec -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org1.example.com/msp" peer0.org1.example.com peer channel join -b composerchannel.block --tls true --cafile /etc/hyperledger/orderer/tls/ca.crt + # Fetch the channel. + docker exec -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org2.example.com/msp" peer0.org2.example.com peer channel fetch config -o orderer.example.com:7050 -c composerchannel composerchannel.block --tls --cafile /etc/hyperledger/orderer/tls/ca.crt + # Join peer0 from org2 to the channel. + docker exec -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org2.example.com/msp" peer0.org2.example.com peer channel join -b composerchannel.block --tls true --cafile /etc/hyperledger/orderer/tls/ca.crt else # Create the channel docker exec -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org1.example.com/msp" peer0.org1.example.com peer channel create -o orderer.example.com:7050 -c composerchannel -f /etc/hyperledger/configtx/composer-channel.tx - # Join peer0 to the channel. + # Join peer0 from org1 to the channel. docker exec -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org1.example.com/msp" peer0.org1.example.com peer channel join -b composerchannel.block + # Fetch the channel. + docker exec -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org2.example.com/msp" peer0.org2.example.com peer channel fetch config -o orderer.example.com:7050 -c composerchannel composerchannel.block + # Join peer0 from org2 to the channel. + docker exec -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org2.example.com/msp" peer0.org2.example.com peer channel join -b composerchannel.block fi fi @@ -93,8 +106,8 @@ for SYSTEST in $(echo ${SYSTEST} | tr "," " "); do fi # Delete any written configuration. - rm -rf ${HOME}/.composer-connection-profiles/composer-systests - rm -rf ${HOME}/.composer-credentials/composer-systests + rm -rf ${HOME}/.composer-connection-profiles/composer-systests* + rm -rf ${HOME}/.composer-credentials/composer-systests* # Delete any crypto-config material if [ -d ./hlfv1/crypto-config ]; then diff --git a/packages/composer-systests/systest/accesscontrols.js b/packages/composer-systests/systest/accesscontrols.js index da7cbf3f49..d28f9e3c68 100644 --- a/packages/composer-systests/systest/accesscontrols.js +++ b/packages/composer-systests/systest/accesscontrols.js @@ -32,7 +32,6 @@ process.setMaxListeners(Infinity); describe('Access control system tests', () => { let businessNetworkDefinition; - let admin; let client, aliceClient, bobClient; let alice, bob; let aliceAssetRegistry, bobAssetRegistry; @@ -57,8 +56,7 @@ describe('Access control system tests', () => { }); let aclFile = businessNetworkDefinition.getAclManager().createAclFile('permissions.acl', fs.readFileSync(path.resolve(__dirname, 'data/accesscontrols.acl'), 'utf8')); businessNetworkDefinition.getAclManager().setAclFile(aclFile); - admin = TestUtil.getAdmin(); - return admin.deploy(businessNetworkDefinition) + return TestUtil.deploy(businessNetworkDefinition) .then(() => { return TestUtil.getClient('systest-accesscontrols') .then((result) => { diff --git a/packages/composer-systests/systest/assets.js b/packages/composer-systests/systest/assets.js index e5e301f148..afc44f8bc8 100644 --- a/packages/composer-systests/systest/assets.js +++ b/packages/composer-systests/systest/assets.js @@ -29,7 +29,6 @@ chai.use(require('chai-subset')); describe('Asset system tests', function () { let businessNetworkDefinition; - let admin; let client; before(function () { @@ -41,13 +40,7 @@ describe('Asset system tests', function () { modelFiles.forEach((modelFile) => { businessNetworkDefinition.getModelManager().addModelFile(modelFile.contents, modelFile.fileName); }); - admin = TestUtil.getAdmin(); - console.log('testing install/start'); - // Have some system test perform install/start rather than deploy - return admin.install(businessNetworkDefinition.getName()) - .then(() => { - return admin.start(businessNetworkDefinition); - }) + return TestUtil.deploy(businessNetworkDefinition) .then(() => { return TestUtil.getClient('systest-assets') .then((result) => { diff --git a/packages/composer-systests/systest/events.js b/packages/composer-systests/systest/events.js index 2dcac0e357..e356075326 100644 --- a/packages/composer-systests/systest/events.js +++ b/packages/composer-systests/systest/events.js @@ -27,8 +27,8 @@ chai.use(require('chai-as-promised')); chai.use(require('chai-subset')); describe('Event system tests', function () { + let businessNetworkDefinition; - let admin; let client; before(function () { @@ -47,9 +47,7 @@ describe('Event system tests', function () { let scriptManager = businessNetworkDefinition.getScriptManager(); scriptManager.addScript(scriptManager.createScript(scriptFile.identifier, 'JS', scriptFile.contents)); }); - - admin = TestUtil.getAdmin(); - return admin.deploy(businessNetworkDefinition) + return TestUtil.deploy(businessNetworkDefinition) .then(() => { return TestUtil.getClient('systest-events') .then((result) => { @@ -87,47 +85,57 @@ describe('Event system tests', function () { afterEach(() => { client.removeAllListeners('event'); - return; }); after(() => { - client.removeAllListeners('event'); + if (client) { + client.removeAllListeners('event'); + } }); - it('should emit a valid SimpleEvent', (done) => { + it('should emit a valid SimpleEvent', () => { this.timeout(1000); let emitted = 0; let factory = client.getBusinessNetwork().getFactory(); let transaction = factory.newTransaction('systest.events', 'EmitSimpleEvent'); // Listen for the event - client.on('event', (ev) => { - validateEvent(ev, emitted); - emitted++; - emitted.should.equal(1); - done(); + const promise = new Promise((resolve, reject) => { + client.on('event', (ev) => { + validateEvent(ev, emitted); + emitted++; + emitted.should.equal(1); + resolve(); + }); }); - client.submitTransaction(transaction); + return client.submitTransaction(transaction) + .then(() => { + return promise; + }); }); - it('should emit a valid ComplexEvent', (done) => { + it('should emit a valid ComplexEvent', () => { this.timeout(1000); // Delay to prevent transaction failing let emitted = 0; let factory = client.getBusinessNetwork().getFactory(); let transaction = factory.newTransaction('systest.events', 'EmitComplexEvent'); // Listen for the event - client.on('event', (ev) => { - validateEvent(ev, emitted); - emitted++; - emitted.should.equal(1); - - done(); + const promise = new Promise((resolve, reject) => { + client.on('event', (ev) => { + validateEvent(ev, emitted); + emitted++; + emitted.should.equal(1); + resolve(); + }); }); - client.submitTransaction(transaction); + return client.submitTransaction(transaction) + .then(() => { + return promise; + }); }); - it('should emit two valid SimpleEvents', (done) => { + it('should emit two valid SimpleEvents', () => { this.timeout(1000); // Delay to prevent transaction failing let counts = [1, 2]; let emitted = 0; @@ -135,14 +143,19 @@ describe('Event system tests', function () { let transaction = factory.newTransaction('systest.events', 'EmitMultipleEvents'); // Listen for the event - client.on('event', (ev) => { - validateEvent(ev, emitted); - emitted++; - emitted.should.equal(counts[emitted - 1]); - if (emitted === 2) { - done(); - } + const promise = new Promise((resolve, reject) => { + client.on('event', (ev) => { + validateEvent(ev, emitted); + emitted++; + emitted.should.equal(counts[emitted - 1]); + if (emitted === 2) { + resolve(); + } + }); }); - client.submitTransaction(transaction); + return client.submitTransaction(transaction) + .then(() => { + return promise; + }); }); }); diff --git a/packages/composer-systests/systest/identities.js b/packages/composer-systests/systest/identities.js index 047c04801f..570b042528 100644 --- a/packages/composer-systests/systest/identities.js +++ b/packages/composer-systests/systest/identities.js @@ -15,6 +15,7 @@ 'use strict'; +const AdminConnection = require('composer-admin').AdminConnection; const BusinessNetworkDefinition = require('composer-admin').BusinessNetworkDefinition; const fs = require('fs'); @@ -32,7 +33,6 @@ process.setMaxListeners(Infinity); describe('Identity system tests', () => { let businessNetworkDefinition; - let admin; let client; let participant; @@ -52,13 +52,7 @@ describe('Identity system tests', () => { let scriptManager = businessNetworkDefinition.getScriptManager(); scriptManager.addScript(scriptManager.createScript(scriptFile.identifier, 'JS', scriptFile.contents)); }); - admin = TestUtil.getAdmin(); - console.log('testing install/start'); - // Have some system test perform install/start rather than deploy - return admin.install(businessNetworkDefinition.getName()) - .then(() => { - return admin.start(businessNetworkDefinition); - }) + return TestUtil.deploy(businessNetworkDefinition) .then(() => { return TestUtil.getClient('systest-identities') .then((result) => { @@ -108,7 +102,7 @@ describe('Identity system tests', () => { } else if (TestUtil.isHyperledgerFabricV1()) { const certificateFile = path.resolve(__dirname, '../hlfv1/crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/signcerts/User1@org1.example.com-cert.pem'); certificate = fs.readFileSync(certificateFile, 'utf8'); - const privateKeyFile = path.resolve(__dirname, '../hlfv1/crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/keystore/8b627256737cb372d6e51036b74f2952181000bc4a3a7123acea36ee909b454c_sk'); + const privateKeyFile = path.resolve(__dirname, '../hlfv1/crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/keystore/key.pem'); privateKey = fs.readFileSync(privateKeyFile, 'utf8'); } else { certificate = [ @@ -120,7 +114,12 @@ describe('Identity system tests', () => { } return client.bindIdentity(participant, certificate) .then(() => { - return admin.importIdentity('composer-systests', identity, certificate, privateKey); + const admin = new AdminConnection(); + if (TestUtil.isHyperledgerFabricV1()) { + return admin.importIdentity('composer-systests-org1', identity, certificate, privateKey); + } else { + return admin.importIdentity('composer-systests', identity, certificate, privateKey); + } }) .then(() => { return TestUtil.getClient('systest-identities', identity, 'not used'); @@ -198,9 +197,9 @@ describe('Identity system tests', () => { if (TestUtil.isHyperledgerFabricV06()) { return this.skip(); } else if (TestUtil.isHyperledgerFabricV1()) { - const certificateFile = path.resolve(__dirname, '../hlfv1/crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/signcerts/User1@org1.example.com-cert.pem'); + const certificateFile = path.resolve(__dirname, '../hlfv1/crypto-config/peerOrganizations/org1.example.com/users/User2@org1.example.com/msp/signcerts/User2@org1.example.com-cert.pem'); certificate = fs.readFileSync(certificateFile, 'utf8'); - const privateKeyFile = path.resolve(__dirname, '../hlfv1/crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/keystore/8b627256737cb372d6e51036b74f2952181000bc4a3a7123acea36ee909b454c_sk'); + const privateKeyFile = path.resolve(__dirname, '../hlfv1/crypto-config/peerOrganizations/org1.example.com/users/User2@org1.example.com/msp/keystore/key.pem'); privateKey = fs.readFileSync(privateKeyFile, 'utf8'); } else { certificate = [ @@ -212,7 +211,12 @@ describe('Identity system tests', () => { } return client.bindIdentity(participant, certificate) .then(() => { - return admin.importIdentity('composer-systests', identity, certificate, privateKey); + const admin = new AdminConnection(); + if (TestUtil.isHyperledgerFabricV1()) { + return admin.importIdentity('composer-systests-org1', identity, certificate, privateKey); + } else { + return admin.importIdentity('composer-systests', identity, certificate, privateKey); + } }) .then(() => { return TestUtil.getClient('systest-identities', identity, 'not used'); diff --git a/packages/composer-systests/systest/participants.js b/packages/composer-systests/systest/participants.js index 27c9fab7bb..86ee993db1 100644 --- a/packages/composer-systests/systest/participants.js +++ b/packages/composer-systests/systest/participants.js @@ -28,7 +28,6 @@ chai.use(require('chai-subset')); describe('Participant system tests', function () { let businessNetworkDefinition; - let admin; let client; before(function () { @@ -40,8 +39,7 @@ describe('Participant system tests', function () { modelFiles.forEach((modelFile) => { businessNetworkDefinition.getModelManager().addModelFile(modelFile.contents, modelFile.fileName); }); - admin = TestUtil.getAdmin(); - return admin.deploy(businessNetworkDefinition) + return TestUtil.deploy(businessNetworkDefinition) .then(() => { return TestUtil.getClient('systest-participants') .then((result) => { diff --git a/packages/composer-systests/systest/post.js b/packages/composer-systests/systest/post.js index 8f87ec7701..a6669ef92e 100644 --- a/packages/composer-systests/systest/post.js +++ b/packages/composer-systests/systest/post.js @@ -28,7 +28,6 @@ chai.use(require('chai-as-promised')); describe('HTTP POST system tests', () => { let businessNetworkDefinition; - let admin; let client; before(function () { @@ -46,8 +45,7 @@ describe('HTTP POST system tests', () => { let scriptManager = businessNetworkDefinition.getScriptManager(); scriptManager.addScript(scriptManager.createScript(scriptFile.identifier, 'JS', scriptFile.contents)); }); - admin = TestUtil.getAdmin(); - return admin.deploy(businessNetworkDefinition) + return TestUtil.deploy(businessNetworkDefinition) .then(() => { return TestUtil.getClient('systest-post') .then((result) => { diff --git a/packages/composer-systests/systest/queries.js b/packages/composer-systests/systest/queries.js index 03b019c4c3..3ed3bd03b5 100644 --- a/packages/composer-systests/systest/queries.js +++ b/packages/composer-systests/systest/queries.js @@ -28,7 +28,6 @@ chai.use(require('chai-as-promised')); describe('Query system tests', () => { let businessNetworkDefinition; - let admin; let client; let assetsAsJSON; let participantsAsJSON; @@ -116,8 +115,7 @@ describe('Query system tests', () => { let scriptManager = businessNetworkDefinition.getScriptManager(); scriptManager.addScript(scriptManager.createScript(scriptFile.identifier, 'JS', scriptFile.contents)); }); - admin = TestUtil.getAdmin(); - return admin.deploy(businessNetworkDefinition) + return TestUtil.deploy(businessNetworkDefinition, true) .then(() => { return TestUtil.getClient('systest-queries') .then((result) => { diff --git a/packages/composer-systests/systest/testutil.js b/packages/composer-systests/systest/testutil.js index 7935aee26c..f4291e4f13 100644 --- a/packages/composer-systests/systest/testutil.js +++ b/packages/composer-systests/systest/testutil.js @@ -24,8 +24,8 @@ const path = require('path'); const sleep = require('sleep-promise'); const Util = require('composer-common').Util; -let adminConnection; let client; +let forceDeploy = false; /** * Trick browserify by making the ID parameter to require dynamic. @@ -169,21 +169,29 @@ class TestUtil { * connected instance of BusinessNetworkConnection. */ static setUp() { + const adminConnection = new AdminConnection(); + forceDeploy = false; return TestUtil.waitForPorts() - .then(function () { - adminConnection = new AdminConnection(); - let adminOptions; + .then(() => { + + // Create all necessary configuration for the web runtime. if (TestUtil.isWeb()) { const BrowserFS = require('browserfs'); BrowserFS.initialize(new BrowserFS.FileSystem.LocalStorage()); ConnectionProfileManager.registerConnectionManager('web', require('composer-connector-web')); - adminOptions = { + console.log('Calling AdminConnection.createProfile() ...'); + return adminConnection.createProfile('composer-systests', { type: 'web' - }; + }); + + // Create all necessary configuration for the embedded runtime. } else if (TestUtil.isEmbedded()) { - adminOptions = { + console.log('Calling AdminConnection.createProfile() ...'); + return adminConnection.createProfile('composer-systests', { type: 'embedded' - }; + }); + + // Create all necessary configuration for the embedded runtime hosted via the connector server. } else if (TestUtil.isProxy()) { // A whole bunch of dynamic requires to trick browserify. const ConnectorServer = dynamicRequire('composer-connector-server'); @@ -194,9 +202,6 @@ class TestUtil { const socketIO = dynamicRequire('socket.io'); // We are using the embedded connector, but we configure it to route through the // proxy connector and connector server. - adminOptions = { - type: 'embedded' - }; const connectionProfileStore = new FSConnectionProfileStore(fs); ConnectionProfileManager.registerConnectionManager('embedded', ProxyConnectionManager); const connectionProfileManager = new ConnectionProfileManager(connectionProfileStore); @@ -214,113 +219,200 @@ class TestUtil { io.on('disconnect', (socket) => { console.log(`Client with ID '${socket.id}' on host '${socket.request.connection.remoteAddress}' disconnected`); }); - } else if (TestUtil.isHyperledgerFabric()) { - // hlf need to decide if v1 or 0.6 - let keyValStore = path.resolve(homedir(), '.composer-credentials', 'composer-systests'); + console.log('Calling AdminConnection.createProfile() ...'); + return adminConnection.createProfile('composer-systests', { + type: 'embedded' + }); + + // Create all necessary configuration for Hyperledger Fabric v0.6. + } else if (TestUtil.isHyperledgerFabricV06()) { + const keyValStore = path.resolve(homedir(), '.composer-credentials', 'composer-systests'); mkdirp.sync(keyValStore); - if (TestUtil.isHyperledgerFabricV1()) { - if (process.env.SYSTEST.match('tls$')) { - console.log('setting up TLS Connection Profile for HLF V1'); - adminOptions = { - type: 'hlfv1', - orderers: [ - { - url: 'grpcs://localhost:7050', - hostnameOverride: 'orderer.example.com', - cert: './hlfv1/crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/ca.crt' - } - ], - ca: { - url: 'https://localhost:7054', - name: 'ca.org1.example.com' + const connectionProfile = { + type: 'hlf', + keyValStore: keyValStore, + membershipServicesURL: 'grpc://localhost:7054', + peerURL: 'grpc://localhost:7051', + eventHubURL: 'grpc://localhost:7053' + }; + if (process.env.COMPOSER_DEPLOY_WAIT_SECS) { + connectionProfile.deployWaitTime = parseInt(process.env.COMPOSER_DEPLOY_WAIT_SECS); + console.log('COMPOSER_DEPLOY_WAIT_SECS set, using: ', connectionProfile.deployWaitTime); + } + if (process.env.COMPOSER_INVOKE_WAIT_SECS) { + connectionProfile.invokeWaitTime = parseInt(process.env.COMPOSER_INVOKE_WAIT_SECS); + console.log('COMPOSER_INVOKE_WAIT_SECS set, using: ', connectionProfile.invokeWaitTime); + } + console.log('Calling AdminConnection.createProfile() ...'); + return adminConnection.createProfile('composer-systests', connectionProfile); + + // Create all necessary configuration for Hyperledger Fabric v1.0. + } else if (TestUtil.isHyperledgerFabricV1()) { + const keyValStoreOrg1 = path.resolve(homedir(), '.composer-credentials', 'composer-systests-org1'); + mkdirp.sync(keyValStoreOrg1); + const keyValStoreOrg2 = path.resolve(homedir(), '.composer-credentials', 'composer-systests-org2'); + mkdirp.sync(keyValStoreOrg2); + let connectionProfileOrg1, connectionProfileOrg2; + if (process.env.SYSTEST.match('tls$')) { + console.log('setting up TLS Connection Profile for HLF V1'); + connectionProfileOrg1 = { + type: 'hlfv1', + orderers: [ + { + url: 'grpcs://localhost:7050', + hostnameOverride: 'orderer.example.com', + cert: './hlfv1/crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/ca.crt' + } + ], + ca: { + url: 'https://localhost:7054', + name: 'ca.org1.example.com' + }, + peers: [ + { + requestURL: 'grpcs://localhost:7051', + eventURL: 'grpcs://localhost:7053', + hostnameOverride: 'peer0.org1.example.com', + cert: './hlfv1/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt' }, - peers: [ - { - requestURL: 'grpcs://localhost:7051', - eventURL: 'grpcs://localhost:7053', - hostnameOverride: 'peer0.org1.example.com', - cert: './hlfv1/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt' - } - ], - keyValStore: keyValStore, - channel: 'composerchannel', - mspID: 'Org1MSP', - timeout: '300' - }; - } else { - console.log('setting up Non-TLS Connection Profile for HLF V1'); - adminOptions = { - type: 'hlfv1', - orderers: [ - 'grpc://localhost:7050' - ], - ca: { - url: 'http://localhost:7054', - name: 'ca.org1.example.com' + { + requestURL: 'grpcs://localhost:8051', + hostnameOverride: 'peer0.org2.example.com', + cert: './hlfv1/crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt' + } + ], + keyValStore: keyValStoreOrg1, + channel: 'composerchannel', + mspID: 'Org1MSP', + timeout: '300' + }; + connectionProfileOrg2 = { + type: 'hlfv1', + orderers: [ + { + url: 'grpcs://localhost:7050', + hostnameOverride: 'orderer.example.com', + cert: './hlfv1/crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/ca.crt' + } + ], + ca: { + url: 'https://localhost:8054', + name: 'ca.org2.example.com' + }, + peers: [ + { + requestURL: 'grpcs://localhost:8051', + eventURL: 'grpcs://localhost:8053', + hostnameOverride: 'peer0.org2.example.com', + cert: './hlfv1/crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt' }, - peers: [ - { - requestURL: 'grpc://localhost:7051', - eventURL: 'grpc://localhost:7053' - } - ], - channel: 'composerchannel', - mspID: 'Org1MSP', - timeout: '300', - keyValStore: keyValStore - }; - } + { + requestURL: 'grpcs://localhost:7051', + hostnameOverride: 'peer0.org1.example.com', + cert: './hlfv1/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt' + } + ], + keyValStore: keyValStoreOrg2, + channel: 'composerchannel', + mspID: 'Org2MSP', + timeout: '300' + }; } else { - adminOptions = { - type: 'hlf', - keyValStore: keyValStore, - membershipServicesURL: 'grpc://localhost:7054', - peerURL: 'grpc://localhost:7051', - eventHubURL: 'grpc://localhost:7053' + console.log('setting up Non-TLS Connection Profile for HLF V1'); + connectionProfileOrg1 = { + type: 'hlfv1', + orderers: [ + 'grpc://localhost:7050' + ], + ca: { + url: 'http://localhost:7054', + name: 'ca.org1.example.com' + }, + peers: [ + { + requestURL: 'grpc://localhost:7051', + eventURL: 'grpc://localhost:7053' + }, + { + requestURL: 'grpc://localhost:8051' + } + ], + channel: 'composerchannel', + mspID: 'Org1MSP', + timeout: '300', + keyValStore: keyValStoreOrg1 + }; + connectionProfileOrg2 = { + type: 'hlfv1', + orderers: [ + 'grpc://localhost:7050' + ], + ca: { + url: 'http://localhost:8054', + name: 'ca.org2.example.com' + }, + peers: [ + { + requestURL: 'grpc://localhost:8051', + eventURL: 'grpc://localhost:8053' + }, + { + requestURL: 'grpc://localhost:7051', + } + ], + channel: 'composerchannel', + mspID: 'Org2MSP', + timeout: '300', + keyValStore: keyValStoreOrg2 }; } + if (process.env.COMPOSER_TIMEOUT_SECS) { + connectionProfileOrg1.timeout = parseInt(process.env.COMPOSER_TIMEOUT_SECS); + connectionProfileOrg2.timeout = parseInt(process.env.COMPOSER_TIMEOUT_SECS); + console.log('COMPOSER_TIMEOUT_SECS set, using: ', connectionProfileOrg1.timeout, connectionProfileOrg2.timeout); + } + console.log('Calling AdminConnection.createProfile() ...'); + return adminConnection.createProfile('composer-systests-org1', connectionProfileOrg1) + .then(() => { + return adminConnection.createProfile('composer-systests-org2', connectionProfileOrg2); + }) + .then(() => { + connectionProfileOrg1.peers.pop(); + return adminConnection.createProfile('composer-systests-org1-solo', connectionProfileOrg1); + }) + .then(() => { + connectionProfileOrg2.peers.pop(); + return adminConnection.createProfile('composer-systests-org2-solo', connectionProfileOrg2); + }); } else { throw new Error('I do not know what kind of tests you want me to run!'); } - if (process.env.COMPOSER_DEPLOY_WAIT_SECS) { - adminOptions.deployWaitTime = parseInt(process.env.COMPOSER_DEPLOY_WAIT_SECS); - console.log('COMPOSER_DEPLOY_WAIT_SECS set, using: ', adminOptions.deployWaitTime); - } - if (process.env.COMPOSER_INVOKE_WAIT_SECS) { - adminOptions.invokeWaitTime = parseInt(process.env.COMPOSER_INVOKE_WAIT_SECS); - console.log('COMPOSER_INVOKE_WAIT_SECS set, using: ', adminOptions.invokeWaitTime); - } - if (process.env.COMPOSER_TIMEOUT_SECS) { - adminOptions.timeout = parseInt(process.env.COMPOSER_TIMEOUT_SECS); - console.log('COMPOSER_TIMEOUT_SECS set, using: ', adminOptions.timeout); - } - console.log('Calling AdminConnection.createProfile() ...'); - return adminConnection.createProfile('composer-systests', adminOptions); }) - .then(function () { + .then(() => { console.log('Called AdminConnection.createProfile()'); if (TestUtil.isHyperledgerFabricV1()) { let fs = dynamicRequire('fs'); - let org = 'org1'; - let keyPath = path.join(__dirname, '../hlfv1/crypto-config/peerOrganizations/' + org + '.example.com/users/Admin@' + org + '.example.com/msp/keystore/cf961334129e4cf283c1144356f36a394d6b65a75ecff835f5d5de545a006141_sk'); - let certPath = path.join(__dirname, '../hlfv1/crypto-config/peerOrganizations/' + org + '.example.com/users/Admin@' + org + '.example.com/msp/signcerts/Admin@org1.example.com-cert.pem'); - let signerCert = fs.readFileSync(certPath).toString(); - let key = fs.readFileSync(keyPath).toString(); console.log('Calling AdminConnection.importIdentity() ...'); - return adminConnection.importIdentity('composer-systests', 'Org1PeerAdmin', signerCert, key); + const admins = [ + { org: 'org1', keyFile: 'key.pem' }, + { org: 'org2', keyFile: 'key.pem' } + ]; + return admins.reduce((promise, admin) => { + const org = admin.org; + const keyFile = admin.keyFile; + return promise.then(() => { + let keyPath = path.join(__dirname, `../hlfv1/crypto-config/peerOrganizations/${org}.example.com/users/Admin@${org}.example.com/msp/keystore/${keyFile}`); + let certPath = path.join(__dirname, `../hlfv1/crypto-config/peerOrganizations/${org}.example.com/users/Admin@${org}.example.com/msp/signcerts/Admin@${org}.example.com-cert.pem`); + let signerCert = fs.readFileSync(certPath).toString(); + let key = fs.readFileSync(keyPath).toString(); + return adminConnection.importIdentity(`composer-systests-${org}`, 'PeerAdmin', signerCert, key); + }); + }, Promise.resolve()) + .then(() => { + console.log('Called AdminConnection.importIdentity() ...'); + }); } - }) - .then(function () { - console.log('Called AdminConnection.importIdentity() ...'); - console.log('Calling AdminConnection.connect() ...'); - let user = TestUtil.isHyperledgerFabricV1() ? 'Org1PeerAdmin' : 'admin'; - let password = TestUtil.isHyperledgerFabricV1() ? 'NOTNEEDED' : 'Xurw3yU9zI0l'; - return adminConnection.connect('composer-systests', user, password); - }) - .then(function () { - console.log('Called AdminConnection.connect()'); - console.log(''); }); } @@ -330,25 +422,8 @@ class TestUtil { * connected instance of BusinessNetworkConnection. */ static tearDown() { - if (!adminConnection) { - throw new Error('Must call setUp successfully before calling tearDown'); - } - console.log('Calling adminConnection.disconnect() ...'); - return adminConnection.disconnect() - .then(function () { - console.log('Called adminConnection.disconnect()'); - }); - } - - /** - * Get a configured and connected instance of AdminConnection. - * @return {AdminConnection} - a configured and connected instance of AdminConnection. - */ - static getAdmin() { - if (!adminConnection) { - throw new Error('Must call setUp successfully before calling getAdmin'); - } - return adminConnection; + forceDeploy = false; + return Promise.resolve(); } /** @@ -378,16 +453,129 @@ class TestUtil { }) .then(() => { enrollmentID = enrollmentID || 'admin'; - let password = TestUtil.isHyperledgerFabric() && process.env.SYSTEST.match('^hlfv1') ? 'adminpw' : 'Xurw3yU9zI0l'; + let password = TestUtil.isHyperledgerFabricV1() ? 'adminpw' : 'Xurw3yU9zI0l'; enrollmentSecret = enrollmentSecret || password; // console.log(`Calling Client.connect('composer-systest', '${network}', '${enrollmentID}', '${enrollmentSecret}') ...`); - return thisClient.connect('composer-systests', network, enrollmentID, enrollmentSecret); + if (TestUtil.isHyperledgerFabricV1() && !forceDeploy) { + return thisClient.connect('composer-systests-org1', network, enrollmentID, enrollmentSecret); + } else if (TestUtil.isHyperledgerFabricV1() && forceDeploy) { + return thisClient.connect('composer-systests-org1-solo', network, enrollmentID, enrollmentSecret); + } else { + return thisClient.connect('composer-systests', network, enrollmentID, enrollmentSecret); + } }) .then(() => { return thisClient; }); } + /** + * Deploy the specified business network definition. + * @param {BusinessNetworkDefinition} businessNetworkDefinition - the business network definition to deploy. + * @param {boolean} [forceDeploy_] - force use of the deploy API instead of install and start. + * @return {Promise} - a promise that will be resolved when complete. + */ + static deploy(businessNetworkDefinition, forceDeploy_) { + const adminConnection = new AdminConnection(); + forceDeploy = forceDeploy_; + if (TestUtil.isHyperledgerFabricV1() && !forceDeploy) { + console.log(`Deploying business network ${businessNetworkDefinition.getName()} using install & start ...`); + return Promise.resolve() + .then(() => { + // Connect and install the runtime onto the peers for org1. + return adminConnection.connect('composer-systests-org1-solo', 'PeerAdmin', 'NOTNEEDED'); + }) + .then(() => { + return adminConnection.install(businessNetworkDefinition.getName()); + }) + .then(() => { + return adminConnection.disconnect(); + }) + .then(() => { + // Connect and install the runtime onto the peers for org2. + return adminConnection.connect('composer-systests-org2-solo', 'PeerAdmin', 'NOTNEEDED'); + }) + .then(() => { + return adminConnection.install(businessNetworkDefinition.getName()); + }) + .then(() => { + return adminConnection.disconnect(); + }) + .then(() => { + // Connect and start the network on the peers for org1 and org2. + return adminConnection.connect('composer-systests-org1', 'PeerAdmin', 'NOTNEEDED'); + }) + .then(() => { + return adminConnection.start(businessNetworkDefinition, { + endorsementPolicy: { + identities: [ + { + role: { + name: 'member', + mspId: 'Org1MSP' + } + }, + { + role: { + name: 'member', + mspId: 'Org2MSP' + } + } + ], + policy: { + '2-of': [ + { + 'signed-by': 0 + }, + { + 'signed-by': 1 + } + ] + } + } + }); + }) + .then(() => { + return adminConnection.disconnect(); + }); + } else if (TestUtil.isHyperledgerFabricV1() && forceDeploy) { + console.log(`Deploying business network ${businessNetworkDefinition.getName()} using deploy ...`); + // Connect and deploy the network on the peers for org1. + return adminConnection.connect('composer-systests-org1-solo', 'PeerAdmin', 'NOTNEEDED') + .then(() => { + return adminConnection.deploy(businessNetworkDefinition); + }) + .then(() => { + return adminConnection.disconnect(); + }); + } else if (!forceDeploy) { + console.log(`Deploying business network ${businessNetworkDefinition.getName()} using install & start ...`); + // Connect, install the runtime and start the network. + return adminConnection.connect('composer-systests', 'admin', 'Xurw3yU9zI0l') + .then(() => { + return adminConnection.install(businessNetworkDefinition.getName()); + }) + .then(() => { + return adminConnection.start(businessNetworkDefinition); + }) + .then(() => { + return adminConnection.disconnect(); + }); + } else if (forceDeploy) { + console.log(`Deploying business network ${businessNetworkDefinition.getName()} using deploy ...`); + // Connect and deploy the network. + return adminConnection.connect('composer-systests', 'admin', 'Xurw3yU9zI0l') + .then(() => { + return adminConnection.deploy(businessNetworkDefinition); + }) + .then(() => { + return adminConnection.disconnect(); + }); + } else { + throw new Error('I do not know what kind of deploy you want me to run!'); + } + } + /** * Reset the business network to its initial state. * @return {Promise} - a promise that will be resolved when complete. diff --git a/packages/composer-systests/systest/transactions.assets.js b/packages/composer-systests/systest/transactions.assets.js index 7f07dbf6b2..52a66b2166 100644 --- a/packages/composer-systests/systest/transactions.assets.js +++ b/packages/composer-systests/systest/transactions.assets.js @@ -29,7 +29,6 @@ chai.use(require('chai-as-promised')); describe('Transaction (asset specific) system tests', () => { let businessNetworkDefinition; - let admin; let client; before(function () { @@ -47,13 +46,7 @@ describe('Transaction (asset specific) system tests', () => { let scriptManager = businessNetworkDefinition.getScriptManager(); scriptManager.addScript(scriptManager.createScript(scriptFile.identifier, 'JS', scriptFile.contents)); }); - admin = TestUtil.getAdmin(); - console.log('testing install/start'); - // Have some system test perform install/start rather than deploy - return admin.install(businessNetworkDefinition.getName()) - .then(() => { - return admin.start(businessNetworkDefinition); - }) + return TestUtil.deploy(businessNetworkDefinition) .then(() => { return TestUtil.getClient('systest-transactions-assets') .then((result) => { diff --git a/packages/composer-systests/systest/transactions.js b/packages/composer-systests/systest/transactions.js index 9c8634bffe..018a0e9681 100644 --- a/packages/composer-systests/systest/transactions.js +++ b/packages/composer-systests/systest/transactions.js @@ -28,7 +28,6 @@ chai.use(require('chai-as-promised')); describe('Transaction system tests', () => { let businessNetworkDefinition; - let admin; let client; before(function () { @@ -47,8 +46,7 @@ describe('Transaction system tests', () => { let scriptManager = businessNetworkDefinition.getScriptManager(); scriptManager.addScript(scriptManager.createScript(scriptFile.identifier, 'JS', scriptFile.contents)); }); - admin = TestUtil.getAdmin(); - return admin.deploy(businessNetworkDefinition) + return TestUtil.deploy(businessNetworkDefinition) .then(() => { return TestUtil.getClient('systest-transactions') .then((result) => { diff --git a/packages/composer-systests/systest/transactions.participants.js b/packages/composer-systests/systest/transactions.participants.js index 967533773c..190b5f93bc 100644 --- a/packages/composer-systests/systest/transactions.participants.js +++ b/packages/composer-systests/systest/transactions.participants.js @@ -29,7 +29,6 @@ chai.use(require('chai-as-promised')); describe('Transaction (participant specific) system tests', () => { let businessNetworkDefinition; - let admin; let client; before(function () { @@ -47,8 +46,7 @@ describe('Transaction (participant specific) system tests', () => { let scriptManager = businessNetworkDefinition.getScriptManager(); scriptManager.addScript(scriptManager.createScript(scriptFile.identifier, 'JS', scriptFile.contents)); }); - admin = TestUtil.getAdmin(); - return admin.deploy(businessNetworkDefinition) + return TestUtil.deploy(businessNetworkDefinition) .then(() => { return TestUtil.getClient('systest-transactions-participants') .then((result) => { diff --git a/packages/composer-systests/systest/transactions.queries.js b/packages/composer-systests/systest/transactions.queries.js index 0455b190b0..20390540e7 100644 --- a/packages/composer-systests/systest/transactions.queries.js +++ b/packages/composer-systests/systest/transactions.queries.js @@ -28,7 +28,6 @@ chai.use(require('chai-as-promised')); describe('Transaction (query specific) system tests', () => { let businessNetworkDefinition; - let admin; let client; let assetsAsJSON; let participantsAsJSON; @@ -119,8 +118,7 @@ describe('Transaction (query specific) system tests', () => { let scriptManager = businessNetworkDefinition.getScriptManager(); scriptManager.addScript(scriptManager.createScript(scriptFile.identifier, 'JS', scriptFile.contents)); }); - admin = TestUtil.getAdmin(); - return admin.deploy(businessNetworkDefinition) + return TestUtil.deploy(businessNetworkDefinition, true) .then(() => { return TestUtil.getClient('systest-transactions-queries') .then((result) => {