hd and wallet work.

This commit is contained in:
Christopher Jeffrey 2016-02-04 02:44:16 -08:00
parent 517a5d488b
commit fa22c79dbe
8 changed files with 219 additions and 316 deletions

View File

@ -39,6 +39,9 @@ bcoin.protocol.network.set(process.env.BCOIN_NETWORK || 'main');
bcoin.bn = bn;
bcoin.elliptic = elliptic;
bcoin.signature = require('elliptic/lib/elliptic/ec/signature');
bcoin.utils.assert(!bcoin.ecdsa.signature);
bcoin.ecdsa.signature = require('elliptic/lib/elliptic/ec/signature');
bcoin.utils.assert(!bcoin.ecdsa.keypair);
bcoin.ecdsa.keypair = require('elliptic/lib/elliptic/ec/key');
bcoin.hash = hash;
bcoin.async = async;

View File

@ -34,15 +34,9 @@ function Address(options) {
this.storage = options.storage;
this.label = options.label || '';
this.change = !!options.change;
this.derived = !!options.derived;
this.key = bcoin.keypair({
priv: options.priv,
pub: options.pub,
key: options.key,
personalization: options.personalization,
entropy: options.entropy,
compressed: options.compressed
});
this.key = bcoin.keypair(options);
this.type = options.type || 'pubkeyhash';
this.subtype = options.subtype;

View File

@ -538,7 +538,7 @@ HDPrivateKey.prototype._normalize = function _normalize(data, version) {
};
HDPrivateKey.prototype._seed = function _seed(seed) {
if (seed instanceof bcoin.hd.seed)
if (seed instanceof HDSeed)
seed = seed.seed;
if (utils.isHex(seed))
@ -592,27 +592,20 @@ HDPrivateKey.prototype._unbuild = function _unbuild(xkey) {
};
HDPrivateKey.prototype._build = function _build(data) {
var sequence = [];
var sequence = new Array(82);
var off = 0;
var checksum, xprivkey, pair, privateKey, publicKey, size, fingerPrint;
utils.copy(data.version, sequence, off, true);
off += data.version.length;
utils.copy(data.depth, sequence, off, true);
off += data.depth.length;
utils.copy(data.parentFingerPrint, sequence, off, true);
off += data.parentFingerPrint.length;
utils.copy(data.childIndex, sequence, off, true);
off += data.childIndex.length;
utils.copy(data.chainCode, sequence, off, true);
off += data.chainCode.length;
utils.copy([0], sequence, off, true);
off += [0].length;
utils.copy(data.privateKey, sequence, off, true);
off += data.privateKey.length;
checksum = utils.dsha256(sequence).slice(0, 4);
utils.copy(checksum, sequence, off, true);
off += checksum.length;
off += utils.copy(data.version, sequence, off);
off += utils.copy(data.depth, sequence, off);
off += utils.copy(data.parentFingerPrint, sequence, off);
off += utils.copy(data.childIndex, sequence, off);
off += utils.copy(data.chainCode, sequence, off);
off += utils.copy([0], sequence, off);
off += utils.copy(data.privateKey, sequence, off);
checksum = utils.dsha256(sequence.slice(0, off)).slice(0, 4);
off += utils.copy(checksum, sequence, off);
assert(off === 82);
xprivkey = utils.toBase58(sequence);
@ -635,7 +628,18 @@ HDPrivateKey.prototype._build = function _build(data) {
this.fingerPrint = fingerPrint;
this.publicKey = publicKey;
this.hdpub = bcoin.hd.pub(this);
this.hdpub = new HDPublicKey({
version: this.version,
depth: this.depth,
parentFingerPrint: this.parentFingerPrint,
childIndex: this.childIndex,
chainCode: this.chainCode,
privateKey: this.privateKey,
checksum: this.checksum,
publicKey: this.publicKey,
master: this.master
});
this.xpubkey = this.hdpub.xpubkey;
this.pair = bcoin.ecdsa.keyPair({ priv: this.privateKey });
};
@ -697,18 +701,6 @@ HDPrivateKey._getIndexes = function _getIndexes(path) {
index = +step;
if (i === 0) {
indexes.purpose = index;
} else if (i === 1) {
indexes.coinType = index;
} else if (i === 2) {
indexes.accountIndex = index;
} else if (i === 3) {
indexes.isChange = index === 1;
} else if (i === 4) {
indexes.addressIndex = index;
}
if (hardened)
index += constants.hd.hardened;
@ -743,130 +735,76 @@ HDPrivateKey.prototype.deriveString = function deriveString(path) {
indexes = HDPrivateKey._getIndexes(path);
child = indexes.reduce(function(prev, index, i) {
return indexes.reduce(function(prev, index, i) {
return prev.derive(index);
}, this);
child.purpose = indexes.purpose;
child.coinType = indexes.coinType;
child.accountIndex = indexes.accountIndex;
child.isChange = indexes.isChange;
child.addressIndex = indexes.addressIndex;
return child;
};
HDPrivateKey.prototype.toJSON = function toJSON(encrypt) {
var json = {
v: 1,
name: 'keypair',
name: 'hdkey',
encrypted: encrypt ? true : false
};
if (this.hd) {
if (this.hd.xprivkey) {
if (this.hd.seed) {
json.mnemonic = encrypt
? encrypt(this.hd.seed.mnemonic)
: this.hd.seed.mnemonic;
json.passphrase = encrypt
? encrypt(this.hd.seed.passphrase)
: this.hd.seed.passphrase;
return json;
}
json.xpriv = encrypt
? encrypt(this.hd.xprivkey)
: this.hd.xprivkey;
if (this instanceof HDPrivateKey) {
if (this.seed) {
json.mnemonic = encrypt
? encrypt(this.seed.mnemonic)
: this.seed.mnemonic;
json.passphrase = encrypt
? encrypt(this.seed.passphrase)
: this.seed.passphrase;
return json;
}
json.xpub = this.hd.xpubkey;
json.xprivkey = encrypt
? encrypt(this.xprivkey)
: this.xprivkey;
return json;
}
if (this._key.priv) {
json.priv = encrypt
? encrypt(this.getPrivate('base58'))
: this.getPrivate('base58');
return json;
}
json.xpubkey = this.hd.xpubkey;
json.pub = this.getPublic('hex');
return json;
};
HDPrivateKey.fromJSON = function fromJSON(json, decrypt) {
var key, priv, pub, compressed, xprivkey;
var path = {};
assert.equal(json.v, 1);
assert.equal(json.name, 'keypair');
assert.equal(json.name, 'hdkey');
if (json.encrypted && !decrypt)
throw new Error('Cannot decrypt address');
if (json.mnemonic) {
return new KeyPair({
key: bcoin.hd.priv({
seed: bcoin.hd.seed({
mnemonic: json.encrypted
? decrypt(json.mnemonic)
: json.mnemonic,
passphrase: json.encrypted
? decrypt(json.passphrase)
: json.passphrase
})
return new HDPrivateKey({
seed: new HDSeed({
mnemonic: json.encrypted
? decrypt(json.mnemonic)
: json.mnemonic,
passphrase: json.encrypted
? decrypt(json.passphrase)
: json.passphrase
})
});
}
if (json.xpriv) {
xprivkey = json.xpriv;
if (json.encrypted)
xprivkey = decrypt(xprivkey);
return new KeyPair({
key: bcoin.hd.priv({
xkey: xprivkey
})
if (json.xprivkey) {
return new HDPrivateKey({
xkey: json.encrypted
? decrypt(json.xprivkey)
: json.xprivkey
});
}
if (json.xpub) {
return new KeyPair({
key: bcoin.hd.pub({
xkey: json.xpub
})
});
}
if (json.priv) {
priv = json.priv;
if (json.encrypted)
priv = decrypt(priv);
key = KeyPair.fromSecret(json.priv);
priv = key.priv;
compressed = key.compressed;
return new KeyPair({
priv: priv,
compressed: compressed
});
}
if (json.pub) {
pub = bcoin.utils.toArray(json.pub, 'hex');
compressed = pub[0] !== 0x04;
return new KeyPair({
pub: pub,
compressed: compressed
if (json.xpubkey) {
return new HDPublicKey({
xkey: json.xpubkey
});
}
assert(false);
};
/**
* HD Public Key
*/
@ -917,6 +855,8 @@ HDPublicKey.prototype.deriveCosignerAddress = HDPrivateKey.prototype.deriveCosig
HDPublicKey.prototype.isPurpose45 = HDPrivateKey.prototype.isPurpose45;
HDPublicKey.prototype.isAccount44 = HDPrivateKey.prototype.isAccount44;
HDPublicKey.prototype.toJSON = HDPrivateKey.prototype.toJSON;
HDPublicKey.fromJSON = HDPrivateKey.fromJSON;
HDPublicKey.isExtended = function isExtended(data) {
if (typeof data !== 'string')
@ -956,25 +896,19 @@ HDPublicKey.prototype._unbuild = function _unbuild(xkey) {
};
HDPublicKey.prototype._build = function _build(data) {
var sequence = [];
var sequence = new Array(82);
var off = 0;
var checksum, xpubkey, publicKey, size, fingerPrint;
utils.copy(data.version, sequence, off, true);
off += data.version.length;
utils.copy(data.depth, sequence, off, true);
off += data.depth.length;
utils.copy(data.parentFingerPrint, sequence, off, true);
off += data.parentFingerPrint.length;
utils.copy(data.childIndex, sequence, off, true);
off += data.childIndex.length;
utils.copy(data.chainCode, sequence, off, true);
off += data.chainCode.length;
utils.copy(data.publicKey, sequence, off, true);
off += data.publicKey.length;
checksum = utils.dsha256(sequence).slice(0, 4);
utils.copy(checksum, sequence, off, true);
off += checksum.length;
off += utils.copy(data.version, sequence, off);
off += utils.copy(data.depth, sequence, off);
off += utils.copy(data.parentFingerPrint, sequence, off);
off += utils.copy(data.childIndex, sequence, off);
off += utils.copy(data.chainCode, sequence, off);
off += utils.copy(data.publicKey, sequence, off);
checksum = utils.dsha256(sequence.slice(0, off)).slice(0, 4);
off += utils.copy(checksum, sequence, off);
assert(off === 82);
if (!data.checksum || !data.checksum.length)
data.checksum = checksum;
@ -1156,10 +1090,10 @@ function pbkdf2(key, salt, iterations, dkLen) {
if (typeof salt === 'string')
salt = utils.toArray(salt, null);
var DK = new Array(dkLen);
var U = new Array(hLen);
var T = new Array(hLen);
var block1 = new Array(salt.length + 4);
var DK = new Buffer(dkLen);
var U = new Buffer(hLen);
var T = new Buffer(hLen);
var block1 = new Buffer(salt.length + 4);
var l = Math.ceil(dkLen / hLen);
var r = dkLen - (l - 1) * hLen;
@ -1200,4 +1134,7 @@ function pbkdf2(key, salt, iterations, dkLen) {
hd.seed = HDSeed;
hd.priv = HDPrivateKey;
hd.pub = HDPublicKey;
hd.privateKey = HDPrivateKey;
hd.publicKey = HDPublicKey;
hd.pbkdf2 = pbkdf2;
hd.fromJSON = HDPrivateKey.fromJSON;

View File

@ -29,59 +29,59 @@ function KeyPair(options) {
return options.key;
this.options = options;
this._key = options.key || null;
this.hd = options.hd || null;
this.pair = null;
this.compressed = options.compressed !== false;
if (options.privateKey)
options.priv = options.privateKey;
if (options.key)
options.pair = options.key;
if (options.publicKey)
options.pub = options.publicKey;
if (options.priv)
options.privateKey = options.priv;
if (options.priv instanceof bcoin.hd.priv) {
this.hd = options.priv;
this._key = options.priv.pair;
} else if (options.pub instanceof bcoin.hd.pub) {
this.hd = options.pub;
this._key = options.pub.pair;
} else if (options.hd) {
this.hd = typeof options.hd === 'object'
? bcoin.hd.priv(options.hd)
: bcoin.hd.priv();
this._key = this.hd.pair;
} else if (options.key) {
if ((options.key instanceof bcoin.hd.priv)
|| (options.key instanceof bcoin.hd.pub)) {
this.hd = options.key;
this._key = options.key.pair;
} else {
this._key = options.key;
}
} else if (options.priv || options.pub) {
this._key = bcoin.ecdsa.keyPair({
priv: options.priv,
pub: options.pub
if (options.pub)
options.publicKey = options.pub;
if (options.passphrase)
options.entropy = utils.sha256(options.passphrase);
if (options.privateKey instanceof bcoin.hd.privateKey) {
this.pair = options.privateKey.pair;
} else if (options.publicKey instanceof bcoin.hd.publicKey) {
this.pair = options.publicKey.pair;
} else if (options.pair) {
assert(options.pair instanceof bcoin.ecdsa.keypair);
this.pair = options.pair;
} else if (options.privateKey || options.publicKey) {
this.pair = bcoin.ecdsa.keyPair({
priv: options.privateKey,
pub: options.publicKey
});
} else {
this._key = bcoin.ecdsa.genKeyPair({
this.pair = bcoin.ecdsa.genKeyPair({
pers: options.personalization,
entropy: options.entropy
|| (options.passphrase ? utils.sha256(options.passphrase) : null)
});
}
}
KeyPair.prototype.__defineGetter__('priv', function() {
return this._key.getPrivate();
return this.pair.getPrivate();
});
KeyPair.prototype.__defineGetter__('pub', function() {
return this._key.getPublic();
return this.pair.getPublic();
});
KeyPair.prototype.__defineGetter__('privateKey', function() {
return this.pair.getPrivate();
});
KeyPair.prototype.__defineGetter__('publicKey', function() {
return this.pair.getPublic();
});
KeyPair.prototype.getPrivate = function getPrivate(enc) {
var priv = this._key.getPrivate();
var priv = this.pair.getPrivate();
if (!priv)
return;
@ -98,7 +98,7 @@ KeyPair.prototype.getPrivate = function getPrivate(enc) {
};
KeyPair.prototype.getPublic = function getPublic(enc) {
var pub = this._key.getPublic(this.compressed, 'array');
var pub = this.pair.getPublic(this.compressed, 'array');
if (enc === 'base58')
return utils.toBase58(pub);
@ -125,7 +125,7 @@ KeyPair.toSecret = function toSecret(priv, compressed) {
arr = arr.concat(priv);
if (compressed)
if (compressed !== false)
arr.push(1);
chk = utils.checksum(arr);
@ -151,7 +151,7 @@ KeyPair.fromSecret = function fromSecret(priv) {
}
return new KeyPair({
priv: priv,
privateKey: priv,
compressed: compressed
});
};
@ -163,29 +163,7 @@ KeyPair.prototype.toJSON = function toJSON(encrypt) {
encrypted: encrypt ? true : false
};
if (this.hd) {
if (this.hd.xprivkey) {
if (this.hd.seed) {
json.mnemonic = encrypt
? encrypt(this.hd.seed.mnemonic)
: this.hd.seed.mnemonic;
json.passphrase = encrypt
? encrypt(this.hd.seed.passphrase)
: this.hd.seed.passphrase;
return json;
}
json.xpriv = encrypt
? encrypt(this.hd.xprivkey)
: this.hd.xprivkey;
return json;
}
json.xpub = this.hd.xpubkey;
return json;
}
if (this._key.priv) {
if (this.pair.priv) {
json.priv = encrypt
? encrypt(this.getPrivate('base58'))
: this.getPrivate('base58');
@ -206,40 +184,6 @@ KeyPair.fromJSON = function fromJSON(json, decrypt) {
if (json.encrypted && !decrypt)
throw new Error('Cannot decrypt address');
if (json.mnemonic) {
return new KeyPair({
key: bcoin.hd.priv({
seed: bcoin.hd.seed({
mnemonic: json.encrypted
? decrypt(json.mnemonic)
: json.mnemonic,
passphrase: json.encrypted
? decrypt(json.passphrase)
: json.passphrase
})
})
});
}
if (json.xpriv) {
xprivkey = json.xpriv;
if (json.encrypted)
xprivkey = decrypt(xprivkey);
return new KeyPair({
key: bcoin.hd.priv({
xkey: xprivkey
})
});
}
if (json.xpub) {
return new KeyPair({
key: bcoin.hd.pub({
xkey: json.xpub
})
});
}
if (json.priv) {
priv = json.priv;
if (json.encrypted)
@ -249,7 +193,7 @@ KeyPair.fromJSON = function fromJSON(json, decrypt) {
priv = key.priv;
compressed = key.compressed;
return new KeyPair({
priv: priv,
privateKey: priv,
compressed: compressed
});
}
@ -258,7 +202,7 @@ KeyPair.fromJSON = function fromJSON(json, decrypt) {
pub = bcoin.utils.toArray(json.pub, 'hex');
compressed = pub[0] !== 0x04;
return new KeyPair({
pub: pub,
publicKey: pub,
compressed: compressed
});
}

View File

@ -2061,7 +2061,7 @@ script.isLowDER = function isLowDER(sig) {
return false;
try {
sig = new bcoin.signature(sig.slice(0, -1));
sig = new bcoin.ecdsa.signature(sig.slice(0, -1));
} catch (e) {
return false;
}

View File

@ -307,6 +307,23 @@ TXPool.prototype.fromJSON = function fromJSON(json) {
}, this);
};
TXPool.fromJSON = function fromJSON(wallet, json) {
var txPool;
assert.equal(json.v, 1);
assert.equal(json.type, 'tx-pool');
txPool = new TXPool(wallet);
utils.nextTick(function() {
json.txs.forEach(function(tx) {
txPool.add(bcoin.tx.fromJSON(tx));
});
});
return tx;
};
/**
* Expose
*/

View File

@ -240,7 +240,7 @@ utils.array2utf8 = function array2utf8(arr) {
utils.copy = function copy(src, dst, off, force) {
if (Buffer.isBuffer(src) && Buffer.isBuffer(dst)) {
assert(!force);
utils.assert(!force);
return src.copy(dst, off, 0, src.length);
}

View File

@ -38,32 +38,38 @@ function Wallet(options) {
delete options.hd;
}
if (options.priv
|| options.pub
|| options.key
if (options.key)
options.pair = options.key;
if (options.priv)
options.privateKey = options.priv;
if (options.pub)
options.publicKey = options.pub;
if (options.privateKey
|| options.publicKey
|| options.pair
|| options.personalization
|| options.entropy
|| options.passphrase
|| options.compressed) {
if ((options.key instanceof bcoin.hd.priv)
|| options.key instanceof bcoin.hd.pub) {
options.master = options.key;
delete options.key;
} else if (options.priv instanceof bcoin.hd.priv) {
options.master = options.priv;
delete options.priv;
} else if (options.pub instanceof bcoin.hd.pub) {
options.master = options.pub;
delete options.pub;
if ((options.pair instanceof bcoin.hd.privateKey)
|| options.pair instanceof bcoin.hd.publicKey) {
options.master = options.pair;
delete options.pair;
} else if (options.privateKey instanceof bcoin.hd.privateKey) {
options.master = options.privateKey;
delete options.privateKey;
} else if (options.publicKey instanceof bcoin.hd.publicKey) {
options.master = options.publicKey;
delete options.publicKey;
}
}
this.options = options;
this.addresses = [];
this.master = options.master || null;
if (this.master && !(this.master instanceof bcoin.keypair))
this.master = bcoin.keypair({ hd: this.master });
this._addressTable = {};
this._labelMap = {};
@ -119,28 +125,29 @@ function Wallet(options) {
throw new Error('n ranges between 1 and ' + this.nmax);
if (this.bip45) {
this.purposeKey = this.master.hd.isPurpose45()
? this.master.hd
: this.master.hd.derivePurpose45();
} else if (this.hd) {
this.accountKey = this.master.hd.isAccount44()
? this.master.hd
: this.master.hd.deriveAccount44(this.accountIndex);
this.purposeKey = this.master.isPurpose45()
? this.master
: this.master.derivePurpose45();
} else if (this.bip44) {
this.accountKey = this.master.isAccount44()
? this.master
: this.master.deriveAccount44(this.accountIndex);
}
if (!options.addresses)
options.addresses = [];
if (options.priv
|| options.pub
|| options.key
if (options.privateKey
|| options.publicKey
|| options.pair
|| options.personalization
|| options.entropy
|| options.passphrase
|| options.compressed) {
options.addresses.push({
priv: options.priv,
pub: options.pub,
key: options.key,
privateKey: options.privateKey,
publicKey: options.publicKey,
pair: options.pair,
personalization: options.personalization,
entropy: options.entropy,
compressed: options.compressed,
@ -168,10 +175,33 @@ function Wallet(options) {
// generate the last receiving address. However, since "normal" wallets
// cannot deterministically generate keys, we have to buffer the generated
// key for later.
if (!this.bip45) {
if (this.hd) {
// Generate the last known receiving address
key = this.createKey(false, Math.max(0, this.addressDepth - 1));
if (this.bip44) {
// Generate the last known receiving address
key = this.createKey(false, Math.max(0, this.addressDepth - 1));
this.current = bcoin.address({
priv: key.priv,
type: this.type,
subtype: this.subtype,
m: this.m,
n: this.n,
keys: options.keys,
derived: true
});
} else if (this.normal) {
// Try to find the last receiving address if there is one.
receiving = options.addresses.filter(function(address) {
return !address.change
&& ((address.priv || address.privateKey)
|| (address.pub || address.publicKey)
|| (address.key || address.pair));
}).pop();
if (receiving) {
this.current = bcoin.address(receiving);
} else {
// No receiving address is in this wallet yet, generate
// it and save it so createKey can recreate it later.
key = this.createKey();
this._firstKey = key;
this.current = bcoin.address({
priv: key.priv,
type: this.type,
@ -180,27 +210,6 @@ function Wallet(options) {
n: this.n,
keys: options.keys
});
} else {
// Try to find the last receiving address if there is one.
receiving = options.addresses.filter(function(address) {
return !address.change && (address.priv || address.pub || address.key);
}).pop();
if (receiving) {
this.current = bcoin.address(receiving);
} else {
// No receiving address is in this wallet yet, generate
// it and save it so createKey can recreate it later.
key = this.createKey();
this._firstKey = key;
this.current = bcoin.address({
priv: key.priv,
type: this.type,
subtype: this.subtype,
m: this.m,
n: this.n,
keys: options.keys
});
}
}
}
@ -218,13 +227,13 @@ inherits(Wallet, EventEmitter);
// Wallet ID:
// bip45: Purpose key address
// HD: Account key address
// Normal: Address of first key in wallet
// bip44: Account key address
// normal: Address of first key in wallet
Wallet.prototype.getID = function() {
if (this.bip45)
return bcoin.address.key2addr(this.purposeKey.publicKey);
if (this.hd)
if (this.bip44)
return bcoin.address.key2addr(this.accountKey.publicKey);
if (this.addresses.length)
@ -274,7 +283,7 @@ Wallet.prototype._initAddresses = function() {
this.prefix = 'bt/wallet/' + this.getID() + '/';
this.tx = new bcoin.txPool(this);
this.tx = options.tx || bcoin.txPool(this);
this._init();
};
@ -513,7 +522,8 @@ Wallet.prototype.createAddress = function createAddress(change, index) {
m: this.m,
n: this.n,
keys: [],
change: change
change: change,
derived: !!this.hd
};
if (index == null) {
@ -652,7 +662,7 @@ Wallet.prototype.getPublicKey = function getPublicKey(enc) {
};
Wallet.prototype.createKey = function createKey(change, index) {
var key, pub, priv;
var key;
if (!this.hd) {
if (this._firstKey) {
@ -931,6 +941,12 @@ Wallet.prototype.toJSON = function toJSON(encrypt) {
addressDepth: this.addressDepth,
changeDepth: this.changeDepth,
cosignerIndex: this.cosignerIndex,
master: this.master ? this.master.toJSON(encrypt) : null,
addresses: this.addresses.filter(function(address) {
return !address.derived;
}, this).map(function(address) {
return address.toJSON(encrypt);
}),
keys: this.bip45
? this.purposeKeys.map(function(key) {
return key.xpubkey;
@ -938,14 +954,6 @@ Wallet.prototype.toJSON = function toJSON(encrypt) {
: this.keys.map(function(key) {
return utils.toBase58(key);
}),
master: this.master ? this.master.toJSON(encrypt) : null,
addresses: this.addresses.filter(function(address) {
if (this.hd)
return false;
return true;
}, this).map(function(address) {
return address.toJSON(encrypt);
}),
balance: utils.toBTC(this.getBalance()),
tx: this.tx.toJSON()
};
@ -970,12 +978,12 @@ Wallet.fromJSON = function fromJSON(json, decrypt) {
changeDepth: json.changeDepth,
cosignerIndex: json.cosignerIndex,
master: json.master
? bcoin.address.fromJSON(json.master, decrypt)
? bcoin.hd.fromJSON(json.master, decrypt)
: null,
keys: json.keys,
addresses: json.addresses.map(function(address) {
return bcoin.address.fromJSON(address, decrypt);
})
}),
keys: json.keys
});
w.tx.fromJSON(json.tx);