Commit be952dd6 authored by kenshin-samourai's avatar kenshin-samourai
Browse files

add initialization from a seed and support of derivation from a notif pubkey

parent 3c30539a
......@@ -3,4 +3,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
var bip47 = require("./payment-code");
exports.fromBuffer = bip47.fromBuffer;
exports.fromBase58 = bip47.fromBase58;
exports.fromWalletSeed = bip47.fromWalletSeed;
exports.utils = require('./utils');
\ No newline at end of file
......@@ -2,7 +2,7 @@
Object.defineProperty(exports, "__esModule", { value: true });
const ecc = require('tiny-secp256k1');
const { networks, getP2pkhAddress, sha256 } = require('./utils');
const { fromPublicKey } = require('bip32');
const { fromPublicKey, fromSeed } = require('bip32');
const { encode, decode } = require('bs58check');
......@@ -25,6 +25,23 @@ class PaymentCode {
this.root = fromPublicKey(this.pubKey, this.chainCode, this.network);
}
static fromSeed(bSeed, id, network) {
var reserved = Buffer.alloc(13, 0);
const root = fromSeed(bSeed);
const root_bip47 = root.derivePath(`m/47'/0'/${id}'`);
let pc = Buffer.from('0100', 'hex'); // version + options
pc = Buffer.concat([pc, root_bip47.publicKey]);
pc = Buffer.concat([pc, root_bip47.chainCode]);
if (pc.length !== 67)
throw new TypeError('Missing or wrong publicKey or chainCode');
pc = Buffer.concat([pc, reserved]); // reserved bytes
const pcode = new PaymentCode(pc, network);
pcode.root = root_bip47; // store the privkey
return pcode;
}
get features() {
return this.buf.slice(1, 1);
}
......@@ -47,6 +64,10 @@ class PaymentCode {
return encode(buf);
}
_hasPrivKeys() {
return this.root.privateKey != null;
}
derive(index) {
return this.root.derive(index);
}
......@@ -61,15 +82,31 @@ class PaymentCode {
}
derivePaymentPublicKey(a, idx) {
if (!ecc.isPrivate(a))
throw new TypeError('Invalid private key');
const B = this.derive(idx).publicKey;
if (!ecc.isPrivate(a) && !ecc.isPoint(a))
throw new TypeError('Argument is neither a valid private key or public key');
let B = null;
let S = null;
if (ecc.isPrivate(a)) {
// a is a private key
B = this.derive(idx).publicKey;
S = ecc.pointMultiply(B, a);
} else if (ecc.isPoint(a)) {
if (!this._hasPrivKeys())
throw new Error('Unable to compute the derivation with a public key provided as argument');
// a is a public key
const A = a;
const b_node = this.derive(idx);
const b = b_node.privateKey;
B = b_node.publicKey;
S = ecc.pointMultiply(A, b);
}
if (!ecc.isPoint(B))
throw new TypeError('Invalid derived public key');
const S = ecc.pointMultiply(B, a);
const Sx = S.slice(1, 33);
const s = sha256(Sx);
......@@ -102,3 +139,9 @@ function fromBuffer(buf, network) {
return new PaymentCode(buf, network);
}
exports.fromBuffer = fromBuffer;
function fromWalletSeed(bSeed, id, network) {
return PaymentCode.fromSeed(bSeed, id, network);
}
exports.fromWalletSeed = fromWalletSeed;
......@@ -16,6 +16,7 @@ const PC_1 = {
};
const PC_2 = {
seed: '87eaaac5a539ab028df44d9110defbef3797ddb805ca309f61a69ff96dbaa7ab5b24038cf029edec5235d933110f0aea8aeecf939ed14fc20730bba71e4b1110',
pcBase58: 'PM8TJS2JxQ5ztXUpBBRnpTbcUXbUHy2T1abfrb3KkAAtMEGNbey4oumH7Hc578WgQJhPjBxteQ5GHHToTYHE3A1w6p7tU6KSoFmWBVbFGjKPisZDbP97',
notifAddr: '1ChvUUvht2hUQufHBXF8NgLhW8SwE2ecGV',
notifPrivkey: '04448fd1be0c9c13a5ca0b530e464b619dc091b299b98c5cab9978b32b4a1b8b',
......@@ -74,6 +75,19 @@ describe('payment-code', function() {
});
});
describe('fromWalletSeed()', function() {
it('should successfully initialize a PaymentCode from a seed', function() {
try {
const seed = Buffer.from(PC_2.seed, 'hex');
const pc = bip47.fromWalletSeed(seed, 0);
const pc_b58 = pc.toBase58();
assert(pc_b58 == PC_2.pcBase58);
} catch(e) {
assert(false);
}
});
});
describe('PaymentCode.toBase58()', function() {
it('should successfully reencode a payment code in base58', function() {
try {
......@@ -107,7 +121,7 @@ describe('payment-code', function() {
});
describe('PaymentCode.derivePaymentPublicKey()', function() {
it('should successfully derive public keys for payments', function() {
it('should successfully derive public keys from a payment code and a notif privkey', function() {
try {
const privkey1 = Buffer.from(PC_1.notifPrivkey, 'hex');
const pc2 = bip47.fromBase58(PC_2.pcBase58);
......@@ -122,10 +136,38 @@ describe('payment-code', function() {
assert(false);
}
});
it('should successfully derive public keys from a payment code and a notif pubkey', function() {
try {
const pubkey1 = Buffer.from(PC_1.notifPubKey, 'hex');
const seed = Buffer.from(PC_2.seed, 'hex');
const pc2 = bip47.fromWalletSeed(seed, 0);
for (let i=0; i<10; i++) {
const pubkeyPayment = pc2.derivePaymentPublicKey(pubkey1, i);
const addrPayment = utils.getP2pkhAddress(pubkeyPayment, utils.networks.bitcoin);
if (addrPayment !== PC_2_PAYMENT_ADDR[i])
assert(false);
}
assert(true);
} catch(e) {
assert(false);
}
});
it('should fail to derive public keys from a notif pubkey if master privkey is unknown', function() {
try {
const pubkey1 = Buffer.from(PC_1.notifPubKey, 'hex');
const pc2 = bip47.fromBase58(PC_2.pcBase58);
const pubkeyPayment = pc2.derivePaymentPublicKey(pubkey1, 0);
assert(false);
} catch(e) {
assert(true);
}
});
});
describe('PaymentCode.getPaymentAddress()', function() {
it('should successfully derive P2PKH addresses for payments', function() {
it('should successfully derive P2PKH addresses from a payment code and a notif privkey', function() {
try {
const privkey1 = Buffer.from(PC_1.notifPrivkey, 'hex');
const pc2 = bip47.fromBase58(PC_2.pcBase58);
......@@ -139,6 +181,22 @@ describe('payment-code', function() {
assert(false);
}
});
it('should successfully derive P2PKH addresses from a payment code and a notif pubkey', function() {
try {
const pubkey1 = Buffer.from(PC_1.notifPubKey, 'hex');
const seed = Buffer.from(PC_2.seed, 'hex');
const pc2 = bip47.fromWalletSeed(seed, 0);
for (let i=0; i<10; i++) {
const addrPayment = pc2.getPaymentAddress(pubkey1, i);
if (addrPayment !== PC_2_PAYMENT_ADDR[i])
assert(false);
}
assert(true);
} catch(e) {
assert(false);
}
});
});
});
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment