Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Init #1113

Closed
wants to merge 13 commits into from
3 changes: 2 additions & 1 deletion bin/bwallet-cli
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ class CLI {
watchOnly: !this.config.has('account-key')
? this.config.bool('watch-only')
: true,
accountKey: this.config.str('account-key')
accountKey: this.config.str('account-key'),
purpose: this.config.str('purpose')
};

const wallet = await this.client.createWallet(id, options);
Expand Down
36 changes: 36 additions & 0 deletions lib/hd/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,39 @@ common.isAccount = function isAccount(key, account) {
*/

common.ZERO_KEY = Buffer.alloc(33, 0x00);

/**
* Purposes from extended key prefix bytes.
* @enum {Number}
* @default
*/

common.purposes = {
x: 0,
y: 1,
z: 2
};

/**
* Purposes by value.
* @enum {Number}
* @default
*/

common.purposesByVal = [
'x',
'y',
'z'
];

/**
* BIP44 "purpose" values by extended key prefix bytes
* @enum {Number}
* @default
*/

common.purposePath = {
x: 44,
y: 49,
z: 84
};
45 changes: 43 additions & 2 deletions lib/hd/private.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class HDPrivateKey {
* Create an hd private key.
* @constructor
* @param {Object|String} options
* @param {string} options.purpose
* @param {Number?} options.depth
* @param {Number?} options.parentFingerPrint
* @param {Number?} options.childIndex
Expand All @@ -50,6 +51,7 @@ class HDPrivateKey {
*/

constructor(options) {
this.purpose = 'x';
this.depth = 0;
this.parentFingerPrint = 0;
this.childIndex = 0;
Expand Down Expand Up @@ -79,6 +81,11 @@ class HDPrivateKey {
assert(Buffer.isBuffer(options.chainCode));
assert(Buffer.isBuffer(options.privateKey));

if (options.purpose) {
assert(common.purposes[options.purpose] !== undefined, 'Bad purpose');
this.purpose = options.purpose;
}

this.depth = options.depth;
this.parentFingerPrint = options.parentFingerPrint;
this.childIndex = options.childIndex;
Expand Down Expand Up @@ -109,6 +116,7 @@ class HDPrivateKey {

if (!key) {
key = new HDPublicKey();
key.purpose = this.purpose;
key.depth = this.depth;
key.parentFingerPrint = this.parentFingerPrint;
key.childIndex = this.childIndex;
Expand Down Expand Up @@ -218,6 +226,7 @@ class HDPrivateKey {
}

const child = new this.constructor();
child.purpose = this.purpose;
child.depth = this.depth + 1;
child.parentFingerPrint = this.fingerPrint;
child.childIndex = index;
Expand Down Expand Up @@ -436,6 +445,7 @@ class HDPrivateKey {
if (!secp256k1.privateKeyVerify(left))
throw new Error('Master private key is invalid.');

this.purpose = 'x';
this.depth = 0;
this.parentFingerPrint = 0;
this.childIndex = 0;
Expand Down Expand Up @@ -563,8 +573,29 @@ class HDPrivateKey {

fromReader(br, network) {
const version = br.readU32BE();
const confirmNetwork = Network.fromPrivate(version, network);

let purpose = null;
for (const label in confirmNetwork.keyPrefix) {
if (confirmNetwork.keyPrefix[label] === version) {
purpose = label;
break;
}
}

Network.fromPrivate(version, network);
switch (purpose) {
case 'xprivkey':
this.purpose = 'x';
break;
case 'yprivkey':
this.purpose = 'y';
break;
case 'zprivkey':
this.purpose = 'z';
break;
default:
throw new Error('Bad purpose bytes');
}

this.depth = br.readU8();
this.parentFingerPrint = br.readU32BE();
Expand Down Expand Up @@ -618,7 +649,17 @@ class HDPrivateKey {
toWriter(bw, network) {
network = Network.get(network);

bw.writeU32BE(network.keyPrefix.xprivkey);
switch (this.purpose) {
case 'x':
bw.writeU32BE(network.keyPrefix.xprivkey);
break;
case 'y':
bw.writeU32BE(network.keyPrefix.yprivkey);
break;
case 'z':
bw.writeU32BE(network.keyPrefix.zprivkey);
break;
}
bw.writeU8(this.depth);
bw.writeU32BE(this.parentFingerPrint);
bw.writeU32BE(this.childIndex);
Expand Down
44 changes: 42 additions & 2 deletions lib/hd/public.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class HDPublicKey {
* @constructor
* @param {Object|Base58String} options
* @param {Base58String?} options.xkey - Serialized base58 key.
* @param {string} options.purpose
* @param {Number?} options.depth
* @param {Number?} options.parentFingerPrint
* @param {Number?} options.childIndex
Expand All @@ -42,6 +43,7 @@ class HDPublicKey {
*/

constructor(options) {
this.purpose = 'x';
this.depth = 0;
this.parentFingerPrint = 0;
this.childIndex = 0;
Expand All @@ -68,6 +70,11 @@ class HDPublicKey {
assert(Buffer.isBuffer(options.chainCode));
assert(Buffer.isBuffer(options.publicKey));

if (options.purpose) {
assert(common.purposes[options.purpose] !== undefined, 'Bad purpose');
this.purpose = options.purpose;
}

this.depth = options.depth;
this.parentFingerPrint = options.parentFingerPrint;
this.childIndex = options.childIndex;
Expand Down Expand Up @@ -434,7 +441,29 @@ class HDPublicKey {
fromReader(br, network) {
const version = br.readU32BE();

Network.fromPublic(version, network);
const confirmNetwork = Network.fromPublic(version, network);

let purpose = null;
for (const label in confirmNetwork.keyPrefix) {
if (confirmNetwork.keyPrefix[label] === version) {
purpose = label;
break;
}
}

switch (purpose) {
case 'xpubkey':
this.purpose = 'x';
break;
case 'ypubkey':
this.purpose = 'y';
break;
case 'zpubkey':
this.purpose = 'z';
break;
default:
throw new Error('Bad purpose bytes');
}

this.depth = br.readU8();
this.parentFingerPrint = br.readU32BE();
Expand Down Expand Up @@ -472,12 +501,23 @@ class HDPublicKey {
* Write the key to a buffer writer.
* @param {BufferWriter} bw
* @param {(Network|NetworkType)?} network
* @param {string} type
*/

toWriter(bw, network) {
network = Network.get(network);

bw.writeU32BE(network.keyPrefix.xpubkey);
switch (this.purpose) {
case 'x':
bw.writeU32BE(network.keyPrefix.xpubkey);
break;
case 'y':
bw.writeU32BE(network.keyPrefix.ypubkey);
break;
case 'z':
bw.writeU32BE(network.keyPrefix.zpubkey);
break;
}
bw.writeU8(this.depth);
bw.writeU32BE(this.parentFingerPrint);
bw.writeU32BE(this.childIndex);
Expand Down
3 changes: 2 additions & 1 deletion lib/primitives/keyring.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ class KeyRing {
assert(Buffer.isBuffer(key), 'Public key must be a buffer.');
assert(secp256k1.publicKeyVerify(key), 'Not a valid public key.');
this.publicKey = key;

return this;
}

Expand Down Expand Up @@ -623,7 +624,7 @@ class KeyRing {
*/

getAddress(enc, network) {
if (this.nested)
if (this.nested || this.purpose === 'y')
return this.getNestedAddress(enc, network);

if (this.script)
Expand Down
24 changes: 20 additions & 4 deletions lib/protocol/network.js
Original file line number Diff line number Diff line change
Expand Up @@ -413,19 +413,35 @@ function cmpWIF(network, prefix) {
}

function cmpPub(network, prefix) {
return network.keyPrefix.xpubkey === prefix;
return (
network.keyPrefix.xpubkey === prefix ||
network.keyPrefix.ypubkey === prefix ||
network.keyPrefix.zpubkey === prefix
);
}

function cmpPriv(network, prefix) {
return network.keyPrefix.xprivkey === prefix;
return (
network.keyPrefix.xprivkey === prefix ||
network.keyPrefix.yprivkey === prefix ||
network.keyPrefix.zprivkey === prefix
);
}

function cmpPub58(network, prefix) {
return network.keyPrefix.xpubkey58 === prefix;
return (
network.keyPrefix.xpubkey58 === prefix ||
network.keyPrefix.ypubkey58 === prefix ||
network.keyPrefix.zpubkey58 === prefix
);
}

function cmpPriv58(network, prefix) {
return network.keyPrefix.xprivkey58 === prefix;
return (
network.keyPrefix.xprivkey58 === prefix ||
network.keyPrefix.yprivkey58 === prefix ||
network.keyPrefix.zprivkey58 === prefix
);
}

function cmpBase58(network, prefix) {
Expand Down
36 changes: 36 additions & 0 deletions lib/protocol/networks.js
Original file line number Diff line number Diff line change
Expand Up @@ -418,10 +418,22 @@ main.deploys = [

main.keyPrefix = {
privkey: 0x80,

xpubkey: 0x0488b21e,
xprivkey: 0x0488ade4,
xpubkey58: 'xpub',
xprivkey58: 'xprv',
// P2WPKH in P2SH
ypubkey: 0x049d7cb2,
yprivkey: 0x049d7878,
ypubkey58: 'ypub',
yprivkey58: 'yprv',
// P2WSH
zpubkey: 0x04b24746,
zprivkey: 0x04b2430c,
zpubkey58: 'zpub',
zprivkey58: 'zprv',

coinType: 0
};

Expand Down Expand Up @@ -676,10 +688,22 @@ testnet.deploys = [

testnet.keyPrefix = {
privkey: 0xef,

xpubkey: 0x043587cf,
xprivkey: 0x04358394,
xpubkey58: 'tpub',
xprivkey58: 'tprv',
// P2WPKH in P2SH -- note variable name (x, y, z) vs actual (t, u, v)
ypubkey: 0x044a5262,
yprivkey: 0x044a4e28,
ypubkey58: 'upub',
yprivkey58: 'uprv',
// P2WPKH -- note variable name (x, y, z) vs actual (t, u, v)
zpubkey: 0x045f1cf6,
zprivkey: 0x045f18bc,
zpubkey58: 'vpub',
zprivkey58: 'vprv',

coinType: 1
};

Expand Down Expand Up @@ -840,6 +864,18 @@ regtest.keyPrefix = {
xprivkey: 0x04358394,
xpubkey58: 'tpub',
xprivkey58: 'tprv',

// P2WPKH in P2SH -- note variable name (x, y, z) vs actual (t, u, v)
ypubkey: 0x044a5262,
yprivkey: 0x044a4e28,
ypubkey58: 'upub',
yprivkey58: 'uprv',
// P2WPKH -- note variable name (x, y, z) vs actual (t, u, v)
zpubkey: 0x045f1cf6,
zprivkey: 0x045f18bc,
zpubkey58: 'vpub',
zprivkey58: 'vprv',

coinType: 1
};

Expand Down
Loading