Skip to content

Commit

Permalink
fix: commands in crypto module
Browse files Browse the repository at this point in the history
  • Loading branch information
davidyuk committed Jul 6, 2022
1 parent 03c645c commit 6d6da0c
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 60 deletions.
92 changes: 33 additions & 59 deletions src/commands/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,78 +17,52 @@

import { Command } from 'commander';
import fs from 'fs';
import { Crypto } from '@aeternity/aepp-sdk';

// ## Transaction Deserialization
//
// This helper function deserialized the transaction `tx` and prints the result.
function unpackTx(tx) {
const deserializedTx = Crypto.deserialize(Crypto.decodeTx(tx));
console.log(JSON.stringify(deserializedTx, undefined, 2));
}

// ## Address decoder
//
// This helper function decodes address(base58) to hex
function decodeAddress(address) {
const decoded = Crypto.decodeBase58Check(address.split('_')[1]).toString('hex');
console.log(`Decoded address (hex): ${decoded}`);
}
import {
Crypto, TxBuilderHelper, TxBuilder, SCHEMA,
} from '@aeternity/aepp-sdk';

const program = new Command().name('aecli crypto');

// ## Transaction Signing
//
// This function shows how to use a compliant private key to sign an æternity
// transaction and turn it into an RLP-encoded tuple ready for mining
function signTx(tx, privKey) {
if (!tx.match(/^tx_.+/)) {
throw Error('Not a valid transaction');
}

const binaryKey = (() => {
if (program.file) {
return fs.readFileSync(program.file);
} if (privKey) {
return Buffer.from(privKey, 'hex');
}
throw Error('Must provide either [privkey] or [file]');
})();

const decryptedKey = program.password ? Crypto.decryptKey(program.password, binaryKey) : binaryKey;

// Split the base58Check part of the transaction
const base58CheckTx = tx.split('_')[1];
// ... and sign the binary create_contract transaction
const binaryTx = Crypto.decodeBase58Check(base58CheckTx);

const signature = Crypto.sign(Buffer.concat([Buffer.from(Crypto.NETWORK_ID), binaryTx]), decryptedKey);

// the signed tx deserializer expects a 4-tuple:
// <tag, version, signatures_array, binary_tx>
const unpackedSignedTx = [
Buffer.from([11]),
Buffer.from([1]),
[Buffer.from(signature)],
binaryTx,
];

console.log(Crypto.encodeTx(unpackedSignedTx));
}

program
.command('decode <base58address>')
.description('Decodes base58 address to hex')
.action(decodeAddress);
// ## Address decoder
// This helper function decodes address(base58) to hex
.action((address) => {
const decoded = TxBuilderHelper.decode(address, 'ak').toString('hex');
console.log(`Decoded address (hex): ${decoded}`);
});

program
.command('sign <tx> [privkey]')
.option('-p, --password [password]', 'password of the private key')
.option('-f, --file [file]', 'private key file')
.action(signTx);
.option('--networkId [networkId]', 'Network id', 'ae_mainnet')
// ## Transaction Signing
//
// This function shows how to use a compliant private key to sign an æternity
// transaction and turn it into an RLP-encoded tuple ready for mining
.action((tx, privKey, { networkId, password, file }) => {
const binaryKey = (() => {
if (file) return fs.readFileSync(file);
if (privKey) return Buffer.from(privKey, 'hex');
throw new Error('Must provide either [privkey] or [file]');
})();
const decryptedKey = password ? Crypto.decryptKey(password, binaryKey) : binaryKey;
const encodedTx = TxBuilderHelper.decode(tx, 'tx');
const signature = Crypto.sign(Buffer.concat([Buffer.from(networkId), encodedTx]), decryptedKey);
console.log(TxBuilder.buildTx({ encodedTx, signatures: [signature] }, SCHEMA.TX_TYPE.signed).tx);
});

program
.command('unpack <tx>')
.action(unpackTx);
// ## Transaction Deserialization
// This helper function deserialized the transaction `tx` and prints the result.
.action((tx) => {
const unpackedTx = TxBuilder.unpackTx(tx);
delete unpackedTx.rlpEncoded;
delete unpackedTx.binary;
console.log(JSON.stringify(unpackedTx, undefined, 2));
});

export default program;
63 changes: 63 additions & 0 deletions test/crypto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* ISC License (ISC)
* Copyright (c) 2022 aeternity developers
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/

import {
after, before, describe, it,
} from 'mocha';
import { expect } from 'chai';
import { Crypto, TxBuilder } from '@aeternity/aepp-sdk';
import { executeProgram, getSdk } from './index';
import cryptoProgramFactory from '../src/commands/crypto';

const executeCrypto = (args) => executeProgram(cryptoProgramFactory, args);

describe('CLI Crypto Module', () => {
let sdk;

before(async () => {
sdk = await getSdk();
});

after(() => sdk.removeWallet());

it('decodes address', async () => {
const output = await executeCrypto([
'decode', 'ak_MA8Qe8ac7e9EARYK7fQxEqFufRGrG1i6qFvHA21eXXMDcnmuc',
]);
expect(output).to.include('2dc51099d9b3921f5578d5968c2b0b5a37d11a6cc514f13862f3a9af7f0ab05f');
});

it('signs transaction', async () => {
const { secretKey } = Crypto.generateKeyPair();
const output = await executeCrypto([
'sign',
'tx_+F0MAaEB4TK48d23oE5jt/qWR5pUu8UlpTGn8bwM5JISGQMGf7ChAeEyuPHdt6BOY7f6lkeaVLvFJaUxp/G8DOSSEhkDBn+wiBvBbWdOyAAAhg9e1n8oAAABhHRlc3QLK3OW',
secretKey.toString('hex'),
]);
expect(TxBuilder.unpackTx(output).txType).to.equal('signedTx');
});

it('unpacks transaction', async () => {
const output = await executeCrypto([
'unpack', 'tx_+F0MAaEB4TK48d23oE5jt/qWR5pUu8UlpTGn8bwM5JISGQMGf7ChAeEyuPHdt6BOY7f6lkeaVLvFJaUxp/G8DOSSEhkDBn+wiBvBbWdOyAAAhg9e1n8oAAABhHRlc3QLK3OW',
]);
expect(output).to.include('spendTx');
expect(output).to.include('"recipientId": "ak_2iBPH7HUz3cSDVEUWiHg76MZJ6tZooVNBmmxcgVK6VV8KAE688"');
expect(output).to.include('"amount": "2000000000000000000"');
expect(output).to.include('"payload": "ba_dGVzdJVNWkk="');
});
});
2 changes: 1 addition & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export async function executeProgram(program, args) {
try {
await program.parseAsync([
...args,
...['config'].includes(args[0]) ? [] : ['--url', url],
...['config', 'decode', 'sign', 'unpack'].includes(args[0]) ? [] : ['--url', url],
...args[0] === 'contract' ? ['--compilerUrl', compilerUrl] : [],
], { from: 'user' });
} finally {
Expand Down

0 comments on commit 6d6da0c

Please sign in to comment.