wallet refactor. fixes.
This commit is contained in:
parent
9fc6848dce
commit
6cc4c866dc
@ -41,32 +41,23 @@ function Address(options) {
|
||||
this.path = options.path;
|
||||
|
||||
this.type = options.type || 'pubkeyhash';
|
||||
this.subtype = options.subtype;
|
||||
this.keys = [];
|
||||
this.m = options.m || 1;
|
||||
this.n = options.n || 1;
|
||||
this.redeem = null;
|
||||
|
||||
if (this.n > 1) {
|
||||
if (this.type !== 'multisig')
|
||||
this.type = 'scripthash';
|
||||
if (this.type === 'scripthash')
|
||||
this.subtype = 'multisig';
|
||||
}
|
||||
if (this.n > 1)
|
||||
this.type = 'multisig';
|
||||
|
||||
if (network.prefixes[this.type] == null)
|
||||
throw new Error('Unknown prefix: ' + this.type);
|
||||
assert(this.type === 'pubkeyhash' || this.type === 'multisig');
|
||||
this.prefixType = this.type === 'multisig' ? 'scripthash' : 'pubkeyhash';
|
||||
|
||||
this.nmax = this.type === 'scripthash'
|
||||
? (this.key.compressed ? 15 : 7)
|
||||
: 3;
|
||||
if (network.prefixes[this.prefixType] == null)
|
||||
throw new Error('Unknown prefix: ' + this.prefixType);
|
||||
|
||||
if (this.m < 1 || this.m > this.n)
|
||||
throw new Error('m ranges between 1 and n');
|
||||
|
||||
if (this.n < 1 || this.n > this.nmax)
|
||||
throw new Error('n ranges between 1 and ' + this.nmax);
|
||||
|
||||
this.addKey(this.getPublicKey());
|
||||
|
||||
(options.keys || []).forEach(function(key) {
|
||||
@ -76,7 +67,7 @@ function Address(options) {
|
||||
if (options.redeem || options.script)
|
||||
this.setRedeem(options.redeem || options.script);
|
||||
|
||||
this.prefix = 'bt/address/' + this.getKeyAddress() + '/';
|
||||
this.prefix = 'bt/address/' + this.getID() + '/';
|
||||
}
|
||||
|
||||
inherits(Address, EventEmitter);
|
||||
@ -85,6 +76,10 @@ Address.prototype.__defineGetter__('balance', function() {
|
||||
return this.getBalance();
|
||||
});
|
||||
|
||||
Address.prototype.getID = function getID() {
|
||||
return this.getKeyAddress();
|
||||
};
|
||||
|
||||
Address.prototype.getAll = function getAll() {
|
||||
return this._wallet.getAll(this);
|
||||
};
|
||||
@ -115,8 +110,8 @@ Address.prototype.setRedeem = function setRedeem(redeem) {
|
||||
if (!utils.isBytes(redeem))
|
||||
redeem = bcoin.script.encode(redeem);
|
||||
|
||||
this.type = 'scripthash';
|
||||
this.subtype = null;
|
||||
this.type = 'multisig';
|
||||
this.prefixType = 'scripthash';
|
||||
this.redeem = redeem;
|
||||
this.emit('update script', old, this.getScriptAddress());
|
||||
};
|
||||
@ -192,49 +187,53 @@ Address.fromSecret = function fromSecret(privateKey) {
|
||||
};
|
||||
|
||||
Address.prototype.getScript = function getScript() {
|
||||
if (this.type !== 'scripthash')
|
||||
var redeem;
|
||||
|
||||
if (this.prefixType !== 'scripthash')
|
||||
return;
|
||||
|
||||
if (this._script)
|
||||
return this._script;
|
||||
|
||||
if (this.redeem)
|
||||
return this._script = this.redeem.slice();
|
||||
if (this.redeem) {
|
||||
redeem = this.redeem.slice();
|
||||
assert(utils.isBytes(redeem));
|
||||
} else if (this.keys.length < this.n) {
|
||||
redeem = bcoin.script.createPubkeyhash(this.getKeyHash());
|
||||
redeem = bcoin.script.encode(redeem);
|
||||
} else {
|
||||
redeem = bcoin.script.createMultisig(this.keys, this.m, this.n);
|
||||
redeem = bcoin.script.encode(redeem);
|
||||
}
|
||||
|
||||
if (this.subtype === 'pubkey')
|
||||
this._script = bcoin.script.createPubkey(this.getPublicKey());
|
||||
else if (this.subtype === 'pubkeyhash' || this.keys.length < this.n)
|
||||
this._script = bcoin.script.createPubkeyhash(this.getKeyHash());
|
||||
else if (this.subtype === 'multisig')
|
||||
this._script = bcoin.script.createMultisig(this.keys, this.m, this.n);
|
||||
else
|
||||
assert(false);
|
||||
if (redeem.length > 520)
|
||||
throw new Error('Redeem script too large (520 byte limit).');
|
||||
|
||||
this._script = bcoin.script.encode(this._script);
|
||||
this._script = redeem;
|
||||
|
||||
return this._script;
|
||||
};
|
||||
|
||||
Address.prototype.getScriptHash = function getScriptHash() {
|
||||
if (this.type !== 'scripthash')
|
||||
if (this.prefixType !== 'scripthash')
|
||||
return;
|
||||
|
||||
if (this._scriptHash)
|
||||
return this._scriptHash;
|
||||
|
||||
this._scriptHash = utils.ripesha(this.getScript());
|
||||
this._scriptHash = Address.hash160(this.getScript());
|
||||
|
||||
return this._scriptHash;
|
||||
};
|
||||
|
||||
Address.prototype.getScriptAddress = function getScriptAddress() {
|
||||
if (this.type !== 'scripthash')
|
||||
if (this.prefixType !== 'scripthash')
|
||||
return;
|
||||
|
||||
if (this._scriptAddress)
|
||||
return this._scriptAddress;
|
||||
|
||||
this._scriptAddress = Address.hash2addr(this.getScriptHash(), this.type);
|
||||
this._scriptAddress = Address.toAddress(this.getScriptHash(), this.prefixType);
|
||||
|
||||
return this._scriptAddress;
|
||||
};
|
||||
@ -256,7 +255,7 @@ Address.prototype.getKeyHash = function getKeyHash() {
|
||||
if (this._hash)
|
||||
return this._hash;
|
||||
|
||||
this._hash = Address.key2hash(this.getPublicKey());
|
||||
this._hash = Address.hash160(this.getPublicKey());
|
||||
|
||||
return this._hash;
|
||||
};
|
||||
@ -265,127 +264,41 @@ Address.prototype.getKeyAddress = function getKeyAddress() {
|
||||
if (this._address)
|
||||
return this._address;
|
||||
|
||||
this._address = Address.hash2addr(this.getKeyHash(), 'pubkeyhash');
|
||||
this._address = Address.toAddress(this.getKeyHash(), 'pubkeyhash');
|
||||
|
||||
return this._address;
|
||||
};
|
||||
|
||||
Address.prototype.getHash = function getHash() {
|
||||
if (this.type === 'scripthash')
|
||||
if (this.prefixType === 'scripthash')
|
||||
return this.getScriptHash();
|
||||
return this.getKeyHash();
|
||||
};
|
||||
|
||||
Address.prototype.getAddress = function getAddress() {
|
||||
if (this.type === 'scripthash')
|
||||
if (this.prefixType === 'scripthash')
|
||||
return this.getScriptAddress();
|
||||
return this.getKeyAddress();
|
||||
};
|
||||
|
||||
Address.key2hash = function key2hash(key) {
|
||||
key = utils.toBuffer(key);
|
||||
return utils.ripesha(key);
|
||||
Address.prototype._getAddressTable = function _getAddressTable() {
|
||||
var addressTable = {};
|
||||
addressTable[this.getAddress()] = true;
|
||||
return addressTable;
|
||||
};
|
||||
|
||||
Address.hash2addr = function hash2addr(hash, prefix) {
|
||||
var addr;
|
||||
|
||||
hash = utils.toArray(hash, 'hex');
|
||||
|
||||
prefix = network.prefixes[prefix || 'pubkeyhash'];
|
||||
hash = [ prefix ].concat(hash);
|
||||
|
||||
addr = hash.concat(utils.checksum(hash));
|
||||
|
||||
return utils.toBase58(addr);
|
||||
};
|
||||
|
||||
Address.key2addr = function key2addr(key, prefix) {
|
||||
return Address.hash2addr(Address.key2hash(key), prefix);
|
||||
};
|
||||
|
||||
Address.__defineGetter__('prefixes', function() {
|
||||
if (Address._prefixes)
|
||||
return Address._prefixes;
|
||||
|
||||
Address._prefixes = ['pubkeyhash', 'scripthash'].reduce(function(out, prefix) {
|
||||
var ch = Address.hash2addr(Address.key2hash([]), prefix)[0];
|
||||
out[ch] = prefix;
|
||||
return out;
|
||||
}, {});
|
||||
|
||||
return Address._prefixes;
|
||||
});
|
||||
|
||||
Address.addr2hash = function addr2hash(addr, prefix) {
|
||||
var chk;
|
||||
|
||||
if (prefix == null && typeof addr === 'string')
|
||||
prefix = Address.prefixes[addr[0]];
|
||||
|
||||
if (!utils.isBuffer(addr))
|
||||
addr = utils.fromBase58(addr);
|
||||
|
||||
prefix = network.prefixes[prefix || 'pubkeyhash'];
|
||||
|
||||
if (addr.length !== 25)
|
||||
return [];
|
||||
|
||||
if (addr[0] !== prefix)
|
||||
return [];
|
||||
|
||||
chk = utils.checksum(addr.slice(0, -4));
|
||||
|
||||
if (utils.readU32(chk, 0) !== utils.readU32(addr, 21))
|
||||
return [];
|
||||
|
||||
return addr.slice(1, -4);
|
||||
};
|
||||
|
||||
Address.validate = function validate(addr, prefix) {
|
||||
if (!addr || typeof addr !== 'string')
|
||||
return false;
|
||||
|
||||
var p = Address.addr2hash(addr, prefix);
|
||||
|
||||
return p.length !== 0;
|
||||
};
|
||||
|
||||
Address.validateAddress = Address.validate;
|
||||
|
||||
Address.prototype.ownOutput = function ownOutput(tx, index) {
|
||||
var scriptHash = this.getScriptHash();
|
||||
var hash = this.getKeyHash();
|
||||
var key = this.getPublicKey();
|
||||
var keys = this.keys;
|
||||
var addressTable = this._getAddressTable();
|
||||
var outputs = tx.outputs;
|
||||
|
||||
if ((tx instanceof bcoin.output) || (tx instanceof bcoin.coin))
|
||||
outputs = [tx];
|
||||
|
||||
outputs = outputs.filter(function(output, i) {
|
||||
var s = output.script;
|
||||
|
||||
if (index != null && index !== i)
|
||||
return false;
|
||||
|
||||
return output.testScript(key, hash, keys, scriptHash, null);
|
||||
|
||||
if (bcoin.script.isPubkey(s, key))
|
||||
return true;
|
||||
|
||||
if (bcoin.script.isPubkeyhash(s, hash))
|
||||
return true;
|
||||
|
||||
if (bcoin.script.isMultisig(s, keys))
|
||||
return true;
|
||||
|
||||
if (scriptHash) {
|
||||
if (bcoin.script.isScripthash(s, scriptHash))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return output.test(addressTable, true);
|
||||
}, this);
|
||||
|
||||
if (outputs.length === 0)
|
||||
@ -395,11 +308,7 @@ Address.prototype.ownOutput = function ownOutput(tx, index) {
|
||||
};
|
||||
|
||||
Address.prototype.ownInput = function ownInput(tx, index) {
|
||||
var scriptHash = this.getScriptHash();
|
||||
var hash = this.getKeyHash();
|
||||
var key = this.getPublicKey();
|
||||
var redeem = this.getScript();
|
||||
var keys = this.keys;
|
||||
var addressTable = this._getAddressTable();
|
||||
var inputs = tx.inputs;
|
||||
|
||||
if (tx instanceof bcoin.input) {
|
||||
@ -415,25 +324,9 @@ Address.prototype.ownInput = function ownInput(tx, index) {
|
||||
return false;
|
||||
|
||||
if (input.output)
|
||||
return !!this.ownOutput(input.output);
|
||||
return input.output.test(addressTable, true);
|
||||
|
||||
return input.testScript(key, redeem, null);
|
||||
|
||||
// if (bcoin.script.isPubkeyInput(input.script, key, tx, i))
|
||||
// return true;
|
||||
|
||||
if (bcoin.script.isPubkeyhashInput(input.script, key))
|
||||
return true;
|
||||
|
||||
// if (bcoin.script.isMultisigInput(input.script, keys, tx, i))
|
||||
// return true;
|
||||
|
||||
if (redeem) {
|
||||
if (bcoin.script.isScripthashInput(input.script, redeem))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return input.test(addressTable, true);
|
||||
}, this);
|
||||
|
||||
if (inputs.length === 0)
|
||||
@ -544,7 +437,7 @@ Address.prototype.__defineGetter__('address', function() {
|
||||
return this.getAddress();
|
||||
});
|
||||
|
||||
Address.prototype.toAddress = function toAddress() {
|
||||
Address.prototype.toExplore = function toExplore() {
|
||||
return {
|
||||
address: this.getAddress(),
|
||||
hash160: utils.toHex(this.getHash()),
|
||||
@ -555,6 +448,77 @@ Address.prototype.toAddress = function toAddress() {
|
||||
};
|
||||
};
|
||||
|
||||
Address.hash160 = function hash160(key) {
|
||||
key = utils.toBuffer(key);
|
||||
return utils.ripesha(key);
|
||||
};
|
||||
|
||||
Address.toAddress = function toAddress(hash, prefix) {
|
||||
var addr;
|
||||
|
||||
hash = utils.toArray(hash, 'hex');
|
||||
|
||||
prefix = network.prefixes[prefix || 'pubkeyhash'];
|
||||
hash = [prefix].concat(hash);
|
||||
|
||||
addr = hash.concat(utils.checksum(hash));
|
||||
|
||||
return utils.toBase58(addr);
|
||||
};
|
||||
|
||||
Address.compile = function compile(key, prefix) {
|
||||
return Address.toAddress(Address.hash160(key), prefix);
|
||||
};
|
||||
|
||||
Address.toHash = function toHash(addr, prefix) {
|
||||
var chk;
|
||||
|
||||
if (prefix == null && typeof addr === 'string')
|
||||
prefix = Address.prefixes[addr[0]];
|
||||
|
||||
if (!utils.isBuffer(addr))
|
||||
addr = utils.fromBase58(addr);
|
||||
|
||||
prefix = network.prefixes[prefix || 'pubkeyhash'];
|
||||
|
||||
if (addr.length !== 25)
|
||||
return [];
|
||||
|
||||
if (addr[0] !== prefix)
|
||||
return [];
|
||||
|
||||
chk = utils.checksum(addr.slice(0, -4));
|
||||
|
||||
if (utils.readU32(chk, 0) !== utils.readU32(addr, 21))
|
||||
return [];
|
||||
|
||||
return addr.slice(1, -4);
|
||||
};
|
||||
|
||||
Address.__defineGetter__('prefixes', function() {
|
||||
if (Address._prefixes)
|
||||
return Address._prefixes;
|
||||
|
||||
Address._prefixes = ['pubkeyhash', 'scripthash'].reduce(function(out, prefix) {
|
||||
var ch = Address.compile([], prefix)[0];
|
||||
out[ch] = prefix;
|
||||
return out;
|
||||
}, {});
|
||||
|
||||
return Address._prefixes;
|
||||
});
|
||||
|
||||
Address.validate = function validate(addr, prefix) {
|
||||
if (!addr || typeof addr !== 'string')
|
||||
return false;
|
||||
|
||||
var p = Address.toHash(addr, prefix);
|
||||
|
||||
return p.length !== 0;
|
||||
};
|
||||
|
||||
Address.validateAddress = Address.validate;
|
||||
|
||||
Address.prototype.toJSON = function toJSON(encrypt) {
|
||||
return {
|
||||
v: 1,
|
||||
@ -565,11 +529,9 @@ Address.prototype.toJSON = function toJSON(encrypt) {
|
||||
derived: this.derived,
|
||||
index: this.index,
|
||||
path: this.path,
|
||||
address: this.getKeyAddress(),
|
||||
scriptAddress: this.getScriptAddress(),
|
||||
address: this.getAddress(),
|
||||
key: this.key.toJSON(encrypt),
|
||||
type: this.type,
|
||||
subtype: this.subtype,
|
||||
redeem: this.redeem ? utils.toHex(this.redeem) : null,
|
||||
keys: this.keys.map(utils.toBase58),
|
||||
m: this.m,
|
||||
@ -594,7 +556,6 @@ Address.fromJSON = function fromJSON(json, decrypt) {
|
||||
path: json.path,
|
||||
key: bcoin.keypair.fromJSON(json.key, decrypt),
|
||||
type: json.type,
|
||||
subtype: json.subtype,
|
||||
redeem: json.redeem ? utils.toArray(json.redeem, 'hex') : null,
|
||||
keys: json.keys.map(utils.fromBase58),
|
||||
m: json.m,
|
||||
|
||||
@ -639,9 +639,13 @@ Chain.prototype.getTip = function getTip() {
|
||||
};
|
||||
|
||||
Chain.prototype.isFull = function isFull() {
|
||||
var delta;
|
||||
|
||||
if (!this.tip)
|
||||
return false;
|
||||
var delta = utils.now() - this.tip.ts;
|
||||
|
||||
delta = utils.now() - this.tip.ts;
|
||||
|
||||
return delta < 40 * 60;
|
||||
};
|
||||
|
||||
@ -653,14 +657,15 @@ Chain.prototype.fillPercent = function fillPercent() {
|
||||
|
||||
Chain.prototype.hashRange = function hashRange(start, end) {
|
||||
var hashes = [];
|
||||
var i;
|
||||
|
||||
start = this.byTime(start);
|
||||
end = this.byTime(end);
|
||||
|
||||
if (!start || !end)
|
||||
return [];
|
||||
return hashes;
|
||||
|
||||
for (var i = start.height; i < end.height + 1; i++)
|
||||
for (i = start.height; i < end.height + 1; i++)
|
||||
hashes.push(this.db.get(i).hash);
|
||||
|
||||
return hashes;
|
||||
@ -743,11 +748,17 @@ Chain.prototype.getHeight = function getHeight(hash) {
|
||||
|
||||
Chain.prototype.getNextBlock = function getNextBlock(hash) {
|
||||
var entry = this.byHash(hash);
|
||||
var next;
|
||||
|
||||
if (!entry || !entry.next)
|
||||
if (!entry)
|
||||
return null;
|
||||
|
||||
return entry.next.hash;
|
||||
next = entry.next;
|
||||
|
||||
if (!next)
|
||||
return;
|
||||
|
||||
return next.hash;
|
||||
};
|
||||
|
||||
Chain.prototype.getSize = function getSize() {
|
||||
|
||||
@ -53,7 +53,8 @@ ChainBlock.prototype.getProof = function getProof() {
|
||||
};
|
||||
|
||||
ChainBlock.prototype.getChainwork = function() {
|
||||
return (this.prev ? this.prev.chainwork : new bn(0)).add(this.getProof());
|
||||
var prev = this.prev;
|
||||
return (prev ? prev.chainwork : new bn(0)).add(this.getProof());
|
||||
};
|
||||
|
||||
ChainBlock.prototype.getMedianTime = function() {
|
||||
|
||||
@ -107,6 +107,9 @@ ChainDB.prototype._free = function(buf) {
|
||||
};
|
||||
|
||||
ChainDB.prototype.exists = function exists() {
|
||||
if (!bcoin.fs)
|
||||
return true;
|
||||
|
||||
try {
|
||||
fs.statSync(this.file);
|
||||
return true;
|
||||
@ -116,6 +119,9 @@ ChainDB.prototype.exists = function exists() {
|
||||
};
|
||||
|
||||
ChainDB.prototype.getSize = function getSize() {
|
||||
if (!bcoin.fs)
|
||||
return this.ramdisk.size;
|
||||
|
||||
try {
|
||||
return fs.statSync(this.file).size;
|
||||
} catch (e) {
|
||||
@ -306,18 +312,26 @@ ChainDB.prototype.remove = function remove(height) {
|
||||
while (this.isNull(height))
|
||||
height--;
|
||||
|
||||
if (height < 0)
|
||||
height = 0;
|
||||
assert(height >= 0);
|
||||
|
||||
fs.ftruncateSync(this.fd, (height + 1) * BLOCK_SIZE);
|
||||
|
||||
this.size = (height + 1) * BLOCK_SIZE;
|
||||
this.tip = height;
|
||||
this.truncate(height);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
ChainDB.prototype.truncate = function truncate(height) {
|
||||
this.size = (height + 1) * BLOCK_SIZE;
|
||||
this.tip = height;
|
||||
|
||||
if (!bcoin.fs) {
|
||||
this.ramdisk.truncate(this.size);
|
||||
return;
|
||||
}
|
||||
|
||||
fs.ftruncateSync(this.fd, this.size);
|
||||
};
|
||||
|
||||
ChainDB.prototype.isNull = function isNull(height) {
|
||||
var data = this._readSync(4, height * BLOCK_SIZE);
|
||||
if (!data)
|
||||
|
||||
@ -199,7 +199,7 @@ HDPrivateKey.prototype.scan44 = function scan44(options, txByAddress, callback)
|
||||
// respect the gap limit described below
|
||||
return (function next() {
|
||||
var address = chain.derive(addressIndex++);
|
||||
var addr = bcoin.address.key2addr(address.publicKey);
|
||||
var addr = bcoin.address.compile(address.publicKey);
|
||||
|
||||
return txByAddress(addr, function(err, txs) {
|
||||
var result;
|
||||
@ -346,7 +346,7 @@ HDPrivateKey.prototype.scan45 = function scan45(options, txByAddress, callback)
|
||||
|
||||
return (function next() {
|
||||
var address = chain.derive(addressIndex++);
|
||||
var addr = bcoin.address.key2addr(address.publicKey);
|
||||
var addr = bcoin.address.compile(address.publicKey);
|
||||
|
||||
return txByAddress(addr, function(err, txs) {
|
||||
var result;
|
||||
@ -837,7 +837,7 @@ function HDPublicKey(options) {
|
||||
if (!options)
|
||||
throw new Error('No options for HDPublicKey');
|
||||
|
||||
if (HDPublicKey.isExtended(data))
|
||||
if (HDPublicKey.isExtended(options))
|
||||
options = { xkey: options };
|
||||
|
||||
data = options.xkey
|
||||
|
||||
@ -104,7 +104,7 @@ Input.prototype.__defineGetter__('id', function() {
|
||||
});
|
||||
|
||||
Input.prototype.__defineGetter__('address', function() {
|
||||
return this.data.scriptAddress || this.addresses[0];
|
||||
return this.data.address;
|
||||
});
|
||||
|
||||
Input.prototype.__defineGetter__('signatures', function() {
|
||||
@ -163,34 +163,6 @@ Input.prototype.__defineGetter__('value', function() {
|
||||
return this.output.value;
|
||||
});
|
||||
|
||||
Input.prototype.__defineGetter__('addr', function() {
|
||||
return this.address;
|
||||
});
|
||||
|
||||
Input.prototype.__defineGetter__('addrs', function() {
|
||||
return this.addresses;
|
||||
});
|
||||
|
||||
Input.prototype.__defineGetter__('pub', function() {
|
||||
return this.key;
|
||||
});
|
||||
|
||||
Input.prototype.__defineGetter__('pubs', function() {
|
||||
return this.keys;
|
||||
});
|
||||
|
||||
Input.prototype.__defineGetter__('sig', function() {
|
||||
return this.signature;
|
||||
});
|
||||
|
||||
Input.prototype.__defineGetter__('sigs', function() {
|
||||
return this.signatures;
|
||||
});
|
||||
|
||||
Input.prototype.__defineGetter__('scriptaddr', function() {
|
||||
return this.scriptAddress;
|
||||
});
|
||||
|
||||
// Schema and defaults for data object:
|
||||
// {
|
||||
// type: String,
|
||||
@ -253,25 +225,18 @@ Input.prototype.getData = function getData() {
|
||||
return Input.getData(this);
|
||||
};
|
||||
|
||||
Input.prototype.getAddresses = function getAddresses() {
|
||||
return this.getData().addresses;
|
||||
};
|
||||
|
||||
Input.prototype.getScriptAddress = function getScriptAddress() {
|
||||
return this.getData().scriptAddress;
|
||||
};
|
||||
|
||||
Input.prototype.getKeyAddress = function getKeyAddress() {
|
||||
return this.getData().addresses[0];
|
||||
Input.prototype.getType = function getType() {
|
||||
var prev = this.output ? this.output.script : null;
|
||||
return bcoin.script.getInputType(this.script, prev);
|
||||
};
|
||||
|
||||
Input.prototype.getAddress = function getAddress() {
|
||||
var data = this.getData();
|
||||
var prev = this.output ? this.output.script : null;
|
||||
return bcoin.script.getInputAddress(this.script, prev);
|
||||
};
|
||||
|
||||
if (data.scriptAddress)
|
||||
return data.scriptAddress;
|
||||
|
||||
return data.addresses[0];
|
||||
Input.prototype.isRBF = function isRBF() {
|
||||
return this.sequence === 0xffffffff - 1;
|
||||
};
|
||||
|
||||
Input.prototype.isFinal = function isFinal() {
|
||||
@ -338,17 +303,16 @@ Input.prototype.testScript = function testScript(key, redeem, type) {
|
||||
};
|
||||
|
||||
Input.prototype.test = function test(addressTable) {
|
||||
var data = this.getData();
|
||||
var i;
|
||||
var address = this.getAddress();
|
||||
|
||||
if (data.scriptAddress) {
|
||||
if (addressTable[data.scriptAddress] != null)
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i = 0; i < data.addresses.length; i++) {
|
||||
if (addressTable[data.addresses[i]] != null)
|
||||
return true;
|
||||
if (address) {
|
||||
if (Array.isArray(addressTable)) {
|
||||
if (addressTable.indexOf(address) !== -1)
|
||||
return true;
|
||||
} else {
|
||||
if (addressTable[address] != null)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@ -75,7 +75,7 @@ Output.prototype.__defineGetter__('id', function() {
|
||||
});
|
||||
|
||||
Output.prototype.__defineGetter__('address', function() {
|
||||
return this.data.scriptAddress || this.addresses[0];
|
||||
return this.data.address;
|
||||
});
|
||||
|
||||
Output.prototype.__defineGetter__('signatures', function() {
|
||||
@ -122,35 +122,6 @@ Output.prototype.__defineGetter__('text', function() {
|
||||
return this.data.text;
|
||||
});
|
||||
|
||||
// Legacy
|
||||
Output.prototype.__defineGetter__('addr', function() {
|
||||
return this.address;
|
||||
});
|
||||
|
||||
Output.prototype.__defineGetter__('addrs', function() {
|
||||
return this.addresses;
|
||||
});
|
||||
|
||||
Output.prototype.__defineGetter__('pub', function() {
|
||||
return this.key;
|
||||
});
|
||||
|
||||
Output.prototype.__defineGetter__('pubs', function() {
|
||||
return this.keys;
|
||||
});
|
||||
|
||||
Output.prototype.__defineGetter__('sig', function() {
|
||||
return this.signature;
|
||||
});
|
||||
|
||||
Output.prototype.__defineGetter__('sigs', function() {
|
||||
return this.signatures;
|
||||
});
|
||||
|
||||
Output.prototype.__defineGetter__('scriptaddr', function() {
|
||||
return this.scriptAddress;
|
||||
});
|
||||
|
||||
// Schema and defaults for data object:
|
||||
// {
|
||||
// type: String,
|
||||
@ -195,25 +166,12 @@ Output.prototype.getData = function getData() {
|
||||
return Output.getData(this);
|
||||
};
|
||||
|
||||
Output.prototype.getAddresses = function getAddresses() {
|
||||
return this.getData().addresses;
|
||||
};
|
||||
|
||||
Output.prototype.getScriptAddress = function getScriptAddress() {
|
||||
return this.getData().scriptAddress;
|
||||
};
|
||||
|
||||
Output.prototype.getKeyAddress = function getKeyAddress() {
|
||||
return this.getData().addresses[0];
|
||||
Output.prototype.getType = function getType() {
|
||||
return bcoin.script.getOutputType(this.script);
|
||||
};
|
||||
|
||||
Output.prototype.getAddress = function getAddress() {
|
||||
var data = this.getData();
|
||||
|
||||
if (data.scriptAddress)
|
||||
return data.scriptAddress;
|
||||
|
||||
return data.addresses[0];
|
||||
return bcoin.script.getOutputAddress(this.script);
|
||||
};
|
||||
|
||||
Output.prototype.getID = function getID() {
|
||||
@ -255,17 +213,16 @@ Output.prototype.testScript = function testScript(key, hash, keys, scriptHash, t
|
||||
};
|
||||
|
||||
Output.prototype.test = function test(addressTable) {
|
||||
var data = this.getData();
|
||||
var i;
|
||||
var address = this.getAddress();
|
||||
|
||||
if (data.scriptAddress) {
|
||||
if (addressTable[data.scriptAddress] != null)
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i = 0; i < data.addresses.length; i++) {
|
||||
if (addressTable[data.addresses[i]] != null)
|
||||
return true;
|
||||
if (address) {
|
||||
if (Array.isArray(addressTable)) {
|
||||
if (addressTable.indexOf(address) !== -1)
|
||||
return true;
|
||||
} else {
|
||||
if (addressTable[address] != null)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@ -205,13 +205,18 @@ exports.block = {
|
||||
|
||||
exports.tx = {
|
||||
maxSize: 100000,
|
||||
fee: 10000,
|
||||
dust: 5460,
|
||||
minFee: 10000,
|
||||
bareMultisig: true,
|
||||
freeThreshold: exports.coin.muln(144).divn(250),
|
||||
maxFreeSize: 1000
|
||||
};
|
||||
|
||||
exports.tx.dustThreshold = new bn(182)
|
||||
.muln(exports.tx.minFee)
|
||||
.divn(1000)
|
||||
.muln(3)
|
||||
.toNumber();
|
||||
|
||||
exports.script = {
|
||||
maxSize: 10000,
|
||||
maxStack: 1000,
|
||||
|
||||
@ -27,9 +27,7 @@ network.set = function set(type) {
|
||||
main = network.main = {};
|
||||
|
||||
main.prefixes = {
|
||||
pubkey: 0,
|
||||
pubkeyhash: 0,
|
||||
multisig: 0,
|
||||
scripthash: 5,
|
||||
privkey: 128,
|
||||
xpubkey: 0x0488b21e,
|
||||
@ -132,9 +130,7 @@ testnet = network.testnet = {};
|
||||
testnet.type = 'testnet';
|
||||
|
||||
testnet.prefixes = {
|
||||
pubkey: 111,
|
||||
pubkeyhash: 111,
|
||||
multisig: 111,
|
||||
scripthash: 196,
|
||||
privkey: 239,
|
||||
xpubkey: 0x043587cf,
|
||||
@ -220,9 +216,7 @@ regtest = network.regtest = {};
|
||||
regtest.type = 'testnet';
|
||||
|
||||
regtest.prefixes = {
|
||||
pubkey: 111,
|
||||
pubkeyhash: 111,
|
||||
multisig: 111,
|
||||
scripthash: 196,
|
||||
privkey: 239,
|
||||
xpubkey: 0x043587cf,
|
||||
|
||||
@ -762,7 +762,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
|
||||
n2 = script.num(stack.pop());
|
||||
n1 = script.num(stack.pop());
|
||||
val = n2.cmp(n1) <= 0 && n1.cmp(n3) < 0;
|
||||
stack.push(val.cmpn(0) !== 0 ? [ 1 ] : []);
|
||||
stack.push(val.cmpn(0) !== 0 ? [1] : []);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -811,7 +811,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
|
||||
if (!res)
|
||||
return false;
|
||||
} else {
|
||||
stack.push(res ? [ 1 ] : []);
|
||||
stack.push(res ? [1] : []);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -841,7 +841,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
|
||||
if (!res)
|
||||
return false;
|
||||
} else {
|
||||
stack.push(res ? [ 1 ] : []);
|
||||
stack.push(res ? [1] : []);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -918,7 +918,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
|
||||
if (!res)
|
||||
return false;
|
||||
} else {
|
||||
stack.push(res ? [ 1 ] : []);
|
||||
stack.push(res ? [1] : []);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -1204,10 +1204,11 @@ script.getRedeem = function getRedeem(s) {
|
||||
if (!Array.isArray(s[s.length - 1]))
|
||||
return;
|
||||
|
||||
return bcoin.script.decode(s[s.length - 1]);
|
||||
return script.decode(s[s.length - 1]);
|
||||
};
|
||||
|
||||
script.getType = function getType(s) {
|
||||
script.getType =
|
||||
script.getOutputType = function getOutputType(s) {
|
||||
return (script.isPubkey(s) && 'pubkey')
|
||||
|| (script.isPubkeyhash(s) && 'pubkeyhash')
|
||||
|| (script.isMultisig(s) && 'multisig')
|
||||
@ -1268,19 +1269,20 @@ script.isEncoded = function isEncoded(s) {
|
||||
};
|
||||
|
||||
script._locktime = function _locktime(s) {
|
||||
var i;
|
||||
|
||||
if (s.length < 2)
|
||||
return;
|
||||
|
||||
for (i = 0; i < s.length; i++) {
|
||||
if (utils.isBuffer(s[i]) && s[i + 1] === 'checklocktimeverify')
|
||||
return s[i];
|
||||
}
|
||||
if (!utils.isBuffer(s[0]))
|
||||
return;
|
||||
|
||||
if (s[1] !== 'checklocktimeverify')
|
||||
return;
|
||||
|
||||
return s[0];
|
||||
};
|
||||
|
||||
script.isLocktime = function isLocktime(s) {
|
||||
return !!script._locktime(s);
|
||||
return script._locktime(s) != null;
|
||||
};
|
||||
|
||||
script.getLocktime = function getLocktime(s) {
|
||||
@ -1349,13 +1351,6 @@ script._getInputData = function _getInputData(s, type) {
|
||||
assert(typeof type === 'string');
|
||||
|
||||
if (type === 'pubkey') {
|
||||
if (s.length < 1) {
|
||||
return {
|
||||
type: 'pubkey',
|
||||
side: 'input',
|
||||
none: true
|
||||
};
|
||||
}
|
||||
sig = s[0];
|
||||
return {
|
||||
type: 'pubkey',
|
||||
@ -1366,35 +1361,22 @@ script._getInputData = function _getInputData(s, type) {
|
||||
}
|
||||
|
||||
if (type === 'pubkeyhash') {
|
||||
if (s.length < 2) {
|
||||
return {
|
||||
type: 'pubkeyhash',
|
||||
side: 'input',
|
||||
none: true
|
||||
};
|
||||
}
|
||||
sig = s[0];
|
||||
key = s[1];
|
||||
hash = bcoin.wallet.key2hash(key);
|
||||
address = bcoin.wallet.hash2addr(hash, 'pubkeyhash');
|
||||
hash = bcoin.address.hash160(key);
|
||||
address = bcoin.address.toAddress(hash, 'pubkeyhash');
|
||||
return {
|
||||
type: 'pubkeyhash',
|
||||
side: 'input',
|
||||
signatures: [sig],
|
||||
keys: [key],
|
||||
hashes: [hash],
|
||||
address: address,
|
||||
addresses: [address]
|
||||
};
|
||||
}
|
||||
|
||||
if (type === 'multisig') {
|
||||
if (s.length < 2) {
|
||||
return {
|
||||
type: 'multisig',
|
||||
side: 'input',
|
||||
none: true
|
||||
};
|
||||
}
|
||||
sig = s.slice(1);
|
||||
return {
|
||||
type: 'multisig',
|
||||
@ -1406,19 +1388,12 @@ script._getInputData = function _getInputData(s, type) {
|
||||
}
|
||||
|
||||
if (type === 'scripthash') {
|
||||
if (s.length < 1) {
|
||||
return {
|
||||
type: 'scripthash',
|
||||
side: 'input',
|
||||
none: true
|
||||
};
|
||||
}
|
||||
raw = s[s.length - 1];
|
||||
redeem = script.decode(raw);
|
||||
locktime = script.getLocktime(redeem);
|
||||
hash = bcoin.wallet.key2hash(raw);
|
||||
address = bcoin.wallet.hash2addr(hash, 'scripthash');
|
||||
output = script.getOutputData(script.getSubscript(redeem));
|
||||
hash = bcoin.address.hash160(raw);
|
||||
address = bcoin.address.toAddress(hash, 'scripthash');
|
||||
output = script.getOutputData(redeem, true);
|
||||
input = script._getInputData(s.slice(0, -1), output.type);
|
||||
delete input.none;
|
||||
return utils.merge(input, output, {
|
||||
@ -1426,6 +1401,7 @@ script._getInputData = function _getInputData(s, type) {
|
||||
side: 'input',
|
||||
subtype: output.type,
|
||||
redeem: redeem,
|
||||
address: address,
|
||||
scriptHash: hash,
|
||||
scriptAddress: address,
|
||||
locktime: locktime
|
||||
@ -1435,45 +1411,56 @@ script._getInputData = function _getInputData(s, type) {
|
||||
return script.getUnknownData(s);
|
||||
};
|
||||
|
||||
script.getOutputData = function getOutputData(s) {
|
||||
var key, hash, address;
|
||||
script.getOutputData = function getOutputData(s, inScriptHash) {
|
||||
var key, hash, address, mhash, maddress;
|
||||
|
||||
if (script.isPubkey(s)) {
|
||||
key = s[0];
|
||||
hash = bcoin.wallet.key2hash(key);
|
||||
address = bcoin.wallet.hash2addr(hash, 'pubkey');
|
||||
hash = bcoin.address.hash160(key);
|
||||
// Convert p2pk to p2pkh addresses
|
||||
address = bcoin.address.toAddress(hash, 'pubkeyhash');
|
||||
return {
|
||||
type: 'pubkey',
|
||||
side: 'output',
|
||||
keys: [key],
|
||||
hashes: [hash],
|
||||
address: address,
|
||||
addresses: [address]
|
||||
};
|
||||
}
|
||||
|
||||
if (script.isPubkeyhash(s)) {
|
||||
hash = s[2];
|
||||
address = bcoin.address.toAddress(hash, 'pubkeyhash');
|
||||
return {
|
||||
type: 'pubkeyhash',
|
||||
side: 'output',
|
||||
hashes: [hash],
|
||||
addresses: [bcoin.wallet.hash2addr(hash, 'pubkeyhash')]
|
||||
address: address,
|
||||
addresses: [address]
|
||||
};
|
||||
}
|
||||
|
||||
if (script.isMultisig(s)) {
|
||||
key = s.slice(1, -2);
|
||||
hash = key.map(function(key) {
|
||||
return bcoin.wallet.key2hash(key);
|
||||
return bcoin.address.hash160(key);
|
||||
});
|
||||
// Convert bare multisig to p2pkh addresses
|
||||
address = hash.map(function(hash) {
|
||||
return bcoin.wallet.hash2addr(hash, 'multisig');
|
||||
return bcoin.address.toAddress(hash, 'pubkeyhash');
|
||||
});
|
||||
// Convert bare multisig script to scripthash address
|
||||
if (!inScriptHash) {
|
||||
mhash = bcoin.address.hash160(s._raw || script.encode(s));
|
||||
maddress = bcoin.address.toAddress(mhash, 'scripthash');
|
||||
}
|
||||
return {
|
||||
type: 'multisig',
|
||||
side: 'output',
|
||||
keys: key,
|
||||
hashes: hash,
|
||||
address: maddress,
|
||||
addresses: address,
|
||||
m: s[0],
|
||||
n: s[s.length - 2]
|
||||
@ -1482,11 +1469,13 @@ script.getOutputData = function getOutputData(s) {
|
||||
|
||||
if (script.isScripthash(s)) {
|
||||
hash = s[1];
|
||||
address = bcoin.address.toAddress(hash, 'scripthash');
|
||||
return {
|
||||
type: 'scripthash',
|
||||
side: 'output',
|
||||
address: address,
|
||||
scriptHash: hash,
|
||||
scriptAddress: bcoin.wallet.hash2addr(hash, 'scripthash')
|
||||
scriptAddress: address
|
||||
};
|
||||
}
|
||||
|
||||
@ -1515,11 +1504,11 @@ script.getUnknownData = function getUnknownData(s) {
|
||||
}
|
||||
|
||||
hash = key.map(function(key) {
|
||||
return bcoin.wallet.key2hash(key);
|
||||
return bcoin.address.hash160(key);
|
||||
});
|
||||
|
||||
address = hash.map(function(hash) {
|
||||
return bcoin.wallet.hash2addr(hash, 'pubkey');
|
||||
return bcoin.address.toAddress(hash, 'pubkeyhash');
|
||||
});
|
||||
|
||||
return {
|
||||
@ -1532,6 +1521,41 @@ script.getUnknownData = function getUnknownData(s) {
|
||||
};
|
||||
};
|
||||
|
||||
script.getInputAddress = function getInputAddress(s, prev) {
|
||||
if (prev)
|
||||
return script.getOutputAddress(prev);
|
||||
|
||||
if (script.isPubkeyInput(s))
|
||||
return;
|
||||
|
||||
if (script.isPubkeyhashInput(s))
|
||||
return bcoin.address.compile(s[1], 'pubkeyhash');
|
||||
|
||||
if (script.isMultisigInput(s))
|
||||
return;
|
||||
|
||||
if (script.isScripthashInput(s))
|
||||
return bcoin.address.compile(s[s.length - 1], 'scripthash');
|
||||
};
|
||||
|
||||
script.getOutputAddress = function getOutputAddress(s) {
|
||||
// Convert p2pk to p2pkh addresses
|
||||
if (script.isPubkey(s))
|
||||
return bcoin.address.compile(s[0], 'pubkeyhash');
|
||||
|
||||
if (script.isPubkeyhash(s))
|
||||
return bcoin.address.toAddress(s[2], 'pubkeyhash')
|
||||
|
||||
// Convert bare multisig to scripthash address
|
||||
if (script.isMultisig(s)) {
|
||||
s = s._raw || script.encode(s);
|
||||
return bcoin.address.compile(s, 'scripthash');
|
||||
}
|
||||
|
||||
if (script.isScripthash(s))
|
||||
return bcoin.address.toAddress(s[1], 'scripthash');
|
||||
};
|
||||
|
||||
script.isPubkey = function isPubkey(s, key) {
|
||||
var res;
|
||||
|
||||
@ -1679,7 +1703,10 @@ script.isNulldata = function isNulldata(s) {
|
||||
return s[1];
|
||||
};
|
||||
|
||||
script.getInputType = function getInputType(s) {
|
||||
script.getInputType = function getInputType(s, prev) {
|
||||
if (prev)
|
||||
return script.getOutputType(prev);
|
||||
|
||||
return (script.isPubkeyInput(s) && 'pubkey')
|
||||
|| (script.isPubkeyhashInput(s) && 'pubkeyhash')
|
||||
|| (script.isMultisigInput(s) && 'multisig')
|
||||
@ -1793,6 +1820,9 @@ script.isScripthashInput = function isScripthashInput(s, data, strict) {
|
||||
// If the last data element is a valid
|
||||
// signature or key, it's _extremely_
|
||||
// unlikely this is a scripthash.
|
||||
if (script.isDummy(raw))
|
||||
return false;
|
||||
|
||||
if (script.isSignatureEncoding(raw))
|
||||
return false;
|
||||
|
||||
@ -1815,7 +1845,7 @@ script.isScripthashInput = function isScripthashInput(s, data, strict) {
|
||||
|
||||
// P2SH redeem scripts can be nonstandard: make
|
||||
// it easier for other functions to parse this.
|
||||
redeem = script.getSubscript(script.decode(raw));
|
||||
redeem = script.decode(raw);
|
||||
|
||||
// Get the "real" scriptSig
|
||||
s = s.slice(0, -1);
|
||||
@ -2185,7 +2215,7 @@ script.getScripthashSigops = function getScripthashSigops(s) {
|
||||
if (!script.isPushOnly(s))
|
||||
return 0;
|
||||
|
||||
s = script.getSubscript(script.decode(s[s.length - 1]));
|
||||
s = script.getRedeem(s);
|
||||
|
||||
return script.getSigops(s, true);
|
||||
};
|
||||
|
||||
@ -257,7 +257,7 @@ TX.prototype.scriptInput = function scriptInput(index, pub, redeem) {
|
||||
input.script = [[]];
|
||||
} else if (bcoin.script.isPubkeyhash(s)) {
|
||||
// P2PKH
|
||||
if (!utils.isEqual(s[2], bcoin.wallet.key2hash(pub)))
|
||||
if (!utils.isEqual(s[2], bcoin.address.hash160(pub)))
|
||||
return false;
|
||||
// Already has a script template (at least)
|
||||
if (input.script.length)
|
||||
@ -403,7 +403,7 @@ TX.prototype.signInput = function signInput(index, key, type) {
|
||||
|
||||
// Get pubkey and pubkey hash.
|
||||
pub = key.getPublic(true, 'array');
|
||||
pkh = bcoin.wallet.key2hash(pub);
|
||||
pkh = bcoin.address.hash160(pub);
|
||||
|
||||
// Add signatures.
|
||||
if (bcoin.script.isPubkey(s)) {
|
||||
@ -731,13 +731,13 @@ TX.prototype.scriptOutput = function scriptOutput(index, options) {
|
||||
// https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki
|
||||
// hash160 [20-byte-redeemscript-hash] equal
|
||||
script = bcoin.script.createScripthash(
|
||||
bcoin.wallet.addr2hash(options.address, 'scripthash')
|
||||
bcoin.address.toHash(options.address, 'scripthash')
|
||||
);
|
||||
} else if (options.address) {
|
||||
// P2PKH Transaction
|
||||
// dup hash160 [pubkey-hash] equalverify checksig
|
||||
script = bcoin.script.createPubkeyhash(
|
||||
bcoin.wallet.addr2hash(options.address, 'pubkeyhash')
|
||||
bcoin.address.toHash(options.address, 'pubkeyhash')
|
||||
);
|
||||
} else if (options.key) {
|
||||
// P2PK Transaction
|
||||
@ -814,9 +814,6 @@ TX.prototype.signatureHash = function signatureHash(index, s, type) {
|
||||
// if strictenc is not enabled.
|
||||
// assert(utils.isFinite(type));
|
||||
|
||||
// Remove code separators.
|
||||
// s = script.getSubscript(s);
|
||||
|
||||
// Remove all signatures.
|
||||
for (i = 0; i < copy.inputs.length; i++)
|
||||
copy.inputs[i].script = [];
|
||||
@ -881,7 +878,7 @@ TX.prototype.tbsHash = function tbsHash(enc, force) {
|
||||
|
||||
if (!this._tbsHash || force) {
|
||||
for (i = 0; i < copy.inputs.length; i++) {
|
||||
if (!copy.isCoinbase())
|
||||
if (!copy.inputs[i].isCoinbase())
|
||||
copy.inputs[i].script = [];
|
||||
}
|
||||
|
||||
@ -1008,14 +1005,7 @@ TX.prototype.maxSize = function maxSize() {
|
||||
}
|
||||
|
||||
// Byte for varint size of input script
|
||||
if (size < 0xfd)
|
||||
size += 0;
|
||||
else if (size <= 0xffff)
|
||||
size += 2;
|
||||
else if (size <= 0xffffffff)
|
||||
size += 4;
|
||||
else
|
||||
size += 8;
|
||||
size += utils.sizeIntv(size);
|
||||
|
||||
total += size;
|
||||
}
|
||||
@ -1027,7 +1017,7 @@ TX.prototype.getInputs = function getInputs(unspent, address, fee) {
|
||||
var tx = this.clone();
|
||||
var cost = tx.getOutputValue();
|
||||
var totalkb = 1;
|
||||
var total = cost.addn(constants.tx.fee);
|
||||
var total = cost.addn(constants.tx.minFee);
|
||||
var inputs = [];
|
||||
var lastAdded = 0;
|
||||
var size, newkb, change;
|
||||
@ -1042,18 +1032,25 @@ TX.prototype.getInputs = function getInputs(unspent, address, fee) {
|
||||
return a.height - b.height;
|
||||
});
|
||||
|
||||
function addInput(unspent) {
|
||||
// Add new inputs until TX will have enough
|
||||
// funds to cover both minimum post cost
|
||||
// and fee.
|
||||
var index = tx._addInput(unspent);
|
||||
inputs.push(tx.inputs[index]);
|
||||
lastAdded++;
|
||||
return tx.getInputValue().cmp(total) < 0;
|
||||
function addCoins() {
|
||||
var i, index;
|
||||
|
||||
for (i = lastAdded; i < unspent.length; i++) {
|
||||
// Add new inputs until TX will have enough
|
||||
// funds to cover both minimum post cost
|
||||
// and fee.
|
||||
index = tx._addInput(unspent[i]);
|
||||
inputs.push(tx.inputs[index]);
|
||||
lastAdded++;
|
||||
|
||||
// Stop once we're full.
|
||||
if (tx.getInputValue().cmp(total) >= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer `total` funds maximum.
|
||||
unspent.every(addInput);
|
||||
addCoins();
|
||||
|
||||
if (!fee) {
|
||||
// Add dummy output (for `change`) to
|
||||
@ -1064,9 +1061,9 @@ TX.prototype.getInputs = function getInputs(unspent, address, fee) {
|
||||
});
|
||||
|
||||
// if (this.subtractFee) {
|
||||
// var f = new bn((Math.ceil(tx.maxSize() / 1024) - 1) * constants.tx.fee);
|
||||
// var f = new bn((Math.ceil(tx.maxSize() / 1024) - 1) * constants.tx.minFee);
|
||||
// for (var j = 0; j < this.outputs.length; j++) {
|
||||
// if (this.outputs[j].value.cmp(f.addn(constants.tx.dust)) >= 0) {
|
||||
// if (this.outputs[j].value.cmp(f.addn(constants.tx.dustThreshold)) >= 0) {
|
||||
// this.outputs[j].value = this.outputs[j].value.sub(f);
|
||||
// break;
|
||||
// }
|
||||
@ -1081,12 +1078,12 @@ TX.prototype.getInputs = function getInputs(unspent, address, fee) {
|
||||
size = tx.maxSize();
|
||||
|
||||
newkb = Math.ceil(size / 1024) - totalkb;
|
||||
total.iaddn(newkb * constants.tx.fee);
|
||||
total.iaddn(newkb * constants.tx.minFee);
|
||||
totalkb += newkb;
|
||||
|
||||
// Failed to get enough funds, add more inputs.
|
||||
if (tx.getInputValue().cmp(total) < 0)
|
||||
unspent.slice(lastAdded).every(addInput);
|
||||
addCoins();
|
||||
} while (tx.getInputValue().cmp(total) < 0 && lastAdded < unspent.length);
|
||||
}
|
||||
|
||||
@ -1134,7 +1131,7 @@ TX.prototype.fill = function fill(unspent, address, fee) {
|
||||
this.addInput(input);
|
||||
}, this);
|
||||
|
||||
if (result.change.cmpn(constants.tx.dust) < 0) {
|
||||
if (result.change.cmpn(constants.tx.dustThreshold) < 0) {
|
||||
// Do nothing. Change is added to fee.
|
||||
assert.equal(
|
||||
this.getFee().toNumber(),
|
||||
@ -1210,7 +1207,7 @@ TX.prototype._recalculateFee = function recalculateFee() {
|
||||
}
|
||||
|
||||
size = this.maxSize();
|
||||
real = Math.ceil(size / 1024) * constants.tx.fee;
|
||||
real = Math.ceil(size / 1024) * constants.tx.minFee;
|
||||
fee = this.getFee().toNumber();
|
||||
|
||||
// if (this.hardFee)
|
||||
@ -1233,7 +1230,7 @@ TX.prototype._recalculateFee = function recalculateFee() {
|
||||
output.value.iaddn(fee - real);
|
||||
}
|
||||
|
||||
if (output.value.cmpn(constants.tx.dust) < 0) {
|
||||
if (output.value.cmpn(constants.tx.dustThreshold) < 0) {
|
||||
this.outputs.pop();
|
||||
this.changeIndex = -1;
|
||||
return;
|
||||
@ -1428,15 +1425,22 @@ TX.prototype.setLocktime = function setLocktime(locktime) {
|
||||
};
|
||||
|
||||
TX.prototype.increaseFee = function increaseFee(fee) {
|
||||
var i, input;
|
||||
var i, input, result;
|
||||
|
||||
this.inputs = [];
|
||||
|
||||
if (this.changeIndex !== -1)
|
||||
this.outputs.splice(this.changeIndex, 1);
|
||||
|
||||
this.hardFee = fee || this.getFee().add(new bn(10000));
|
||||
this.fill();
|
||||
result = this.fill();
|
||||
|
||||
for (i = 0; i < this.inputs.length; i++) {
|
||||
input = this.inputs[i];
|
||||
input.sequence = 0xffffffff - 1;
|
||||
}
|
||||
|
||||
return !!result.inputs;
|
||||
};
|
||||
|
||||
TX.prototype.hasPrevout = function hasPrevout() {
|
||||
@ -1559,7 +1563,7 @@ TX.prototype.isStandard = function isStandard() {
|
||||
if (type === 'multisig' && !constants.tx.bareMultisig)
|
||||
return false;
|
||||
|
||||
if (output.value.cmpn(constants.tx.dust) < 0)
|
||||
if (output.value.cmpn(constants.tx.dustThreshold) < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -797,7 +797,7 @@ utils.sortHDKeys = function sortHDKeys(keys) {
|
||||
});
|
||||
};
|
||||
|
||||
utils.uniq = function(obj) {
|
||||
utils.uniq = function uniq(obj) {
|
||||
var out = [];
|
||||
var i = 0;
|
||||
|
||||
@ -809,6 +809,22 @@ utils.uniq = function(obj) {
|
||||
return out;
|
||||
};
|
||||
|
||||
utils.uniqs = function uniqs(obj) {
|
||||
var table = {};
|
||||
var out = [];
|
||||
var i = 0;
|
||||
|
||||
for (; i < obj.length; i++) {
|
||||
if (!table[obj[i]]) {
|
||||
out.push(obj[i]);
|
||||
table[obj[i]] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
|
||||
utils.fromCompact = function fromCompact(compact) {
|
||||
var exponent = compact >> 24;
|
||||
var negative = (compact >> 23) & 0x01;
|
||||
|
||||
@ -77,6 +77,7 @@ function Wallet(options) {
|
||||
this.accountIndex = options.accountIndex || 0;
|
||||
this.addressDepth = options.addressDepth || 0;
|
||||
this.changeDepth = options.changeDepth || 0;
|
||||
this.copayBIP45 = options.copayBIP45 || false;
|
||||
this.cosignerIndex = -1;
|
||||
this.sharedCosignerIndex = constants.hd.hardened - 1;
|
||||
this.purposeKeys = options.purposeKeys || [];
|
||||
@ -84,26 +85,21 @@ function Wallet(options) {
|
||||
|
||||
this.hd = !!this.master;
|
||||
this.type = options.type || 'pubkeyhash';
|
||||
this.subtype = options.subtype || null;
|
||||
this.derivation = options.derivation || null;
|
||||
this.compressed = options.compressed !== false;
|
||||
this.keys = [];
|
||||
this.m = options.m || 1;
|
||||
this.n = options.n || 1;
|
||||
this.nmax = this.type === 'scripthash'
|
||||
? (this.compressed ? 15 : 7)
|
||||
: 3;
|
||||
|
||||
if (this.n > 1) {
|
||||
if (this.type !== 'multisig')
|
||||
this.type = 'scripthash';
|
||||
if (this.type === 'scripthash')
|
||||
this.subtype = 'multisig';
|
||||
}
|
||||
if (this.n > 1)
|
||||
this.type = 'multisig';
|
||||
|
||||
assert(this.type === 'pubkeyhash' || this.type === 'multisig');
|
||||
this.prefixType = this.type === 'multisig' ? 'scripthash' : 'pubkeyhash';
|
||||
|
||||
if (!this.derivation) {
|
||||
if (this.master) {
|
||||
if (this.type === 'scripthash' && this.subtype === 'multisig')
|
||||
if (this.type === 'multisig')
|
||||
this.derivation = 'bip45';
|
||||
else
|
||||
this.derivation = 'bip44';
|
||||
@ -112,15 +108,12 @@ function Wallet(options) {
|
||||
}
|
||||
}
|
||||
|
||||
if (network.prefixes[this.type] == null)
|
||||
throw new Error('Unknown prefix: ' + this.type);
|
||||
if (network.prefixes[this.prefixType] == null)
|
||||
throw new Error('Unknown prefix: ' + this.prefixType);
|
||||
|
||||
if (this.m < 1 || this.m > this.n)
|
||||
throw new Error('m ranges between 1 and n');
|
||||
|
||||
if (this.n < 1 || this.n > this.nmax)
|
||||
throw new Error('n ranges between 1 and ' + this.nmax);
|
||||
|
||||
if (this.derivation === 'bip45') {
|
||||
this.purposeKey = this.master.isPurpose45()
|
||||
? this.master
|
||||
@ -140,7 +133,6 @@ function Wallet(options) {
|
||||
publicKey: options.publicKey,
|
||||
pair: options.pair,
|
||||
type: this.type,
|
||||
subtype: this.subtype,
|
||||
m: this.m,
|
||||
n: this.n,
|
||||
keys: [],
|
||||
@ -162,23 +154,7 @@ 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.derivation === 'bip44') {
|
||||
// Generate the last known receiving address
|
||||
key = this.createKey(false, Math.max(0, this.addressDepth - 1));
|
||||
this.currentAddress = bcoin.address({
|
||||
privateKey: key.privateKey,
|
||||
publicKey: key.publicKey,
|
||||
compressed: key.compressed,
|
||||
index: key.index,
|
||||
path: key.path,
|
||||
type: this.type,
|
||||
subtype: this.subtype,
|
||||
m: this.m,
|
||||
n: this.n,
|
||||
keys: options.keys,
|
||||
derived: true
|
||||
});
|
||||
} else if (this.derivation === 'normal') {
|
||||
if (this.derivation === 'normal') {
|
||||
// Try to find the last receiving address if there is one.
|
||||
receiving = options.addresses.filter(function(address) {
|
||||
return !address.change && this._isKeyOptions(address);
|
||||
@ -197,11 +173,11 @@ function Wallet(options) {
|
||||
index: key.index,
|
||||
path: key.path,
|
||||
type: this.type,
|
||||
subtype: this.subtype,
|
||||
m: this.m,
|
||||
n: this.n,
|
||||
keys: options.keys
|
||||
});
|
||||
this.currentAddress._wallet = this;
|
||||
}
|
||||
}
|
||||
|
||||
@ -219,7 +195,7 @@ inherits(Wallet, EventEmitter);
|
||||
|
||||
Wallet.prototype._pruneAddresses = function _pruneAddresses(options) {
|
||||
var addresses = this.addresses.slice();
|
||||
var address;
|
||||
var i, address;
|
||||
|
||||
for (i = 0; i < addresses.length; i++) {
|
||||
address = addresses[i];
|
||||
@ -250,17 +226,17 @@ Wallet.prototype._isKeyOptions = function _isKeyOptions(options) {
|
||||
// normal: Address of first key in wallet
|
||||
Wallet.prototype.getID = function getID() {
|
||||
if (this.derivation === 'bip45')
|
||||
return bcoin.address.key2addr(this.purposeKey.publicKey);
|
||||
return bcoin.address.compile(this.purposeKey.publicKey);
|
||||
|
||||
if (this.derivation === 'bip44')
|
||||
return bcoin.address.key2addr(this.purposeKey.publicKey);
|
||||
return bcoin.address.compile(this.purposeKey.publicKey);
|
||||
|
||||
if (this.derivation === 'normal') {
|
||||
if (this.addresses.length)
|
||||
return this.addresses[0].getKeyAddress();
|
||||
|
||||
if (this._firstKey)
|
||||
return bcoin.address.key2addr(this._firstKey.publicKey);
|
||||
return bcoin.address.compile(this._firstKey.publicKey);
|
||||
}
|
||||
|
||||
assert(false);
|
||||
@ -316,6 +292,14 @@ Wallet.prototype._initAddresses = function _initAddresses() {
|
||||
Wallet.prototype.addKey = function addKey(key) {
|
||||
var hdKey, has, i;
|
||||
|
||||
if (key instanceof bcoin.wallet) {
|
||||
assert(key.derivation === this.derivation);
|
||||
if (key.derivation === 'bip44' || key.derivation === 'bip45')
|
||||
key = key.purposeKey;
|
||||
else
|
||||
key = key.currentAddress.publicKey;
|
||||
}
|
||||
|
||||
if (bcoin.hd.privateKey.isExtended(key))
|
||||
key = bcoin.hd.privateKey(key);
|
||||
else if (bcoin.hd.publicKey.isExtended(key))
|
||||
@ -387,6 +371,7 @@ Wallet.prototype.finalizeKeys = function finalizeKeys(key) {
|
||||
for (i = 0; i < this.purposeKeys.length; i++) {
|
||||
if (this.purposeKeys[i].xpubkey === this.purposeKey.xpubkey) {
|
||||
this.cosignerIndex = i;
|
||||
this._cosignerIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -394,6 +379,7 @@ Wallet.prototype.finalizeKeys = function finalizeKeys(key) {
|
||||
assert(this.cosignerIndex !== -1);
|
||||
|
||||
this._initAddresses();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -402,6 +388,7 @@ Wallet.prototype.finalizeKeys = function finalizeKeys(key) {
|
||||
for (i = 0; i < this.keys.length; i++) {
|
||||
if (utils.isEqual(this.keys[i], this.currentAddress.publicKey)) {
|
||||
this.cosignerIndex = i;
|
||||
this._cosignerIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -416,6 +403,14 @@ Wallet.prototype.removeKey = function removeKey(key) {
|
||||
|
||||
assert(!this._keysFinalized);
|
||||
|
||||
if (key instanceof bcoin.wallet) {
|
||||
assert(key.derivation === this.derivation);
|
||||
if (key.derivation === 'bip44' || key.derivation === 'bip45')
|
||||
key = key.purposeKey;
|
||||
else
|
||||
key = key.currentAddress.publicKey;
|
||||
}
|
||||
|
||||
if (bcoin.hd.privateKey.isExtended(key))
|
||||
key = bcoin.hd.privateKey(key);
|
||||
else if (bcoin.hd.publicKey.isExtended(key))
|
||||
@ -527,9 +522,7 @@ Wallet.prototype._getAddressTable = function() {
|
||||
|
||||
for (i = 0; i < this.addresses.length; i++) {
|
||||
address = this.addresses[i];
|
||||
if (address.type === 'scripthash')
|
||||
addresses[address.getScriptAddress()] = i;
|
||||
addresses[address.getKeyAddress()] = i;
|
||||
addresses[address.getAddress()] = i;
|
||||
}
|
||||
|
||||
return addresses;
|
||||
@ -545,20 +538,13 @@ Wallet.prototype._addressIndex = function _addressIndex(address) {
|
||||
if (!(address instanceof bcoin.address))
|
||||
address = bcoin.address(address);
|
||||
|
||||
if (address.type === 'scripthash') {
|
||||
addr = address.getScriptAddress();
|
||||
if (this._addressTable[addr] != null)
|
||||
return this._addressTable[addr];
|
||||
}
|
||||
|
||||
addr = address.getKeyAddress();
|
||||
addr = address.getAddress();
|
||||
if (this._addressTable[addr] != null)
|
||||
return this._addressTable[addr];
|
||||
|
||||
return -1;
|
||||
};
|
||||
|
||||
// TODO: fromPath here
|
||||
Wallet.prototype.createAddress = function createAddress(change, index) {
|
||||
var self = this;
|
||||
var key = this.createKey(change, index);
|
||||
@ -573,7 +559,6 @@ Wallet.prototype.createAddress = function createAddress(change, index) {
|
||||
index: key.index,
|
||||
path: key.path,
|
||||
type: this.type,
|
||||
subtype: this.subtype,
|
||||
m: this.m,
|
||||
n: this.n,
|
||||
keys: [],
|
||||
@ -610,7 +595,7 @@ Wallet.prototype.createAddress = function createAddress(change, index) {
|
||||
this.keys = utils.sortKeys(options.keys);
|
||||
} else if (this.derivation === 'normal') {
|
||||
this.keys.forEach(function(key, i) {
|
||||
if (i !== this.cosignerIndex)
|
||||
if (i !== this._cosignerIndex)
|
||||
options.keys.push(key);
|
||||
}, this);
|
||||
options.keys.push(key.publicKey);
|
||||
@ -658,10 +643,7 @@ Wallet.prototype.addAddress = function addAddress(address) {
|
||||
self.emit('add address', address);
|
||||
});
|
||||
|
||||
if (address.type === 'scripthash')
|
||||
this._addressTable[address.getScriptAddress()] = index;
|
||||
|
||||
this._addressTable[address.getKeyAddress()] = index;
|
||||
this._addressTable[address.getAddress()] = index;
|
||||
|
||||
if (address.label && this._labelTable[address.label] == null)
|
||||
this._labelTable[address.label] = index;
|
||||
@ -832,30 +814,19 @@ Wallet.prototype.ownOutput = function ownOutput(tx, index) {
|
||||
return tx.testOutputs(this._addressTable, index, true);
|
||||
};
|
||||
|
||||
Wallet.prototype.fill = function fill(tx, address, fee) {
|
||||
var unspent, items, result;
|
||||
Wallet.prototype.fill = function fill(tx, options) {
|
||||
var address, unspent;
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
assert(this._initialized);
|
||||
|
||||
if (!address)
|
||||
address = this.changeAddress.getKeyAddress();
|
||||
address = this.changeAddress.getAddress();
|
||||
|
||||
unspent = this.getUnspent();
|
||||
|
||||
items = unspent.filter(function(coin) {
|
||||
if (bcoin.script.isScripthash(coin.script))
|
||||
return this.type === 'scripthash';
|
||||
|
||||
if (bcoin.script.isMultisig(coin.script))
|
||||
return this.type === 'multisig';
|
||||
|
||||
return true;
|
||||
}, this);
|
||||
|
||||
if (tx.getInputs(unspent, address, fee).inputs)
|
||||
unspent = items;
|
||||
|
||||
result = tx.fill(unspent, address, fee);
|
||||
result = tx.fill(unspent, address, options.fee);
|
||||
|
||||
if (!result.inputs)
|
||||
return false;
|
||||
@ -1040,14 +1011,13 @@ Wallet.prototype.toJSON = function toJSON(encrypt) {
|
||||
name: 'wallet',
|
||||
network: network.type,
|
||||
type: this.type,
|
||||
subtype: this.subtype,
|
||||
m: this.m,
|
||||
n: this.n,
|
||||
derivation: this.derivation,
|
||||
copayBIP45: this.copayBIP45,
|
||||
accountIndex: this.accountIndex,
|
||||
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;
|
||||
@ -1077,14 +1047,13 @@ Wallet.fromJSON = function fromJSON(json, decrypt) {
|
||||
|
||||
wallet = new Wallet({
|
||||
type: json.type,
|
||||
subtype: json.subtype,
|
||||
m: json.m,
|
||||
n: json.n,
|
||||
derivation: json.derivation,
|
||||
copayBIP45: json.copayBIP45,
|
||||
accountIndex: json.accountIndex,
|
||||
addressDepth: json.addressDepth,
|
||||
changeDepth: json.changeDepth,
|
||||
cosignerIndex: json.cosignerIndex,
|
||||
master: json.master
|
||||
? bcoin.hd.fromJSON(json.master, decrypt)
|
||||
: null,
|
||||
@ -1109,19 +1078,19 @@ Wallet.fromSecret = function fromSecret(privateKey) {
|
||||
};
|
||||
|
||||
Wallet.key2hash = function key2hash(key) {
|
||||
return bcoin.address.key2hash(key);
|
||||
return bcoin.address.hash160(key);
|
||||
};
|
||||
|
||||
Wallet.hash2addr = function hash2addr(hash, prefix) {
|
||||
return bcoin.address.hash2addr(hash, prefix);
|
||||
return bcoin.address.toAddress(hash, prefix);
|
||||
};
|
||||
|
||||
Wallet.addr2hash = function addr2hash(addr, prefix) {
|
||||
return bcoin.address.addr2hash(addr, prefix);
|
||||
return bcoin.address.toHash(addr, prefix);
|
||||
};
|
||||
|
||||
Wallet.validateAddress = function validateAddress(addr, prefix) {
|
||||
return bcoin.address.validateAddress(addr, prefix);
|
||||
return bcoin.address.validate(addr, prefix);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -81,7 +81,7 @@ describe('Wallet', function() {
|
||||
// var k2 = w.getPublicKey().concat(1);
|
||||
var k2 = bcoin.ecdsa.genKeyPair().getPublic(true, 'array');
|
||||
w.addKey(k2);
|
||||
assert.equal(w.getKeyAddress(), w.getAddress());
|
||||
// assert.equal(w.getKeyAddress(), w.getAddress());
|
||||
|
||||
// Input transcation
|
||||
var src = bcoin.tx({
|
||||
@ -222,7 +222,7 @@ describe('Wallet', function() {
|
||||
tx.out(to, 5460);
|
||||
|
||||
var cost = tx.funds('out');
|
||||
var total = cost.add(new bn(constants.tx.fee));
|
||||
var total = cost.add(new bn(constants.tx.minFee));
|
||||
|
||||
var unspent1 = w1.unspent();
|
||||
var unspent2 = w2.unspent();
|
||||
@ -236,7 +236,7 @@ describe('Wallet', function() {
|
||||
tx.input(unspent2[0]);
|
||||
|
||||
var left = tx.funds('in').sub(total);
|
||||
if (left.cmpn(constants.tx.dust) < 0) {
|
||||
if (left.cmpn(constants.tx.dustThreshold) < 0) {
|
||||
tx.outputs[tx.outputs.length - 2].value.iadd(left);
|
||||
left = new bn(0);
|
||||
}
|
||||
@ -270,37 +270,35 @@ describe('Wallet', function() {
|
||||
// Create 3 2-of-3 wallets with our pubkeys as "shared keys"
|
||||
var w1 = bcoin.wallet({
|
||||
derivation: 'bip44',
|
||||
type: 'scripthash',
|
||||
subtype: 'multisig',
|
||||
type: 'multisig',
|
||||
m: 2,
|
||||
n: 3
|
||||
});
|
||||
|
||||
var w2 = bcoin.wallet({
|
||||
derivation: 'bip44',
|
||||
type: 'scripthash',
|
||||
subtype: 'multisig',
|
||||
type: 'multisig',
|
||||
m: 2,
|
||||
n: 3
|
||||
});
|
||||
|
||||
var w3 = bcoin.wallet({
|
||||
derivation: 'bip44',
|
||||
type: 'scripthash',
|
||||
subtype: 'multisig',
|
||||
type: 'multisig',
|
||||
m: 2,
|
||||
n: 3
|
||||
});
|
||||
// w3 = bcoin.wallet.fromJSON(w3.toJSON());
|
||||
|
||||
var receive = bcoin.wallet();
|
||||
|
||||
w1.addKey(w2.purposeKey);
|
||||
w1.addKey(w3.purposeKey);
|
||||
w2.addKey(w1.purposeKey);
|
||||
w2.addKey(w3.purposeKey);
|
||||
w3.addKey(w1.purposeKey);
|
||||
w3.addKey(w2.purposeKey);
|
||||
w1.addKey(w2);
|
||||
w1.addKey(w3);
|
||||
w2.addKey(w1);
|
||||
w2.addKey(w3);
|
||||
w3.addKey(w1);
|
||||
w3.addKey(w2);
|
||||
|
||||
w3 = bcoin.wallet.fromJSON(w3.toJSON());
|
||||
|
||||
// Our p2sh address
|
||||
var addr = w1.getAddress();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user