Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cheery-pick chaincode server feature to release-2.x #166

Merged
merged 6 commits into from
Jun 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions libraries/fabric-shim/lib/chaincode.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ const Logger = require('./logger');
const utils = require('./utils/utils');

const logger = Logger.getLogger('lib/chaincode.js');
const Handler = require('./handler');
const {ChaincodeSupportClient} = require('./handler');
const ChaincodeServer = require('./server');
const Iterators = require('./iterators');
const ChaincodeStub = require('./stub');
const KeyEndorsementPolicy = require('./utils/statebased');
Expand Down Expand Up @@ -122,7 +123,7 @@ class Shim {
}

const chaincodeName = opts['chaincode-id-name'];
const client = new Handler(chaincode, url, optsCpy);
const client = new ChaincodeSupportClient(chaincode, url, optsCpy);
const chaincodeID = {
name: chaincodeName
};
Expand Down Expand Up @@ -194,6 +195,28 @@ class Shim {

return Logger.getLogger(name);
}

/**
* @interface ChaincodeServerTLSProperties
* @property {Buffer} key Private key for TLS
* @property {Buffer} cert Certificate for TLS
* @property {Buffer} [clientCACerts] CA certificate for client certificates if mutual TLS is used.
*/
/**
* @interface ChaincodeServerOpts
* @property {string} ccid Chaincode ID
* @property {string} address Listen address for the server
* @property {ChaincodeServerTLSProperties} [tlsProps] TLS properties if TLS is required.
*/
/**
* Returns a new Chaincode server. Should be called when the chaincode is launched in a server mode.
* @static
* @param {ChaincodeInterface} chaincode User-provided object that must implement <code>ChaincodeInterface</code>
* @param {ChaincodeServerOpts} serverOpts Chaincode server options
*/
static server(chaincode, serverOpts) {
return new ChaincodeServer(chaincode, serverOpts);
}
}

// special OID used by Fabric to save attributes in X.509 certificates
Expand Down
106 changes: 106 additions & 0 deletions libraries/fabric-shim/lib/cmds/serverCommand.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
# Copyright Hitachi America, Ltd. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
*/

'use strict';

const fs = require('fs');

exports.command = 'server [options]';
exports.desc = 'Start the chaincode as a server';

const validOptions = {
'chaincode-address': {type: 'string', required: true},
'grpc.max_send_message_length': {type: 'number', default: -1},
'grpc.max_receive_message_length': {type: 'number', default: -1},
'grpc.keepalive_time_ms': {type: 'number', default: 110000},
'grpc.http2.min_time_between_pings_ms': {type: 'number', default: 110000},
'grpc.keepalive_timeout_ms': {type: 'number', default: 20000},
'grpc.http2.max_pings_without_data': {type: 'number', default: 0},
'grpc.keepalive_permit_without_calls': {type: 'number', default: 1},
'chaincode-id': {type: 'string', required: true},
'chaincode-tls-cert-file': {type: 'string', conflicts: 'chaincode-tls-cert-path'},
'chaincode-tls-cert-path': {type: 'string', conflicts: 'chaincode-tls-cert-file'},
'chaincode-tls-key-file': {type: 'string', conflicts: 'chaincode-tls-key-path'},
'chaincode-tls-key-path': {type: 'string', conflicts: 'chaincode-tls-key-file'},
'chaincode-tls-client-cacert-file': {type: 'string', conflicts: 'chaincode-tls-client-cacert-path'},
'chaincode-tls-client-cacert-path': {type: 'string', conflicts: 'chaincode-tls-client-cacert-file'},
'module-path': {type: 'string', default: process.cwd()}
};

exports.validOptions = validOptions;

exports.builder = function (yargs) {
yargs.options(validOptions);

yargs.usage('fabric-chaincode-node server --chaincode-address 0.0.0.0:9999 --chaincode-id mycc_v0:abcdef12345678...');

yargs.check((argv) => {
if (argv['chaincode-tls-key-file'] || argv['chaincode-tls-key-path'] ||
argv['chaincode-tls-cert-file'] || argv['chaincode-tls-cert-path']) {
// TLS should be enabled
if (!argv['chaincode-tls-key-file'] && !argv['chaincode-tls-key-path']) {
throw new Error('A TLS option is set but no key is specified');
}
if (!argv['chaincode-tls-cert-file'] && !argv['chaincode-tls-cert-path']) {
throw new Error('A TLS option is set but no cert is specified');
}
}
return true;
});

return yargs;
};

exports.handler = function (argv) {
const Bootstrap = require('../contract-spi/bootstrap');

return argv.thePromise = Bootstrap.bootstrap(true);
};

exports.getArgs = function (yargs) {
const argv = {};

for (const name in validOptions) {
argv[name] = yargs.argv[name];
}

// Load the cryptographic files if TLS is enabled
if (argv['chaincode-tls-key-file'] || argv['chaincode-tls-key-path'] ||
argv['chaincode-tls-cert-file'] || argv['chaincode-tls-cert-path']) {

const tlsProps = {};

if (argv['chaincode-tls-key-file']) {
tlsProps.key = fs.readFileSync(argv['chaincode-tls-key-file']);
} else {
tlsProps.key = Buffer.from(fs.readFileSync(argv['chaincode-tls-key-path']).toString(), 'base64');
}

if (argv['chaincode-tls-cert-file']) {
tlsProps.cert = fs.readFileSync(argv['chaincode-tls-cert-file']);
} else {
tlsProps.cert = Buffer.from(fs.readFileSync(argv['chaincode-tls-cert-path']).toString(), 'base64');
}

// If cacert option is specified, enable client certificate validation
if (argv['chaincode-tls-client-cacert-file']) {
tlsProps.clientCACerts = fs.readFileSync(argv['chaincode-tls-client-cacert-file']);
} else if (argv['chaincode-tls-client-cacert-path']) {
tlsProps.clientCACerts = Buffer.from(fs.readFileSync(argv['chaincode-tls-client-cacert-path']).toString(), 'base64');
}

argv.tlsProps = tlsProps;
}

// Translate the options to server options
argv.ccid = argv['chaincode-id'];
argv.address = argv['chaincode-address'];

delete argv['chaincode-id'];
delete argv['chaincode-address'];

return argv;
};
19 changes: 13 additions & 6 deletions libraries/fabric-shim/lib/contract-spi/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const shim = require('../chaincode');
const ChaincodeFromContract = require('./chaincodefromcontract');
const Logger = require('../logger');
const StartCommand = require('../cmds/startCommand.js');
const ServerCommand = require('../cmds/serverCommand.js');

const logger = Logger.getLogger('contracts-spi/bootstrap.js');

Expand All @@ -28,25 +29,31 @@ class Bootstrap {
* @ignore
* @param {Contract} contracts contract to register to use
*/
static register(contracts, serializers, fileMetadata, title, version) {
static register(contracts, serializers, fileMetadata, title, version, opts, serverMode = false) {
// load up the meta data that the user may have specified
// this will need to passed in and rationalized with the
// code as implemented
const chaincode = new ChaincodeFromContract(contracts, serializers, fileMetadata, title, version);

// say hello to the peer
shim.start(chaincode);
if (serverMode) {
const server = shim.server(chaincode, opts);
server.start();
} else {
// say hello to the peer
shim.start(chaincode);
}
}

/**
*
* @ignore
* @param {boolean} serverMode set true if the chaincode should be started as a server
*/
static async bootstrap() {
const opts = StartCommand.getArgs(yargs);
static async bootstrap(serverMode = false) {
const opts = serverMode ? ServerCommand.getArgs(yargs) : StartCommand.getArgs(yargs);
const {contracts, serializers, title, version} = this.getInfoFromContract(opts['module-path']);
const fileMetadata = await Bootstrap.getMetadata(opts['module-path']);
Bootstrap.register(contracts, serializers, fileMetadata, title, version);
Bootstrap.register(contracts, serializers, fileMetadata, title, version, opts, serverMode);
}

static getInfoFromContract(modulePath) {
Expand Down
45 changes: 35 additions & 10 deletions libraries/fabric-shim/lib/handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ class MsgQueueHandler {


/*
* The ChaincodeSupportClient class represents a the base class for all remote nodes, Peer, Orderer , and MemberServicespeer.
* The ChaincodeSupportClient class represents a chaincode gRPC client to the peer.
*/
class ChaincodeSupportClient {

Expand Down Expand Up @@ -269,11 +269,37 @@ class ChaincodeSupportClient {
this._stream.end();
}

chat(convStarterMsg) {
this._stream = this._client.register();

this._handler = new ChaincodeMessageHandler(this._stream, this.chaincode);
this._handler.chat(convStarterMsg);
}

/*
return a printable representation of this object
*/
toString() {
return 'ChaincodeSupportClient : {' +
'url:' +
this._url +
'}';
}
}

/**
* The ChaincodeMessageHandler class handles messages between peer and chaincode both in the chaincode server and client model.
*/
class ChaincodeMessageHandler {
constructor (stream, chaincode) {
this._stream = stream;
this.chaincode = chaincode;
}

// this is a long-running method that does not return until
// the conversation b/w the chaincode program and the target
// peer has been completed
chat(convStarterMsg) {
this._stream = this._client.register();
this.msgQueueHandler = new MsgQueueHandler(this);

const stream = this._stream;
Expand Down Expand Up @@ -528,15 +554,11 @@ class ChaincodeSupportClient {
});
}


/*
* return a printable representation of this object
*/
return a printable representation of this object
*/
toString() {
return 'ChaincodeSupportClient : {' +
'url:' +
this._url +
'}';
return 'ChaincodeMessageHandler : {}';
}
}

Expand Down Expand Up @@ -723,7 +745,10 @@ function parseResponse(handler, res, method) {
}
}

module.exports = ChaincodeSupportClient;
module.exports = {
ChaincodeSupportClient,
ChaincodeMessageHandler
};

//
// The Endpoint class represents a remote grpc or grpcs target
Expand Down
Loading