diff --git a/lib/bcoin/address.js b/lib/bcoin/address.js index ae122235..4a70dcc5 100644 --- a/lib/bcoin/address.js +++ b/lib/bcoin/address.js @@ -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, diff --git a/lib/bcoin/chain.js b/lib/bcoin/chain.js index 57b4fd57..0f86286a 100644 --- a/lib/bcoin/chain.js +++ b/lib/bcoin/chain.js @@ -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() { diff --git a/lib/bcoin/chainblock.js b/lib/bcoin/chainblock.js index 51fdfb53..a56ed23a 100644 --- a/lib/bcoin/chainblock.js +++ b/lib/bcoin/chainblock.js @@ -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() { diff --git a/lib/bcoin/chaindb.js b/lib/bcoin/chaindb.js index 04a36a23..3b048621 100644 --- a/lib/bcoin/chaindb.js +++ b/lib/bcoin/chaindb.js @@ -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) diff --git a/lib/bcoin/hd.js b/lib/bcoin/hd.js index 71c1ede9..fe21186d 100644 --- a/lib/bcoin/hd.js +++ b/lib/bcoin/hd.js @@ -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 diff --git a/lib/bcoin/input.js b/lib/bcoin/input.js index 283847b6..599808f8 100644 --- a/lib/bcoin/input.js +++ b/lib/bcoin/input.js @@ -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; diff --git a/lib/bcoin/output.js b/lib/bcoin/output.js index d58cf134..60d2bfb0 100644 --- a/lib/bcoin/output.js +++ b/lib/bcoin/output.js @@ -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; diff --git a/lib/bcoin/protocol/constants.js b/lib/bcoin/protocol/constants.js index d586a462..edf98fbd 100644 --- a/lib/bcoin/protocol/constants.js +++ b/lib/bcoin/protocol/constants.js @@ -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, diff --git a/lib/bcoin/protocol/network.js b/lib/bcoin/protocol/network.js index 995cee2b..a88b107d 100644 --- a/lib/bcoin/protocol/network.js +++ b/lib/bcoin/protocol/network.js @@ -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, diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index 7d1ddf52..12a5f9cb 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -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); }; diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index 62acadf3..df3d9f26 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -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; } diff --git a/lib/bcoin/utils.js b/lib/bcoin/utils.js index bd0d0430..df6b369d 100644 --- a/lib/bcoin/utils.js +++ b/lib/bcoin/utils.js @@ -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; diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index fad140d5..3fecf65d 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -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); }; /** diff --git a/test/wallet-test.js b/test/wallet-test.js index 52b05485..74fb1674 100644 --- a/test/wallet-test.js +++ b/test/wallet-test.js @@ -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();