#!/usr/bin/env node
This script shows how to use the SDK to generate and decrypt æternity compliant key pairs, as well as encode and decode transactions.
/*
* ISC License (ISC)
* Copyright (c) 2018 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.
*/
'use strict'
We’ll only load the Crypto
module from the SDK to work with keys and
transactions.
const { Crypto } = require('@aeternity/aepp-sdk')
const program = require('commander')
const fs = require('fs')
const prompt = require('prompt')
const path = require('path')
The prompt
library provides concealed input of passwords.
const promptSchema = {
properties: {
password: {
type: 'string',
description: 'Enter your password',
hidden: true,
required: true,
replace: '*',
conform: function (value) {
return true;
}
}
}
}
function extractReadableKeys (dir, options) {
const pwd = options.input
prompt.start()
prompt.get(promptSchema, (err, { password }) => {
const key = fs.readFileSync(path.join(pwd, dir, 'sign_key'))
const pubKey = fs.readFileSync(path.join(pwd, dir, 'sign_key.pub'))
const decrypted = Crypto.decryptPrivateKey(password, key)
const privateHex = Buffer.from(decrypted).toString('hex')
const decryptedPub = Crypto.decryptPubKey(password, pubKey)
console.log(`Private key (hex): ${privateHex}`)
console.log(`Public key (base check): ak\$${Crypto.encodeBase58Check(decryptedPub)}`)
console.log(`Public key (hex): ${decryptedPub.toString('hex')}`)
})
}
function generateKeyPair (name, { output }) {
const { pub, priv } = Crypto.generateKeyPair()
const data = [[path.join(output, name), priv],
[path.join(output, `${name}.pub`), pub]]
data.forEach(([path, data]) => {
fs.writeFileSync(path, data)
console.log(`Wrote ${path}`)
})
}
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)
} else if (privKey) {
return Buffer.from(privKey, 'hex')
} else {
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(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))
}
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))
}
The commander
library provides maximum command line parsing convenience.
program.version('0.1.0')
program
.command('decrypt <directory>')
.description('Decrypts public and private key to readable formats for testing purposes')
.option('-i, --input [directory]', 'Directory where to look for keys', '.')
.action(extractReadableKeys)
program
.command('genkey <keyname>')
.description('Generate keypair')
.option('-o, --output [directory]', 'Output directory for the keys', '.')
.action(generateKeyPair)
program
.command('sign <tx> [privkey]')
.option('-p, --password [password]', 'password of the private key')
.option('-f, --file [file]', 'private key file')
.action(signTx)
program
.command('unpack <tx>')
.action(unpackTx)
program.parse(process.argv)
if (program.args.length === 0) program.help()