Skip to content

Commit

Permalink
feat(*): contract instance signing and verification
Browse files Browse the repository at this point in the history
Signed-off-by: sanket shevkar <[email protected]>
  • Loading branch information
sanketshevkar authored and jeromesimeon committed Oct 28, 2021
1 parent 24fd02e commit bafc232
Show file tree
Hide file tree
Showing 8 changed files with 498 additions and 2 deletions.
80 changes: 80 additions & 0 deletions packages/cicero-cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,86 @@ require('yargs')
return;
}
})
.command('verify', 'verify the signatures of party/individuals who have signed the contarct', (yargs) => {
yargs.option('contract', {
describe: 'path to a smart legal contract slc file',
type: 'string'
});
yargs.option('warnings', {
describe: 'print warnings',
type: 'boolean',
default: false
});
}, (argv) => {

try {
argv = Commands.validateVerifyArgs(argv);
const options = {
warnings: argv.warnings,
};
return Commands.verify(argv.contract, options)
.then((result) => {
if(result) {Logger.info(JSON.stringify(result));}
})
.catch((err) => {
Logger.error(err.message);
});
} catch (err){
Logger.error(err.message);
}
})
.command('sign', 'sign a contract', (yargs) => {
yargs.option('contract', {
describe: 'path to a smart legal contract slc file',
type: 'string'
});
yargs.option('template', {
describe: 'path to a template',
type: 'string'
});
yargs.option('warnings', {
describe: 'print warnings',
type: 'boolean',
default: false
});
yargs.option('keystore', {
describe: 'p12 keystore path',
type: 'string',
default: null
});
yargs.option('passphrase', {
describe: 'p12 keystore passphrase',
type: 'string',
default: null
});
yargs.option('signatory', {
describe: 'name of the signatory',
type: 'string',
default: null
});
yargs.option('output', {
describe: 'file name for new archive',
type: 'string',
default: null
});
}, (argv) => {

try {
argv = Commands.validateSignArgs(argv);
const options = {
warnings: argv.warnings,
};
return Commands.sign(argv.contract, argv.keystore, argv.passphrase, argv.signatory, argv.output, options)
.then((result) => {
if(result) {Logger.info('Contract has been successfully signed');}
})
.catch((err) => {
Logger.error(err.message);
});
} catch (err){
Logger.error(err.message);
}
})
.command('get', 'save local copies of external dependencies', (yargs) => {
yargs.option('template', {
describe: 'path to the template',
Expand Down
86 changes: 86 additions & 0 deletions packages/cicero-cli/lib/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,92 @@ class Commands {
return argv;
}

/**
* Set default params before we create an archive using a template
*
* @param {object} argv the inbound argument values object
* @returns {object} a modfied argument object
*/
static validateSignArgs(argv) {
argv = Commands.validateCommonArgs(argv);
if (argv.template) {
argv = Commands.validateDataArgs(argv);
}

if(!argv.target){
Logger.info('Using ergo as the default target for the archive.');
argv.target = 'ergo';
}

return argv;
}

/**
* Create an archive using a template
*
* @param {string} slcPath - path to the slc archive
* @param {string} keystore - path to the keystore
* @param {string} passphrase - passphrase of the keystore
* @param {string} signatory - name of the person/party signing the contract
* @param {string} outputPath - to the archive file
* @param {Object} [options] - an optional set of options
* @returns {object} Promise to the code creating an archive
*/
static async sign(slcPath, keystore, passphrase, signatory, outputPath, options) {
return Commands.loadInstance(null, slcPath, options)
.then(async (instance) => {
const p12File = fs.readFileSync(keystore, { encoding: 'base64' });
const archive = await instance.signContract(p12File, passphrase, signatory);
let file;
if (outputPath) {
file = outputPath;
}
else {
const instanceName = instance.getIdentifier();
const version = instance.contractSignatures.length;
file = `${instanceName}.v${version}.slc`;
}
Logger.info('Creating archive: ' + file);
fs.writeFileSync(file, archive);
return true;
});
}

/**
* Set default params before we create an archive using a template
* @param {object} argv the inbound argument values object
* @returns {object} a modfied argument object
*/
static validateVerifyArgs(argv) {
argv = Commands.validateCommonArgs(argv);

if(!argv.target){
Logger.info('Using ergo as the default target for the archive.');
argv.target = 'ergo';
}

return argv;
}

/**
* Create an archive using a template
*
* @param {string} contractPath - path to the template directory or archive
* @param {Object} [options] - an optional set of options
* @returns {object} Promise to the code creating an archive
*/
static async verify(contractPath, options) {
return Commands.loadInstance(null, contractPath, options)
.then(async (instance) => {
const result = await instance.verifySignatures();
if (result) {
Logger.info('All signatures verified successfully');
} else {
Logger.error('Contract signature verification failed');
}
});
}

/**
* Set default params before we create an instance archive
*
Expand Down
52 changes: 52 additions & 0 deletions packages/cicero-cli/test/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -1307,6 +1307,58 @@ describe('#archive', async () => {

});

describe('#validateSignArgs', () => {
it('no args specified', () => {
process.chdir(path.resolve(__dirname, 'data/signContract/'));
const args = Commands.validateSignArgs({
_: ['sign']
});
args.template.should.match(/cicero-cli[/\\]test[/\\]data[/\\]signContract$/);
args.target.should.match(/ergo/);
});
it('only target arg specified', () => {
process.chdir(path.resolve(__dirname, 'data/signContract/'));
const args = Commands.validateSignArgs({
_: ['sign'],
target: 'ergo'
});
args.template.should.match(/cicero-cli[/\\]test[/\\]data[/\\]signContract$/);
args.target.should.match(/ergo/);
});
it('template arg specified', () => {
process.chdir(path.resolve(__dirname));
const args = Commands.validateSignArgs({
_: ['sign', 'data/signContract/']
});
args.template.should.match(/cicero-cli[/\\]test[/\\]data[/\\]signContract$/);
args.target.should.match(/ergo/);
});
it('verbose flag specified', () => {
process.chdir(path.resolve(__dirname, 'data/signContract/'));
Commands.validateSignArgs({
_: ['sign'],
verbose: true
});
});
it('bad package.json', () => {
process.chdir(path.resolve(__dirname, 'data/'));
(() => Commands.validateVerifyArgs({
_: ['sign']
})).should.throw(' not a valid cicero template. Make sure that package.json exists and that it has a cicero entry.');
});
});

describe('#sign', async () => {
it('should sign the contract for a party/individual', async () => {
const slcPath = path.resolve(__dirname, 'data/signContract/latedeliveryandpenalty@0.17.0-d0c1a14e8a7af52e0927a23b8b30af3b5a75bee1ab788a15736e603b88a6312c.slc');
const keystore = path.resolve(__dirname, 'data/keystore.p12');
const signatory = 'partyA';
const outputPath = path.resolve(__dirname, 'data/');
const result = await Commands.sign(slcPath, keystore, 'password', signatory, outputPath);
result.should.be.true;
});
});

describe('#validateInstantiateArgs', () => {
it('no args specified', () => {
process.chdir(path.resolve(__dirname, 'data/latedeliveryandpenalty/'));
Expand Down
Loading

0 comments on commit bafc232

Please sign in to comment.