mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-21 12:45:50 +00:00
Use ripple-keypairs and ripple-address-codec
This commit is contained in:
70
npm-shrinkwrap.json
generated
70
npm-shrinkwrap.json
generated
@@ -102,6 +102,76 @@
|
|||||||
"version": "2.5.2",
|
"version": "2.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.2.tgz"
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.2.tgz"
|
||||||
},
|
},
|
||||||
|
"ripple-address-codec": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"from": "ripple-address-codec@>=1.6.0 <2.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"hash.js": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"from": "hash.js@>=1.0.3 <2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"from": "inherits@>=2.0.1 <3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-address-codec": {
|
||||||
|
"version": "0.6.0",
|
||||||
|
"from": "x-address-codec@>=0.6.0 <0.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/x-address-codec/-/x-address-codec-0.6.0.tgz",
|
||||||
|
"dependencies": {
|
||||||
|
"base-x": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"from": "base-x@>=1.0.1 <2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/base-x/-/base-x-1.0.1.tgz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ripple-keypairs": {
|
||||||
|
"version": "0.7.3",
|
||||||
|
"from": "ripple-keypairs@>=0.7.3 <0.8.0",
|
||||||
|
"dependencies": {
|
||||||
|
"bn.js": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"from": "bn.js@>=3.1.1 <4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-3.1.1.tgz"
|
||||||
|
},
|
||||||
|
"brorand": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"from": "brorand@>=1.0.5 <2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.0.5.tgz"
|
||||||
|
},
|
||||||
|
"elliptic": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"from": "elliptic@>=5.1.0 <6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-5.1.0.tgz",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"from": "inherits@>=2.0.1 <3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hash.js": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"from": "hash.js@>=1.0.3 <2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"from": "inherits@>=2.0.1 <3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"ripple-lib-transactionparser": {
|
"ripple-lib-transactionparser": {
|
||||||
"version": "0.5.0",
|
"version": "0.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/ripple-lib-transactionparser/-/ripple-lib-transactionparser-0.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/ripple-lib-transactionparser/-/ripple-lib-transactionparser-0.5.0.tgz",
|
||||||
|
|||||||
@@ -25,6 +25,8 @@
|
|||||||
"lodash": "^3.1.0",
|
"lodash": "^3.1.0",
|
||||||
"lru-cache": "~2.5.0",
|
"lru-cache": "~2.5.0",
|
||||||
"ripple-lib-transactionparser": "^0.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",
|
"ripple-wallet-generator": "^1.0.3",
|
||||||
"sjcl-extended": "ripple/sjcl-extended#1.0.3",
|
"sjcl-extended": "ripple/sjcl-extended#1.0.3",
|
||||||
"ws": "~0.7.1"
|
"ws": "~0.7.1"
|
||||||
@@ -59,7 +61,7 @@
|
|||||||
"prepublish": "npm run clean && npm run compile",
|
"prepublish": "npm run clean && npm run compile",
|
||||||
"test": "istanbul test _mocha",
|
"test": "istanbul test _mocha",
|
||||||
"coveralls": "cat ./coverage/lcov.info | coveralls",
|
"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"
|
"perf": "./scripts/perf_test.sh"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -16,10 +16,8 @@ function validateAddressAndSecret(obj: {address: string, secret: string}): void
|
|||||||
if (!secret) {
|
if (!secret) {
|
||||||
throw error('Parameter missing: secret');
|
throw error('Parameter missing: secret');
|
||||||
}
|
}
|
||||||
try {
|
if (!core.Seed.from_json(secret).is_valid()) {
|
||||||
core.Seed.from_json(secret).get_key(address);
|
throw error('secret is invalid');
|
||||||
} catch (exception) {
|
|
||||||
throw error('secret does not match address');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,15 +15,13 @@ const validate = utils.common.validate;
|
|||||||
* some arbitrary string. For example "TXN".
|
* some arbitrary string. For example "TXN".
|
||||||
*/
|
*/
|
||||||
const HASH_TX_ID = 0x54584E00; // 'TXN'
|
const HASH_TX_ID = 0x54584E00; // 'TXN'
|
||||||
const HASH_TX_SIGN = 0x53545800; // 'STX'
|
|
||||||
const HASH_TX_SIGN_TESTNET = 0x73747800; // 'stx'
|
|
||||||
|
|
||||||
function getKeyPair(secret) {
|
function getKeyPair(secret) {
|
||||||
return core.Seed.from_json(secret).get_key();
|
return core.Seed.from_json(secret).get_key();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPublicKeyHex(keypair) {
|
function getPublicKeyHex(keypair) {
|
||||||
return keypair.to_hex_pub();
|
return keypair.pubKeyHex();
|
||||||
}
|
}
|
||||||
|
|
||||||
function serialize(txJSON) {
|
function serialize(txJSON) {
|
||||||
@@ -34,17 +32,12 @@ function hashSerialization(serialized, prefix) {
|
|||||||
return serialized.hash(prefix || HASH_TX_ID).to_hex();
|
return serialized.hash(prefix || HASH_TX_ID).to_hex();
|
||||||
}
|
}
|
||||||
|
|
||||||
function hashJSON(txJSON, prefix) {
|
function signingData(txJSON) {
|
||||||
return hashSerialization(serialize(txJSON), prefix);
|
return core.Transaction.from_json(txJSON).signingData().buffer;
|
||||||
}
|
|
||||||
|
|
||||||
function signingHash(txJSON, isTestNet=false) {
|
|
||||||
return hashJSON(txJSON, isTestNet ? HASH_TX_SIGN_TESTNET : HASH_TX_SIGN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function computeSignature(txJSON, keypair) {
|
function computeSignature(txJSON, keypair) {
|
||||||
const signature = keypair.sign(signingHash(txJSON));
|
return keypair.signHex(signingData(txJSON));
|
||||||
return core.sjcl.codec.hex.fromBits(signature).toUpperCase();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function sign(txJSON: {Account: string; SigningPubKey: string,
|
function sign(txJSON: {Account: string; SigningPubKey: string,
|
||||||
|
|||||||
123
src/core/base.js
123
src/core/base.js
@@ -1,18 +1,11 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
const _ = require('lodash');
|
|
||||||
const sjcl = require('./utils').sjcl;
|
const sjcl = require('./utils').sjcl;
|
||||||
const utils = require('./utils');
|
|
||||||
const extend = require('extend');
|
const extend = require('extend');
|
||||||
const convertBase = require('./baseconverter');
|
const {encode, decode} = require('ripple-address-codec');
|
||||||
|
|
||||||
const Base = {};
|
const Base = {};
|
||||||
|
|
||||||
const alphabets = Base.alphabets = {
|
|
||||||
ripple: 'rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz',
|
|
||||||
tipple: 'RPShNAF39wBUDnEGHJKLM4pQrsT7VWXYZ2bcdeCg65jkm8ofqi1tuvaxyz',
|
|
||||||
bitcoin: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
|
||||||
};
|
|
||||||
|
|
||||||
extend(Base, {
|
extend(Base, {
|
||||||
VER_NONE: 1,
|
VER_NONE: 1,
|
||||||
VER_NODE_PUBLIC: 28,
|
VER_NODE_PUBLIC: 28,
|
||||||
@@ -21,134 +14,44 @@ extend(Base, {
|
|||||||
VER_ACCOUNT_PUBLIC: 35,
|
VER_ACCOUNT_PUBLIC: 35,
|
||||||
VER_ACCOUNT_PRIVATE: 34,
|
VER_ACCOUNT_PRIVATE: 34,
|
||||||
VER_FAMILY_GENERATOR: 41,
|
VER_FAMILY_GENERATOR: 41,
|
||||||
VER_FAMILY_SEED: 33
|
VER_FAMILY_SEED: 33,
|
||||||
});
|
VER_ED25519_SEED: [0x01, 0xE1, 0x4B]
|
||||||
|
|
||||||
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]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// --> input: big-endian array of bytes.
|
// --> input: big-endian array of bytes.
|
||||||
// <-- string at least as long as input.
|
// <-- string at least as long as input.
|
||||||
Base.encode = function(input, alpha) {
|
Base.encode = function(input, alphabet) {
|
||||||
return this.encoders[alpha || 'ripple'].encode(input);
|
return encode(input, {alphabet});
|
||||||
};
|
};
|
||||||
|
|
||||||
// --> input: String
|
// --> input: String
|
||||||
// <-- array of bytes or undefined.
|
// <-- array of bytes or undefined.
|
||||||
Base.decode = function(input, alpha) {
|
Base.decode = function(input, alphabet) {
|
||||||
if (typeof input !== 'string') {
|
if (typeof input !== 'string') {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return this.encoders[alpha || 'ripple'].decode(input);
|
return decode(input, {alphabet});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return undefined;
|
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
|
// --> input: Array
|
||||||
// <-- String
|
// <-- String
|
||||||
Base.encode_check = function(version, input, alphabet) {
|
Base.encode_check = function(version, input, alphabet) {
|
||||||
const buffer = [].concat(version, input);
|
return encode(input, {version, alphabet});
|
||||||
const check = sha256(sha256(buffer)).slice(0, 4);
|
|
||||||
|
|
||||||
return Base.encode([].concat(buffer, check), alphabet);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// --> input : String
|
// --> input : String
|
||||||
// <-- NaN || sjcl.bn
|
// <-- NaN || sjcl.bn
|
||||||
Base.decode_check = function(version, input, alphabet) {
|
Base.decode_check = function(version, input, alphabet) {
|
||||||
const buffer = Base.decode(input, alphabet);
|
try {
|
||||||
|
const decoded = decode(input, {version, alphabet});
|
||||||
if (!buffer || buffer.length < 5) {
|
return sjcl.bn.fromBits(sjcl.codec.bytes.toBits(decoded));
|
||||||
|
} catch (e) {
|
||||||
return NaN;
|
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];
|
|
||||||
})) {
|
|
||||||
return NaN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Base.verify_checksum(buffer)) {
|
|
||||||
return NaN;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
exports.Base = Base;
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
/*eslint new-cap: 1*/
|
|
||||||
|
|
||||||
var sjcl = require('./utils').sjcl;
|
|
||||||
|
|
||||||
var UInt160 = require('./uint160').UInt160;
|
|
||||||
var UInt256 = require('./uint256').UInt256;
|
|
||||||
var Base = require('./base').Base;
|
|
||||||
|
|
||||||
function KeyPair() {
|
|
||||||
this._curve = sjcl.ecc.curves.k256;
|
|
||||||
this._secret = null;
|
|
||||||
this._pubkey = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
KeyPair.from_bn_secret = function(j) {
|
|
||||||
return (j instanceof this) ? j.clone() : (new this()).parse_bn_secret(j);
|
|
||||||
};
|
|
||||||
|
|
||||||
KeyPair.prototype.parse_bn_secret = function(j) {
|
|
||||||
this._secret = new sjcl.ecc.ecdsa.secretKey(sjcl.ecc.curves.k256, j);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*
|
|
||||||
* @return {sjcl.ecc.ecdsa.publicKey} public key
|
|
||||||
*/
|
|
||||||
KeyPair.prototype._pub = function() {
|
|
||||||
var curve = this._curve;
|
|
||||||
|
|
||||||
if (!this._pubkey && this._secret) {
|
|
||||||
var exponent = this._secret._exponent;
|
|
||||||
|
|
||||||
this._pubkey = new sjcl.ecc.ecdsa.publicKey(curve, curve.G.mult(exponent));
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._pubkey;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*
|
|
||||||
* @return {sjcl.bitArray} public key bits in compressed form
|
|
||||||
*/
|
|
||||||
KeyPair.prototype._pub_bits = function() {
|
|
||||||
var pub = this._pub();
|
|
||||||
|
|
||||||
if (!pub) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var point = pub._point, y_even = point.y.mod(2).equals(0);
|
|
||||||
|
|
||||||
return sjcl.bitArray.concat(
|
|
||||||
[sjcl.bitArray.partial(8, y_even ? 0x02 : 0x03)],
|
|
||||||
point.x.toBits(this._curve.r.bitLength())
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {String} public key bytes in compressed form, hex encoded.
|
|
||||||
*/
|
|
||||||
KeyPair.prototype.to_hex_pub = function() {
|
|
||||||
var bits = this._pub_bits();
|
|
||||||
|
|
||||||
if (!bits) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sjcl.codec.hex.fromBits(bits).toUpperCase();
|
|
||||||
};
|
|
||||||
|
|
||||||
function sha256_ripemd160(bits) {
|
|
||||||
return sjcl.hash.ripemd160.hash(sjcl.hash.sha256.hash(bits));
|
|
||||||
}
|
|
||||||
|
|
||||||
KeyPair.prototype.get_address = function() {
|
|
||||||
var bits = this._pub_bits();
|
|
||||||
|
|
||||||
if (!bits) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var hash = sha256_ripemd160(bits);
|
|
||||||
|
|
||||||
var address = UInt160.from_bits(hash);
|
|
||||||
address.set_version(Base.VER_ACCOUNT_ID);
|
|
||||||
return address;
|
|
||||||
};
|
|
||||||
|
|
||||||
KeyPair.prototype.sign = function(hash) {
|
|
||||||
var PARANOIA_256_BITS = 6; // sjcl constant for ensuring 256 bits of entropy
|
|
||||||
hash = UInt256.from_json(hash);
|
|
||||||
var sig = this._secret.sign(hash.to_bits(), PARANOIA_256_BITS);
|
|
||||||
sig = this._secret.canonicalizeSignature(sig);
|
|
||||||
return this._secret.encodeDER(sig);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.KeyPair = KeyPair;
|
|
||||||
114
src/core/seed.js
114
src/core/seed.js
@@ -4,18 +4,18 @@
|
|||||||
// Seed support
|
// Seed support
|
||||||
//
|
//
|
||||||
|
|
||||||
|
const {KeyPair, KeyType} = require('ripple-keypairs');
|
||||||
|
const codec = require('ripple-address-codec');
|
||||||
const extend = require('extend');
|
const extend = require('extend');
|
||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
const sjcl = utils.sjcl;
|
|
||||||
|
|
||||||
const Base = require('./base').Base;
|
const sjcl = utils.sjcl;
|
||||||
const UInt = require('./uint').UInt;
|
const UInt = require('./uint').UInt;
|
||||||
const UInt160 = require('./uint160').UInt160;
|
|
||||||
const KeyPair = require('./keypair').KeyPair;
|
|
||||||
|
|
||||||
const Seed = extend(function() {
|
const Seed = extend(function() {
|
||||||
this._curve = sjcl.ecc.curves.k256;
|
this._curve = sjcl.ecc.curves.k256;
|
||||||
this._value = NaN;
|
this._value = NaN;
|
||||||
|
this._type = KeyType.secp256k1;
|
||||||
}, UInt);
|
}, UInt);
|
||||||
|
|
||||||
Seed.width = 16;
|
Seed.width = 16;
|
||||||
@@ -34,7 +34,7 @@ Seed.prototype.parse_json = function(j) {
|
|||||||
this.parse_hex(j);
|
this.parse_hex(j);
|
||||||
// XXX Should also try 1751
|
// XXX Should also try 1751
|
||||||
}
|
}
|
||||||
if (!this.is_valid()) {
|
if (!this.is_valid() && j[0] !== 's') {
|
||||||
this.parse_passphrase(j);
|
this.parse_passphrase(j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,9 +52,19 @@ Seed.prototype.parse_base58 = function(j) {
|
|||||||
if (!j.length || j[0] !== 's') {
|
if (!j.length || j[0] !== 's') {
|
||||||
this._value = NaN;
|
this._value = NaN;
|
||||||
} else {
|
} else {
|
||||||
this._value = Base.decode_check(Base.VER_FAMILY_SEED, j);
|
try {
|
||||||
|
const {bytes, type} = codec.decodeSeed(j);
|
||||||
|
this._value = sjcl.bn.fromBits(sjcl.codec.bytes.toBits(bytes));
|
||||||
|
this._type = type;
|
||||||
|
} catch (e) {
|
||||||
|
this._value = NaN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Seed.prototype.set_ed25519 = function() {
|
||||||
|
this._type = KeyType.ed25519;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -75,100 +85,14 @@ Seed.prototype.to_json = function() {
|
|||||||
if (!(this.is_valid())) {
|
if (!(this.is_valid())) {
|
||||||
return NaN;
|
return NaN;
|
||||||
}
|
}
|
||||||
|
return codec.encodeSeed(this.to_bytes(), this._type);
|
||||||
const output = Base.encode_check(Base.VER_FAMILY_SEED, this.to_bytes());
|
|
||||||
|
|
||||||
return output;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function append_int(a, i) {
|
Seed.prototype.get_key = function() {
|
||||||
return [].concat(a, i >> 24, (i >> 16) & 0xff, (i >> 8) & 0xff, i & 0xff);
|
|
||||||
}
|
|
||||||
|
|
||||||
function firstHalfOfSHA512(bytes) {
|
|
||||||
return sjcl.bitArray.bitSlice(
|
|
||||||
sjcl.hash.sha512.hash(sjcl.codec.bytes.toBits(bytes)),
|
|
||||||
0, 256
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removed a `*` so this JSDoc-ish syntax is ignored.
|
|
||||||
// This will soon all change anyway.
|
|
||||||
/*
|
|
||||||
* @param account
|
|
||||||
* {undefined} take first, default, KeyPair
|
|
||||||
*
|
|
||||||
* {Number} specifies the account number of the KeyPair
|
|
||||||
* desired.
|
|
||||||
*
|
|
||||||
* {Uint160} (from_json able), specifies the address matching the KeyPair
|
|
||||||
* that is desired.
|
|
||||||
*
|
|
||||||
* @param maxLoops (optional)
|
|
||||||
* {Number} specifies the amount of attempts taken
|
|
||||||
* to generate a matching KeyPair
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
Seed.prototype.get_key = function(account, maxLoops) {
|
|
||||||
let account_number = 0, address;
|
|
||||||
let max_loops = maxLoops || 1;
|
|
||||||
|
|
||||||
if (!this.is_valid()) {
|
if (!this.is_valid()) {
|
||||||
throw new Error('Cannot generate keys from invalid seed!');
|
throw new Error('Cannot generate keys from invalid seed!');
|
||||||
}
|
}
|
||||||
if (account) {
|
return KeyPair.fromSeed(this.to_bytes(), this._type);
|
||||||
if (typeof account === 'number') {
|
|
||||||
account_number = account;
|
|
||||||
max_loops = account_number + 1;
|
|
||||||
} else {
|
|
||||||
address = UInt160.from_json(account);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let private_gen, public_gen;
|
|
||||||
const curve = this._curve;
|
|
||||||
let i = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
private_gen = sjcl.bn.fromBits(
|
|
||||||
firstHalfOfSHA512(append_int(this.to_bytes(), i)));
|
|
||||||
i++;
|
|
||||||
} while (!curve.r.greaterEquals(private_gen));
|
|
||||||
|
|
||||||
public_gen = curve.G.mult(private_gen);
|
|
||||||
|
|
||||||
let sec;
|
|
||||||
let key_pair;
|
|
||||||
|
|
||||||
do {
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
sec = sjcl.bn.fromBits(
|
|
||||||
firstHalfOfSHA512(
|
|
||||||
append_int(
|
|
||||||
append_int(public_gen.toBytesCompressed(), account_number)
|
|
||||||
,
|
|
||||||
i
|
|
||||||
)));
|
|
||||||
i++;
|
|
||||||
} while (!curve.r.greaterEquals(sec));
|
|
||||||
|
|
||||||
account_number++;
|
|
||||||
sec = sec.add(private_gen).mod(curve.r);
|
|
||||||
key_pair = KeyPair.from_bn_secret(sec);
|
|
||||||
|
|
||||||
if (max_loops-- <= 0) {
|
|
||||||
// We are almost certainly looking for an account that would take same
|
|
||||||
// value of $too_long {forever, ...}
|
|
||||||
throw new Error('Too many loops looking for KeyPair yielding ' +
|
|
||||||
address.to_json() + ' from ' + this.to_json());
|
|
||||||
}
|
|
||||||
|
|
||||||
} while (address && !key_pair.get_address().equals(address));
|
|
||||||
|
|
||||||
return key_pair;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.Seed = Seed;
|
exports.Seed = Seed;
|
||||||
|
|||||||
@@ -344,8 +344,8 @@ Transaction.prototype._computeFee = function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (fees.length) {
|
switch (fees.length) {
|
||||||
case 0: return undefined;
|
case 0: return undefined;
|
||||||
case 1: return String(fees[0]);
|
case 1: return String(fees[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fees.sort(function ascending(a, b) {
|
fees.sort(function ascending(a, b) {
|
||||||
@@ -399,8 +399,8 @@ Transaction.prototype.complete = function() {
|
|||||||
if (typeof this.tx_json.SigningPubKey === 'undefined') {
|
if (typeof this.tx_json.SigningPubKey === 'undefined') {
|
||||||
try {
|
try {
|
||||||
const seed = Seed.from_json(this._secret);
|
const seed = Seed.from_json(this._secret);
|
||||||
const key = seed.get_key(this.tx_json.Account);
|
const key = seed.get_key();
|
||||||
this.tx_json.SigningPubKey = key.to_hex_pub();
|
this.tx_json.SigningPubKey = key.pubKeyHex();
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
this.emit('error', new RippleError(
|
this.emit('error', new RippleError(
|
||||||
'tejSecretInvalid', 'Invalid secret'));
|
'tejSecretInvalid', 'Invalid secret'));
|
||||||
@@ -469,13 +469,13 @@ Transaction.prototype.hash = function(prefix_, asUINT256, serialized) {
|
|||||||
return asUINT256 ? hash : hash.to_hex();
|
return asUINT256 ? hash : hash.to_hex();
|
||||||
};
|
};
|
||||||
|
|
||||||
Transaction.prototype.sign = function(testnet) {
|
Transaction.prototype.sign = function() {
|
||||||
const seed = Seed.from_json(this._secret);
|
const seed = Seed.from_json(this._secret);
|
||||||
const prev_sig = this.tx_json.TxnSignature;
|
const prev_sig = this.tx_json.TxnSignature;
|
||||||
|
|
||||||
delete this.tx_json.TxnSignature;
|
delete this.tx_json.TxnSignature;
|
||||||
|
|
||||||
const hash = this.signingHash(testnet);
|
const hash = this.signingHash();
|
||||||
|
|
||||||
// If the hash is the same, we can re-use the previous signature
|
// If the hash is the same, we can re-use the previous signature
|
||||||
if (prev_sig && hash === this.previousSigningHash) {
|
if (prev_sig && hash === this.previousSigningHash) {
|
||||||
@@ -483,10 +483,8 @@ Transaction.prototype.sign = function(testnet) {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
const key = seed.get_key(this.tx_json.Account);
|
const key = seed.get_key();
|
||||||
const sig = key.sign(hash);
|
const hex = key.signHex(this.signingData().buffer);
|
||||||
const hex = sjcl.codec.hex.fromBits(sig).toUpperCase();
|
|
||||||
|
|
||||||
this.tx_json.TxnSignature = hex;
|
this.tx_json.TxnSignature = hex;
|
||||||
this.previousSigningHash = hash;
|
this.previousSigningHash = hash;
|
||||||
|
|
||||||
|
|||||||
@@ -151,11 +151,9 @@ describe('RippleAPI', function() {
|
|||||||
|
|
||||||
it('sign', function() {
|
it('sign', function() {
|
||||||
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV';
|
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV';
|
||||||
withDeterministicPRNG(() => {
|
const result = this.api.sign(requests.sign, secret);
|
||||||
const result = this.api.sign(requests.sign, secret);
|
assert.deepEqual(result, responses.sign);
|
||||||
assert.deepEqual(result, responses.sign);
|
schemaValidator.schemaValidate('sign', result);
|
||||||
schemaValidator.schemaValidate('sign', result);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('submit', function() {
|
it('submit', function() {
|
||||||
@@ -647,21 +645,14 @@ describe('RippleAPI', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('addressAndSecret', function() {
|
it('addressAndSecret', function() {
|
||||||
const wrongSecret = {address: address,
|
|
||||||
secret: 'shzjfakiK79YQdMjy4h8cGGfQSV6u'
|
|
||||||
};
|
|
||||||
assert.throws(_.partial(validate.addressAndSecret, wrongSecret),
|
|
||||||
this.api.errors.ValidationError);
|
|
||||||
const noSecret = {address: address};
|
const noSecret = {address: address};
|
||||||
assert.throws(_.partial(validate.addressAndSecret, noSecret),
|
assert.throws(_.partial(validate.addressAndSecret, noSecret),
|
||||||
this.api.errors.ValidationError);
|
this.api.errors.ValidationError);
|
||||||
assert.throws(_.partial(validate.addressAndSecret, noSecret),
|
assert.throws(_.partial(validate.addressAndSecret, noSecret),
|
||||||
/Parameter missing/);
|
/Parameter missing/);
|
||||||
const badSecret = {address: address, secret: 'bad'};
|
const badSecret = {address: address, secret: 'sbad'};
|
||||||
assert.throws(_.partial(validate.addressAndSecret, badSecret),
|
assert.throws(_.partial(validate.addressAndSecret, badSecret),
|
||||||
this.api.errors.ValidationError);
|
this.api.errors.ValidationError);
|
||||||
assert.throws(_.partial(validate.addressAndSecret, badSecret),
|
|
||||||
/not match/);
|
|
||||||
const goodWallet = {address: 'rpZMK8hwyrBvLorFNWHRCGt88nCJWbixur',
|
const goodWallet = {address: 'rpZMK8hwyrBvLorFNWHRCGt88nCJWbixur',
|
||||||
secret: 'shzjfakiK79YQdMjy4h8cGGfQSV6u'
|
secret: 'shzjfakiK79YQdMjy4h8cGGfQSV6u'
|
||||||
};
|
};
|
||||||
|
|||||||
4
test/fixtures/api/responses/sign.json
vendored
4
test/fixtures/api/responses/sign.json
vendored
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"signedTransaction": "12000322000000002400000017201B0086955368400000000000000C732102F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D87446304402207660BDEF67105CE1EBA9AD35DC7156BAB43FF1D47633199EE257D70B6B9AAFBF022045A812486A675750B5A3F37131E9F92299728D37FF6BB7195CA5EE881268CB4C770A726970706C652E636F6D81145E7B112523F68D2F5E879DB4EAC51C6698A69304",
|
"signedTransaction": "12000322000000002400000017201B0086955368400000000000000C732102F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D874473045022100E3C85A98C3D48E2D746B3993D36C38A8922D9499E7D20B54883511C9CFF7F70A02201790168F671AC558E1B62B911CA7AC5B1D8E454898BE6F6E9D87758076683459770A726970706C652E636F6D81145E7B112523F68D2F5E879DB4EAC51C6698A69304",
|
||||||
"id": "29D23159EBA79170DCA5EF467CBC15114DBD35B7A8C3DBF76809BA354D00D250"
|
"id": "7AFE2F2FBE72467C47CCDD6DBA890AB3C97A708C335983F77AF32C4308C73633"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
var assert = require('assert');
|
|
||||||
var Seed = require('ripple-lib').Seed;
|
|
||||||
|
|
||||||
describe('KeyPair', function() {
|
|
||||||
it('can generate an address', function () {
|
|
||||||
var seed = Seed.from_json("masterpassphrase");
|
|
||||||
var address = seed.get_key().get_address();
|
|
||||||
assert.strictEqual(address.to_json(), 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// vim:sw=2:sts=2:ts=8:et
|
|
||||||
@@ -4,17 +4,15 @@
|
|||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const Seed = require('ripple-lib').Seed;
|
const Seed = require('ripple-lib').Seed;
|
||||||
|
|
||||||
function assert_helper(seed_json, address_or_nth, expected) {
|
|
||||||
const seed = Seed.from_json(seed_json);
|
|
||||||
const keypair = seed.get_key(address_or_nth, 500);
|
|
||||||
assert.strictEqual(keypair.to_hex_pub(), expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('Seed', function() {
|
describe('Seed', function() {
|
||||||
it('saESc82Vun7Ta5EJRzGJbrXb5HNYk', function() {
|
it('saESc82Vun7Ta5EJRzGJbrXb5HNYk', function() {
|
||||||
const seed = Seed.from_json('saESc82Vun7Ta5EJRzGJbrXb5HNYk');
|
const seed = Seed.from_json('saESc82Vun7Ta5EJRzGJbrXb5HNYk');
|
||||||
assert.strictEqual(seed.to_hex(), 'FF1CF838D02B2CF7B45BAC27F5F24F4F');
|
assert.strictEqual(seed.to_hex(), 'FF1CF838D02B2CF7B45BAC27F5F24F4F');
|
||||||
});
|
});
|
||||||
|
it('can create ed25519 seeds from a phrase', function() {
|
||||||
|
const seed = Seed.from_json('phrase').set_ed25519().to_json();
|
||||||
|
assert.strictEqual(seed, 'sEdT7U4WpkoiH6wBoNeLzDi1eu9N64Y');
|
||||||
|
});
|
||||||
it('sp6iDHnmiPN7tQFHm5sCW59ax3hfE', function() {
|
it('sp6iDHnmiPN7tQFHm5sCW59ax3hfE', function() {
|
||||||
const seed = Seed.from_json('sp6iDHnmiPN7tQFHm5sCW59ax3hfE');
|
const seed = Seed.from_json('sp6iDHnmiPN7tQFHm5sCW59ax3hfE');
|
||||||
assert.strictEqual(seed.to_hex(), '00AD8DA764C3C8AF5F9B8D51C94B9E49');
|
assert.strictEqual(seed.to_hex(), '00AD8DA764C3C8AF5F9B8D51C94B9E49');
|
||||||
@@ -36,66 +34,13 @@ describe('Seed', function() {
|
|||||||
const seed = new Seed().parse_base58('Xs');
|
const seed = new Seed().parse_base58('Xs');
|
||||||
assert(!seed.is_valid());
|
assert(!seed.is_valid());
|
||||||
});
|
});
|
||||||
it('can generate many addresses', function() {
|
|
||||||
|
|
||||||
const test_data = [
|
|
||||||
// Format:
|
|
||||||
// [passphrase, address, nth-for-seed, expected-public-key]
|
|
||||||
['masterpassphrase', 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', 0,
|
|
||||||
'0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020'],
|
|
||||||
['masterpassphrase', 'r4bYF7SLUMD7QgSLLpgJx38WJSY12ViRjP', 1,
|
|
||||||
'02CD8C4CE87F86AAD1D9D18B03DE28E6E756F040BD72A9C127862833EB90D60BAD'],
|
|
||||||
['masterpassphrase', 'rLpAd4peHUMBPbVJASMYK5GTBUSwXRD9nx', 2,
|
|
||||||
'0259A57642A6F4AEFC9B8062AF453FDEEEAC5572BA602BB1DBD5EF011394C6F9FC'],
|
|
||||||
['otherpassphrase', 'rpe3YWSVwGU2PmUzebAPg2deBXHtmba7hJ', 0,
|
|
||||||
'022235A3DB2CAE57C60B7831929611D58867F86D28C0AD3C82473CC4A84990D01B'],
|
|
||||||
['otherpassphrase', 'raAPC2gALSmsTkXR4wUwQcPgX66kJuLv2S', 5,
|
|
||||||
'03F0619AFABE08D22D98C8721895FE3673B6174168949976F2573CE1138C124994'],
|
|
||||||
['yetanotherpassphrase', 'rKnM44fS48qrGiDxB5fB5u64vHVJwjDPUo', 0,
|
|
||||||
'0385AD049327EF7E5EC429350A15CEB23955037DE99660F6E70C11C5ABF4407036'],
|
|
||||||
['yetanotherpassphrase', 'rMvkT1RHPfsZwTFbKDKBEisa5U4d2a9V8n', 1,
|
|
||||||
'023A2876EA130CBE7BBA0573C2DB4C4CEB9A7547666915BD40366CDC6150CF54DC']
|
|
||||||
];
|
|
||||||
|
|
||||||
for (let nth = 0; nth < test_data.length; nth++) {
|
|
||||||
const seed_json = test_data[nth][0];
|
|
||||||
const address = test_data[nth][1];
|
|
||||||
const nth_for_seed = test_data[nth][2];
|
|
||||||
const expected = test_data[nth][3];
|
|
||||||
|
|
||||||
// `seed.get_key($ripple_address)` is arguably an ill concieved feature
|
|
||||||
// as it needs to generate `nth` many keypairs and generate hashed public
|
|
||||||
// keys (addresses) for equality tests ??
|
|
||||||
// Would need remote.set_secret(address, private_key_not_seed) ??
|
|
||||||
assert_helper(seed_json, address, expected);
|
|
||||||
|
|
||||||
// This isn't too bad as it only needs to generate one keypair `seq`
|
|
||||||
assert_helper(seed_json, nth_for_seed, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return the key_pair for a valid account and secret pair', function() {
|
it('should return the key_pair for a valid account and secret pair', function() {
|
||||||
const address = 'r3GgMwvgvP8h4yVWvjH1dPZNvC37TjzBBE';
|
const address = 'r3GgMwvgvP8h4yVWvjH1dPZNvC37TjzBBE';
|
||||||
const seed = Seed.from_json('shsWGZcmZz6YsWWmcnpfr6fLTdtFV');
|
const seed = Seed.from_json('shsWGZcmZz6YsWWmcnpfr6fLTdtFV');
|
||||||
const keyPair = seed.get_key(address);
|
const keyPair = seed.get_key();
|
||||||
assert.strictEqual(keyPair.get_address().to_json(), address);
|
assert.strictEqual(keyPair.accountID(), address);
|
||||||
assert.strictEqual(keyPair.to_hex_pub(), '02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8');
|
assert.strictEqual(keyPair.pubKeyHex(), '02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not find a KeyPair for a secret that does not belong to the given account', function() {
|
|
||||||
const address = 'r3GgMwvgvP8h4yVWvjH1dPZNvC37TjzBBE';
|
|
||||||
const secret = 'snoPBrXtMeMyMHUVTgbuqAfg1SUTb';
|
|
||||||
const seed = Seed.from_json('snoPBrXtMeMyMHUVTgbuqAfg1SUTb');
|
|
||||||
try {
|
|
||||||
seed.get_key(address);
|
|
||||||
assert(false, 'should throw an error');
|
|
||||||
} catch(e) {
|
|
||||||
assert.strictEqual(e.message, 'Too many loops looking for KeyPair yielding ' + address + ' from ' + secret);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// vim:sw=2:sts=2:ts=8:et
|
// vim:sw=2:sts=2:ts=8:et
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ describe('Signing', function() {
|
|||||||
it('SigningPubKey 1 (ripple-client issue #245)', function() {
|
it('SigningPubKey 1 (ripple-client issue #245)', function() {
|
||||||
const seed = Seed.from_json('saESc82Vun7Ta5EJRzGJbrXb5HNYk');
|
const seed = Seed.from_json('saESc82Vun7Ta5EJRzGJbrXb5HNYk');
|
||||||
const key = seed.get_key('rBZ4j6MsoctipM6GEyHSjQKzXG3yambDnZ');
|
const key = seed.get_key('rBZ4j6MsoctipM6GEyHSjQKzXG3yambDnZ');
|
||||||
const pub = key.to_hex_pub();
|
const pub = key.pubKeyHex();
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
pub,
|
pub,
|
||||||
'0396941B22791A448E5877A44CE98434DB217D6FB97D63F0DAD23BE49ED45173C9');
|
'0396941B22791A448E5877A44CE98434DB217D6FB97D63F0DAD23BE49ED45173C9');
|
||||||
@@ -20,7 +20,7 @@ describe('Signing', function() {
|
|||||||
it('SigningPubKey 2 (master seed)', function() {
|
it('SigningPubKey 2 (master seed)', function() {
|
||||||
const seed = Seed.from_json('snoPBrXtMeMyMHUVTgbuqAfg1SUTb');
|
const seed = Seed.from_json('snoPBrXtMeMyMHUVTgbuqAfg1SUTb');
|
||||||
const key = seed.get_key('rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
const key = seed.get_key('rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||||
const pub = key.to_hex_pub();
|
const pub = key.pubKeyHex();
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
pub,
|
pub,
|
||||||
'0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020');
|
'0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020');
|
||||||
|
|||||||
@@ -454,7 +454,6 @@ describe('Transaction', function() {
|
|||||||
transaction.SigningPubKey = undefined;
|
transaction.SigningPubKey = undefined;
|
||||||
transaction.tx_json.Account = 'rMWwx3Ma16HnqSd4H6saPisihX9aKpXxHJ';
|
transaction.tx_json.Account = 'rMWwx3Ma16HnqSd4H6saPisihX9aKpXxHJ';
|
||||||
transaction._secret = 'sh2pTicynUEG46jjR4EoexHcQEoijX';
|
transaction._secret = 'sh2pTicynUEG46jjR4EoexHcQEoijX';
|
||||||
|
|
||||||
transaction.once('error', function(err) {
|
transaction.once('error', function(err) {
|
||||||
assert.strictEqual(err.result, 'tejSecretInvalid');
|
assert.strictEqual(err.result, 'tejSecretInvalid');
|
||||||
done();
|
done();
|
||||||
@@ -553,6 +552,36 @@ describe('Transaction', function() {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('ed25519 signing', function() {
|
||||||
|
it('can accept an ed25519 seed for ._secret', function() {
|
||||||
|
const expectedPub = 'EDD3993CDC6647896C455F136648B7750' +
|
||||||
|
'723B011475547AF60691AA3D7438E021D';
|
||||||
|
|
||||||
|
const expectedSig = 'C3646313B08EED6AF4392261A31B961F' +
|
||||||
|
'10C66CB733DB7F6CD9EAB079857834C8' +
|
||||||
|
'B0334270A2C037E63CDCCC1932E08328' +
|
||||||
|
'82B7B7066ECD2FAEDEB4A83DF8AE6303';
|
||||||
|
|
||||||
|
const tx_json = {
|
||||||
|
Account: 'rJZdUusLDtY9NEsGea7ijqhVrXv98rYBYN',
|
||||||
|
Amount: '1000',
|
||||||
|
Destination: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh',
|
||||||
|
Fee: '10',
|
||||||
|
Flags: 2147483648,
|
||||||
|
Sequence: 1,
|
||||||
|
TransactionType: 'Payment'
|
||||||
|
};
|
||||||
|
|
||||||
|
const tx = Transaction.from_json(tx_json);
|
||||||
|
tx.setSecret('sEd7rBGm5kxzauRTAV2hbsNz7N45X91');
|
||||||
|
tx.complete();
|
||||||
|
tx.sign();
|
||||||
|
|
||||||
|
assert.strictEqual(tx_json.SigningPubKey, expectedPub);
|
||||||
|
assert.strictEqual(tx_json.TxnSignature, expectedSig);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('signing', function() {
|
describe('signing', function() {
|
||||||
const tx_json = {
|
const tx_json = {
|
||||||
SigningPubKey: '021FED5FD081CE5C4356431267D04C6E2167E4112C897D5E10335D4E22B4DA49ED',
|
SigningPubKey: '021FED5FD081CE5C4356431267D04C6E2167E4112C897D5E10335D4E22B4DA49ED',
|
||||||
@@ -1318,11 +1347,11 @@ describe('Transaction', function() {
|
|||||||
const expected = [
|
const expected = [
|
||||||
{
|
{
|
||||||
Memo:
|
Memo:
|
||||||
{
|
{
|
||||||
MemoType: '6D657373616765',
|
MemoType: '6D657373616765',
|
||||||
MemoFormat: '6A736F6E',
|
MemoFormat: '6A736F6E',
|
||||||
MemoData: '7B22737472696E67223A2276616C7565222C22626F6F6C223A747275652C22696E7465676572223A317D'
|
MemoData: '7B22737472696E67223A2276616C7565222C22626F6F6C223A747275652C22696E7465676572223A317D'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -2014,7 +2043,7 @@ describe('Transaction', function() {
|
|||||||
const queue = new TransactionQueue();
|
const queue = new TransactionQueue();
|
||||||
|
|
||||||
// Randomized submit indexes
|
// Randomized submit indexes
|
||||||
[
|
const indexes = [
|
||||||
28093,
|
28093,
|
||||||
456944,
|
456944,
|
||||||
347213,
|
347213,
|
||||||
@@ -2025,8 +2054,9 @@ describe('Transaction', function() {
|
|||||||
925550,
|
925550,
|
||||||
872298,
|
872298,
|
||||||
543305
|
543305
|
||||||
]
|
];
|
||||||
.forEach(function(index) {
|
|
||||||
|
indexes.forEach(function(index) {
|
||||||
const tx = new Transaction();
|
const tx = new Transaction();
|
||||||
tx.initialSubmitIndex = index;
|
tx.initialSubmitIndex = index;
|
||||||
queue.push(tx);
|
queue.push(tx);
|
||||||
|
|||||||
Reference in New Issue
Block a user