Skip to content

Commit

Permalink
Initial implementation for sending endorser proposal
Browse files Browse the repository at this point in the history
To test, set up a network from the feature/convergence branch using a v1.0
docker-compose such as bddtests/bdd-docker/docker-compose-next-4.yml. Make
sure to map the ports by adding the "ports" section.

If you can't get this working in a native docker, you can run it inside
vagrant.

Run "node test/unit/endorser-tests.js"

In response to comments:
- merged the latest from master (changes from CR 1477)

Change-Id: Icee9600580be1dd42c1d1b44d57906bf84c06c94
Signed-off-by: Jim Zhang <[email protected]>
  • Loading branch information
jimthematrix committed Oct 6, 2016
1 parent c5dd336 commit 25cbf0e
Show file tree
Hide file tree
Showing 11 changed files with 949 additions and 178 deletions.
23 changes: 23 additions & 0 deletions build/tasks/eslint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
var gulp = require('gulp');
var eslint = require('gulp-eslint');

gulp.task('lint', function () {
return gulp.src(['**/*.js', '!node_modules/**', '!docs/**'])
.pipe(eslint(
{
env: ['es6', 'node'],
extends: 'eslint:recommended',
parserOptions: {
sourceType: 'module'
},
rules: {
indent: ['error', 'tab'],
'linebreak-style': ['error', 'unix'],
quotes: ['error', 'single'],
semi: ['error', 'always']
}
}
))
.pipe(eslint.format())
.pipe(eslint.failAfterError());
});
25 changes: 1 addition & 24 deletions gulpfile.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,9 @@
var requireDir = require('require-dir');
var gulp = require('gulp');

// Require all tasks in gulp/tasks, including subfolders
requireDir('./build/tasks', { recurse: true });

var gulp = require('gulp');
var eslint = require('gulp-eslint');

gulp.task('lint', function () {
return gulp.src(['**/*.js', '!node_modules/**', '!docs/**'])
.pipe(eslint(
{
env: ['es6', 'node'],
extends: 'eslint:recommended',
parserOptions: {
sourceType: 'module'
},
rules: {
indent: ['error', 'tab'],
'linebreak-style': ['error', 'unix'],
quotes: ['error', 'single'],
semi: ['error', 'always']
}
}
))
.pipe(eslint.format())
.pipe(eslint.failAfterError());
});

gulp.task('default', ['lint'], function () {
// This will only run if the lint task is successful...
});
2 changes: 1 addition & 1 deletion lib/Chain.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ var Chain = class {
*/
constructor(name) {
// Name of the chain is only meaningful to the client
this._name = '';
this._name = name;

// The peers on this chain to which the client can connect
this._peers = []; // Peer[]
Expand Down
140 changes: 140 additions & 0 deletions lib/Member.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,14 @@

var api = require('./api.js');
var util = require('util');
var fs = require('fs');
var stats = require('./stats.js');
var Peer = require('./Peer.js');
var sdkUtils = require('./utils.js');
var grpc = require('grpc');

var _ccProto = grpc.load(__dirname + '/protos/chaincode.proto').protos;
var _fabricProto = grpc.load(__dirname + '/protos/fabric_next.proto').protos;

/**
* Represents an authenticated user of the application or an entity used by a Peer node.
Expand Down Expand Up @@ -310,6 +317,91 @@ var Member = class {
});
}

/**
* Sends a deployment proposal to an endorser.
*
* @param {Object} request An object containing the following fields:
* endorserUrl
* chaincodePath
* fcn
* args
* @returns Promise for a ProposalResponse
*/
sendDeploymentProposal(request) {
// Verify that chaincodePath is being passed
if (!request.chaincodePath || request.chaincodePath === '') {
return Promise.reject(new Error('missing chaincodePath in Deployment proposal request'));
}

return new Promise(
function(resolve, reject) {
packageChaincode(request.chaincodePath, request.fcn, request.args)
.then(
function(data) {
var targzFilePath = data[0];
var hash = data[1];

// at this point, the targzFile has been successfully generated

// step 1: construct a ChaincodeSpec
var args = [];
args.push(Buffer.from(request.fcn ? request.fcn : 'init', 'utf8'));

for (let i=0; i<request.args.length; i++)
args.push(Buffer.from(request.args[i], 'utf8'));

let ccSpec = {
type: _ccProto.ChaincodeSpec.Type.GOLANG,
chaincodeID: {
name: hash
},
ctorMsg: {
args: args
}
};

// // step 2: construct the ChaincodeDeploymentSpec
let chaincodeDeploymentSpec = new _ccProto.ChaincodeDeploymentSpec();
chaincodeDeploymentSpec.setChaincodeSpec(ccSpec);

fs.readFile(targzFilePath, function(err, data) {
if(err) {
reject(new Error(util.format('Error reading deployment archive [%s]: %s', targzFilePath, err)));
}

chaincodeDeploymentSpec.setCodePackage(data);

let lcccSpec = {
type: _ccProto.ChaincodeSpec.Type.GOLANG,
chaincodeID: {
name: 'lccc'
},
ctorMsg: {
args: [Buffer.from('deploy', 'utf8'), Buffer.from('default', 'utf8'), chaincodeDeploymentSpec.toBuffer()]
}
};

// // step 4: construct the ChaincodeInvocationSpec to call the LCCC
let cciSpec = new _ccProto.ChaincodeInvocationSpec();
cciSpec.setChaincodeSpec(lcccSpec);

let proposal = {
type: _fabricProto.Proposal.Type.CHAINCODE,
id: 'someUniqueName',
payload: cciSpec.toBuffer()
};

let peer = new Peer(request.endorserUrl);
return peer.sendProposal(proposal);
});
},
function(err) {
reject(err);
}
);
});
}

/**
* Get the current state of this member as a string
* @return {string} The state of this member as a string
Expand Down Expand Up @@ -349,6 +441,54 @@ function toKeyValueStoreName(name) {
return 'member.' + name;
}

function packageChaincode(chaincodePath, fcn, args) {

// Determine the user's $GOPATH
let goPath = process.env['GOPATH'];

// Compose the path to the chaincode project directory
let projDir = goPath + '/src/' + chaincodePath;

// Compute the hash of the chaincode deployment parameters
let hash = sdkUtils.GenerateParameterHash(chaincodePath, fcn, args);

// Compute the hash of the project directory contents
hash = sdkUtils.GenerateDirectoryHash(goPath + '/src/', chaincodePath, hash);

// Compose the Dockerfile commands
let dockerFileContents = 'from hyperledger/fabric-baseimage\n' +
'COPY . $GOPATH/src/build-chaincode/\n' +
'WORKDIR $GOPATH\n\n' +
'RUN go install build-chaincode && cp src/build-chaincode/vendor/github.com/hyperledger/fabric/peer/core.yaml $GOPATH/bin && mv $GOPATH/bin/build-chaincode $GOPATH/bin/%s';

// Substitute the hashStrHash for the image name
dockerFileContents = util.format(dockerFileContents, hash);

return new Promise(function(resolve, reject) {
// Create a Docker file with dockerFileContents
let dockerFilePath = projDir + '/Dockerfile';
fs.writeFile(dockerFilePath, dockerFileContents, function(err) {
if (err) {
return reject(new Error(util.format('Error writing file [%s]: %s', dockerFilePath, err)));
}

// Create the .tar.gz file of the chaincode package
let targzFilePath = '/tmp/deployment-package.tar.gz';
// Create the compressed archive
sdkUtils.GenerateTarGz(projDir, targzFilePath)
.then(
function(targzFilePath) {
// return both the hash and the tar.gz file path as resolved data
return resolve([targzFilePath, hash]);
},
function(err) {
return reject(err);
}
);
});
});
}

module.exports = Member;


82 changes: 82 additions & 0 deletions lib/Peer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
Copyright 2016 IBM All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict';

var api = require('./api.js');
var utils = require('./utils.js');
var grpc = require('grpc');

var _fabricProto = grpc.load(__dirname + '/protos/fabric_next.proto').protos;

/**
* The Peer class represents a peer in the target blockchain network to which
* HFC sends endorsement proposals, transaction ordering or query requests.
*
* @class
*/
var Peer = class {

/**
* Constructs a Peer given its endpoint configuration settings
* and returns the new Peer.
*
* @param {string} url The URL with format of "grpcs://host:port".
* @param {Chain} chain The chain of which this peer is a member.
* @param {string} pem The certificate file, in PEM format,
* to use with the gRPC protocol (that is, with TransportCredentials).
* Required when using the grpcs protocol.
*/
constructor(url) {
this._url = url;
this._ep = new utils.Endpoint(url, null);
this._endorserClient = new _fabricProto.Endorser(this._ep.addr, this._ep.creds);
}

/**
* Get the URL of the peer.
* @returns {string} Get the URL associated with the peer.
*/
getUrl() {
return this._url;
}

/**
* Send an endorsement proposal to an endorser.
*
* @param {Object} proposal A proposal of type Proposal
* @returns Promise for a ProposalResponse
*/
sendProposal(proposal) {
var self = this;

// Send the transaction to the peer node via grpc
// The rpc specification on the peer side is:
// rpc ProcessProposal(Proposal) returns (ProposalResponse) {}
return new Promise(function(resolve, reject) {
self._endorserClient.processProposal(proposal, function (err, response) {
if (err) {
reject(new Error(err));
}

console.log('Response code:' + JSON.stringify(response.response.status));
resolve(response.response.status);
});
});
}
};

module.exports = Peer;
7 changes: 4 additions & 3 deletions lib/impl/FileKeyValueStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
'use strict';

var api = require('../api.js');
var fs = require('fs');
var fs = require('fs-extra');
var path = require('path');
var utils = require('../utils');

/**
* This is a default implementation of the [KeyValueStore]{@link module:api.KeyValueStore} API.
Expand All @@ -42,8 +43,8 @@ var KeyValueStore = class extends api.KeyValueStore {
super();

this._dir = options.path;
if (!fs.existsSync(this._dir)) {
fs.mkdirSync(this._dir);
if (!utils.existsSync(this._dir)) {
fs.mkdirsSync(this._dir);
}
}

Expand Down
Loading

0 comments on commit 25cbf0e

Please sign in to comment.