Skip to content

Commit

Permalink
[WIP] Use ripple-keypairs and ripple-address-codec
Browse files Browse the repository at this point in the history
  • Loading branch information
sublimator committed Aug 11, 2015
1 parent fcbe7d3 commit ea6fc0e
Show file tree
Hide file tree
Showing 15 changed files with 219 additions and 421 deletions.
70 changes: 70 additions & 0 deletions npm-shrinkwrap.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Expand Up @@ -25,6 +25,8 @@
"lodash": "^3.1.0",
"lru-cache": "~2.5.0",
"ripple-lib-transactionparser": "^0.5.0",
"ripple-address-codec": "^1.6.0",
"ripple-keypairs": "^0.7.3",
"ripple-wallet-generator": "^1.0.3",
"sjcl-extended": "ripple/sjcl-extended#1.0.3",
"ws": "~0.7.1"
Expand Down Expand Up @@ -59,7 +61,7 @@
"prepublish": "npm run clean && npm run compile",
"test": "istanbul test _mocha",
"coveralls": "cat ./coverage/lcov.info | coveralls",
"lint": "if ! [ -f eslintrc ]; then curl -o eslintrc 'https://raw.githubusercontent.com/ripple/javascript-style-guide/es6/eslintrc'; echo 'plugins:\n - flowtype' >> eslintrc; fi; eslint --reset -c eslintrc src/",
"lint": "if ! [ -f eslintrc ]; then curl -o eslintrc 'https://raw.githubusercontent.com/ripple/javascript-style-guide/es6/eslintrc'; echo 'plugins:\n - flowtype' >> eslintrc; fi; eslint -c eslintrc src/",
"perf": "./scripts/perf_test.sh"
},
"repository": {
Expand Down
6 changes: 2 additions & 4 deletions src/api/common/validate.js
Expand Up @@ -16,10 +16,8 @@ function validateAddressAndSecret(obj: {address: string, secret: string}): void
if (!secret) {
throw error('Parameter missing: secret');
}
try {
core.Seed.from_json(secret).get_key(address);
} catch (exception) {
throw error('secret does not match address');
if (!core.Seed.from_json(secret).is_valid()) {
throw error('secret is invalid');
}
}

Expand Down
15 changes: 4 additions & 11 deletions src/api/transaction/sign.js
Expand Up @@ -15,15 +15,13 @@ const validate = utils.common.validate;
* some arbitrary string. For example "TXN".
*/
const HASH_TX_ID = 0x54584E00; // 'TXN'
const HASH_TX_SIGN = 0x53545800; // 'STX'
const HASH_TX_SIGN_TESTNET = 0x73747800; // 'stx'

function getKeyPair(secret) {
return core.Seed.from_json(secret).get_key();
}

function getPublicKeyHex(keypair) {
return keypair.to_hex_pub();
return keypair.pubKeyHex();
}

function serialize(txJSON) {
Expand All @@ -34,17 +32,12 @@ function hashSerialization(serialized, prefix) {
return serialized.hash(prefix || HASH_TX_ID).to_hex();
}

function hashJSON(txJSON, prefix) {
return hashSerialization(serialize(txJSON), prefix);
}

function signingHash(txJSON, isTestNet=false) {
return hashJSON(txJSON, isTestNet ? HASH_TX_SIGN_TESTNET : HASH_TX_SIGN);
function signingData(txJSON) {
return core.Transaction.from_json(txJSON).signingData().buffer;
}

function computeSignature(txJSON, keypair) {
const signature = keypair.sign(signingHash(txJSON));
return core.sjcl.codec.hex.fromBits(signature).toUpperCase();
return keypair.signHex(signingData(txJSON));
}

function sign(txJSON: {Account: string; SigningPubKey: string,
Expand Down
152 changes: 49 additions & 103 deletions src/core/base.js
@@ -1,18 +1,19 @@
'use strict';
const _ = require('lodash');

const sjcl = require('./utils').sjcl;
const utils = require('./utils');
const extend = require('extend');
const convertBase = require('./baseconverter');

const Base = {};

const alphabets = Base.alphabets = {
// Someone might be using these :)
Base.alphabets = {
ripple: 'rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz',
tipple: 'RPShNAF39wBUDnEGHJKLM4pQrsT7VWXYZ2bcdeCg65jkm8ofqi1tuvaxyz',
bitcoin: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
};

const {encode, decode} = require('ripple-address-codec');

extend(Base, {
VER_NONE: 1,
VER_NODE_PUBLIC: 28,
Expand All @@ -21,134 +22,79 @@ extend(Base, {
VER_ACCOUNT_PUBLIC: 35,
VER_ACCOUNT_PRIVATE: 34,
VER_FAMILY_GENERATOR: 41,
VER_FAMILY_SEED: 33
});

function sha256(bytes) {
return sjcl.codec.bytes.fromBits(
sjcl.hash.sha256.hash(sjcl.codec.bytes.toBits(bytes)));
}

function encodeString(alphabet, input) {
if (input.length === 0) {
return '';
}

const leadingZeros = _.takeWhile(input, function(d) {
return d === 0;
});
const out = convertBase(input, 256, 58).map(function(digit) {
if (digit < 0 || digit >= alphabet.length) {
throw new Error('Value ' + digit + ' is out of bounds for encoding');
}
return alphabet[digit];
});
const prefix = leadingZeros.map(function() {
return alphabet[0];
});
return prefix.concat(out).join('');
}

function decodeString(indexes, input) {
if (input.length === 0) {
return [];
}

const input58 = input.split('').map(function(c) {
const charCode = c.charCodeAt(0);
if (charCode >= indexes.length || indexes[charCode] === -1) {
throw new Error('Character ' + c + ' is not valid for encoding');
}
return indexes[charCode];
});
const leadingZeros = _.takeWhile(input58, function(d) {
return d === 0;
});
const out = convertBase(input58, 58, 256);
return leadingZeros.concat(out);
}

function Base58(alphabet) {
const indexes = utils.arraySet(128, -1);
for (let i = 0; i < alphabet.length; i++) {
indexes[alphabet.charCodeAt(i)] = i;
}
return {
decode: decodeString.bind(null, indexes),
encode: encodeString.bind(null, alphabet)
};
}

Base.encoders = {};
Object.keys(alphabets).forEach(function(alphabet) {
Base.encoders[alphabet] = new Base58(alphabets[alphabet]);
VER_FAMILY_SEED: 33,
VER_ED25519_SEED: [0x01, 0xE1, 0x4B]
});

// --> input: big-endian array of bytes.
// <-- string at least as long as input.
Base.encode = function(input, alpha) {
return this.encoders[alpha || 'ripple'].encode(input);
Base.encode = function(input, alphabet) {
return encode(input, {alphabet});
};

// --> input: String
// <-- array of bytes or undefined.
Base.decode = function(input, alpha) {
Base.decode = function(input, alphabet) {
if (typeof input !== 'string') {
return undefined;
}
try {
return this.encoders[alpha || 'ripple'].decode(input);
return decode(input, {alphabet});
} catch (e) {
return undefined;
}
};

Base.verify_checksum = function(bytes) {
const computed = sha256(sha256(bytes.slice(0, -4))).slice(0, 4);
const checksum = bytes.slice(-4);
return _.isEqual(computed, checksum);
};

// --> input: Array
// <-- String
Base.encode_check = function(version, input, alphabet) {
const buffer = [].concat(version, input);
const check = sha256(sha256(buffer)).slice(0, 4);

return Base.encode([].concat(buffer, check), alphabet);
return encode(input, {version, alphabet});
};

// --> input : String
// <-- NaN || sjcl.bn
Base.decode_check = function(version, input, alphabet) {
const buffer = Base.decode(input, alphabet);

if (!buffer || buffer.length < 5) {
return NaN;
}

// Single valid version
if (typeof version === 'number' && buffer[0] !== version) {
return NaN;
}

// Multiple allowed versions
if (Array.isArray(version) && _.every(version, function(v) {
return v !== buffer[0];
})) {
try {
const decoded = decode(input, {version, alphabet});
return sjcl.bn.fromBits(sjcl.codec.bytes.toBits(decoded));
} catch (e) {
return NaN;
}
};

if (!Base.verify_checksum(buffer)) {
return NaN;
/**
*
* Decodes (with check) `input` into `expectedLength` number of bytes, trying
* multiple possible `versions`.
*
*
* @param {Array} versions - Array of possible versions. Each element can be
* an array of bytes, or just a single byte
* @param {String} input - the base58 encoded input
* @param {Number} expectedLength - of decoded bytes
* @param {String} [alphabet='ripple'] - name of alphabet to use. eg. 'bitcoin'
*
* If decoded bytes is not the `expectedLength` or none of the `versions`
* matched, will return struct with null members.
*
* @return {Object} result - struct
* @return {sjcl.bn} result.value - sjcl.bn interpretation of decoded bytes
* @return {Array|Number} result.version - the matching element from `versions`
* param
*/
Base.decode_multi_versioned = function(versions, input, expectedLength,
alphabet) {
try {
// Will return valid struct or throw
const {bytes, version} = decode(input, {versions, expectedLength,
alphabet});
return {
version,
value: sjcl.bn.fromBits(sjcl.codec.bytes.toBits(bytes))
};
} catch (e) {
return {value: null, version: null};
}

// We'll use the version byte to add a leading zero, this ensures JSBN doesn't
// intrepret the value as a negative number
buffer[0] = 0;

return sjcl.bn.fromBits(
sjcl.codec.bytes.toBits(buffer.slice(0, -4)));
};

exports.Base = Base;

0 comments on commit ea6fc0e

Please sign in to comment.