wallet work. consistency.

This commit is contained in:
Christopher Jeffrey 2016-02-02 16:09:48 -08:00
parent f3fd85354e
commit 2deccde29e
10 changed files with 491 additions and 278 deletions

View File

@ -87,8 +87,8 @@ function Address(options) {
this.addKey(key);
}, this);
if (options.redeem)
this.setRedeem(options.redeem);
if (options.redeem || options.script)
this.setRedeem(options.redeem || options.script);
this.prefix = 'bt/address/' + this.getKeyAddress() + '/';
}
@ -104,7 +104,7 @@ Address.prototype.setRedeem = function setRedeem(redeem) {
this.type = 'scripthash';
this.subtype = null;
this.redeem = redeem;
this.emit('scriptaddress', old, this.getScriptAddress());
this.emit('update script', old, this.getScriptAddress());
};
Address.prototype.addKey = function addKey(key) {
@ -124,10 +124,15 @@ Address.prototype.addKey = function addKey(key) {
this.keys = utils.sortKeys(this.keys);
delete this._scriptAddress;
delete this._scriptHash;
delete this._script;
this.getScriptAddress();
cur = this.getScriptAddress();
if (old !== cur)
this.emit('scriptaddress', old, cur);
this.emit('update script', old, cur);
};
Address.prototype.removeKey = function removeKey(key) {
@ -149,10 +154,15 @@ Address.prototype.removeKey = function removeKey(key) {
this.keys = utils.sortKeys(this.keys);
delete this._scriptAddress;
delete this._scriptHash;
delete this._script;
this.getScriptAddress();
cur = this.getScriptAddress();
if (old !== cur)
this.emit('scriptaddress', old, this.getScriptAddress());
this.emit('update script', old, cur);
};
Address.prototype.getPrivateKey = function getPrivateKey(enc) {
@ -171,51 +181,82 @@ Address.prototype.getScript = function getScript() {
if (this.type !== 'scripthash')
return;
if (this._script)
return this._script;
if (this.redeem)
return this.redeem.slice();
return this._script = this.redeem.slice();
if (this.subtype === 'pubkey')
return bcoin.script.encode([this.getPublicKey(), 'checksig']);
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 (this.subtype === 'pubkeyhash' || this.keys.length < this.n) {
return bcoin.script.encode([
'dup',
'hash160',
this.getKeyHash(),
'equalverify',
'checksig'
]);
}
this._script = bcoin.script.encode(this._script);
return bcoin.script.encode(
bcoin.script.createMultisig(this.keys, this.m, this.n)
);
return this._script;
};
Address.prototype.getScriptHash = function getScriptHash() {
if (this.type !== 'scripthash')
return;
return utils.ripesha(this.getScript());
if (this._scriptHash)
return this._scriptHash;
this._scriptHash = utils.ripesha(this.getScript());
return this._scriptHash;
};
Address.prototype.getScriptAddress = function getScriptAddress() {
if (this.type !== 'scripthash')
return;
return Address.hash2addr(this.getScriptHash(), this.type);
if (this._scriptAddress)
return this._scriptAddress;
this._scriptAddress = Address.hash2addr(this.getScriptHash(), this.type);
return this._scriptAddress;
};
Address.prototype.getPublicKey = function getPublicKey(enc) {
if (!this.key.priv)
return;
if (!enc) {
if (this._pub)
return this._pub;
this._pub = this.key.getPublic();
return this._pub;
}
return this.key.getPublic(enc);
};
Address.prototype.getKeyHash = function getKeyHash() {
return Address.key2hash(this.getPublicKey());
if (this._hash)
return this._hash;
this._hash = Address.key2hash(this.getPublicKey());
return this._hash;
};
Address.prototype.getKeyAddress = function getKeyAddress() {
return Address.hash2addr(this.getKeyHash(), 'pubkeyhash');
if (this._address)
return this._address;
this._address = Address.hash2addr(this.getKeyHash(), 'pubkeyhash');
return this._address;
};
Address.prototype.getHash = function getHash() {
@ -248,6 +289,10 @@ Address.hash2addr = function hash2addr(hash, prefix) {
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;
@ -286,7 +331,7 @@ Address.addr2hash = function addr2hash(addr, prefix) {
return addr.slice(1, -4);
};
Address.validate = function validateAddress(addr, prefix) {
Address.validate = function validate(addr, prefix) {
if (!addr || typeof addr !== 'string')
return false;
@ -298,12 +343,13 @@ Address.validate = function validateAddress(addr, prefix) {
Address.validateAddress = Address.validate;
Address.prototype.ownOutput = function ownOutput(tx, index) {
var scripthash = this.getScriptHash();
var scriptHash = this.getScriptHash();
var hash = this.getKeyHash();
var key = this.getPublicKey();
var keys = this.keys;
var outputs;
var outputs = tx.outputs.filter(function(output, i) {
outputs = tx.outputs.filter(function(output, i) {
var s = output.script;
if (index != null && index !== i)
@ -318,8 +364,8 @@ Address.prototype.ownOutput = function ownOutput(tx, index) {
if (bcoin.script.isMultisig(s, keys))
return true;
if (scripthash) {
if (bcoin.script.isScripthash(s, scripthash))
if (scriptHash) {
if (bcoin.script.isScripthash(s, scriptHash))
return true;
}
@ -333,13 +379,14 @@ Address.prototype.ownOutput = function ownOutput(tx, index) {
};
Address.prototype.ownInput = function ownInput(tx, index) {
var scripthash = this.getScriptHash();
var scriptHash = this.getScriptHash();
var hash = this.getKeyHash();
var key = this.getPublicKey();
var redeem = this.getScript();
var keys = this.keys;
var inputs;
var inputs = tx.inputs.filter(function(input, i) {
inputs = tx.inputs.filter(function(input, i) {
var s;
if (!input.prevout.tx && this.tx._all[input.prevout.hash])
@ -376,8 +423,8 @@ Address.prototype.ownInput = function ownInput(tx, index) {
if (bcoin.script.isMultisig(s, keys))
return true;
if (scripthash) {
if (bcoin.script.isScripthash(s, scripthash))
if (scriptHash) {
if (bcoin.script.isScripthash(s, scriptHash))
return true;
}
@ -390,6 +437,108 @@ Address.prototype.ownInput = function ownInput(tx, index) {
return inputs;
};
Address.prototype.scriptInputs = function scriptInputs(tx) {
var self = this;
var pub = this.getPublicKey();
var redeem = this.getScript();
return tx.inputs.reduce(function(total, input, i) {
if (!input.prevout.tx)
return total;
if (!self.ownOutput(input.prevout.tx, input.prevout.index))
return total;
if (tx.scriptInput(i, pub, redeem))
total++;
return total;
}, 0);
};
Address.prototype.signInputs = function signInputs(tx, type) {
var self = this;
var key = this.key;
var total = 0;
if (!key.priv)
return 0;
return tx.inputs.reduce(function(total, input, i) {
if (!input.prevout.tx)
return total;
if (!self.ownOutput(input.prevout.tx, input.prevout.index))
return total;
if (tx.signInput(i, key, type))
total++;
return total;
}, 0);
};
Address.prototype.sign = function sign(tx, type) {
var self = this;
var pub = this.getPublicKey();
var redeem = this.getScript();
var key = this.key;
if (!key.priv)
return 0;
// Add signature script to each input
return tx.inputs.reduce(function(total, input, i) {
// Filter inputs that this wallet own
if (!input.prevout.tx)
return total;
if (!self.ownOutput(input.prevout.tx, input.prevout.index))
return total;
if (tx.scriptSig(i, key, pub, redeem, type))
total++;
return total;
}, 0);
};
Address.prototype.__defineGetter__('script', function() {
return this.getScript();
});
Address.prototype.__defineGetter__('scriptHash', function() {
return this.getScriptHash();
});
Address.prototype.__defineGetter__('scriptAddress', function() {
return this.getScriptAddress();
});
Address.prototype.__defineGetter__('privateKey', function() {
return this.getPrivateKey();
});
Address.prototype.__defineGetter__('publicKey', function() {
return this.getPublicKey();
});
Address.prototype.__defineGetter__('keyHash', function() {
return this.getKeyHash();
});
Address.prototype.__defineGetter__('keyAddress', function() {
return this.getKeyAddress();
});
Address.prototype.__defineGetter__('hash', function() {
return this.getHash();
});
Address.prototype.__defineGetter__('address', function() {
return this.getAddress();
});
Address.prototype.toJSON = function toJSON(encrypt) {
return {
v: 1,
@ -398,7 +547,7 @@ Address.prototype.toJSON = function toJSON(encrypt) {
label: this.label,
change: this.change,
address: this.getKeyAddress(),
scriptaddress: this.getScriptAddress(),
scriptAddress: this.getScriptAddress(),
key: this.key.toJSON(encrypt),
type: this.type,
subtype: this.subtype,

View File

@ -21,6 +21,8 @@ function Input(options) {
prevout = options.prevout || options.out;
this.tx = options.tx;
this.prevout = {
tx: prevout.tx || null,
hash: prevout.hash,
@ -66,8 +68,10 @@ Input.prototype.__defineGetter__('data', function() {
data = Input.getData(this);
if (this.script.length && this.prevout.tx)
utils.hidden(this, '_data', data);
if (!this.tx || this.tx.ps === 0) {
if (this.script.length && this.prevout.tx)
utils.hidden(this, '_data', data);
}
return data;
});
@ -89,11 +93,11 @@ Input.prototype.__defineGetter__('key', function() {
});
Input.prototype.__defineGetter__('hash', function() {
return this.data.scripthash || this.hashes[0];
return this.data.scriptHash || this.hashes[0];
});
Input.prototype.__defineGetter__('address', function() {
return this.data.scriptaddress || this.addresses[0] || this.getID();
return this.data.scriptAddress || this.addresses[0] || this.getID();
});
Input.prototype.__defineGetter__('signatures', function() {
@ -116,12 +120,12 @@ Input.prototype.__defineGetter__('redeem', function() {
return this.data.redeem;
});
Input.prototype.__defineGetter__('scripthash', function() {
return this.data.scripthash;
Input.prototype.__defineGetter__('scriptHash', function() {
return this.data.scriptHash;
});
Input.prototype.__defineGetter__('scriptaddress', function() {
return this.data.scriptaddress;
Input.prototype.__defineGetter__('scriptAddress', function() {
return this.data.scriptAddress;
});
Input.prototype.__defineGetter__('m', function() {
@ -132,10 +136,10 @@ Input.prototype.__defineGetter__('n', function() {
return this.data.n || this.m;
});
Input.prototype.__defineGetter__('lockTime', function() {
Input.prototype.__defineGetter__('locktime', function() {
if (!this.output)
return 0;
return this.output.lockTime;
return this.output.locktime;
});
Input.prototype.__defineGetter__('flags', function() {
@ -187,7 +191,7 @@ Input.prototype.__defineGetter__('sigs', function() {
});
Input.prototype.__defineGetter__('scriptaddr', function() {
return this.scriptaddress;
return this.scriptAddress;
});
// Schema and defaults for data object:
@ -200,14 +204,14 @@ Input.prototype.__defineGetter__('scriptaddr', function() {
// hashes: Array,
// addresses: Array,
// redeem: Array,
// scripthash: Array,
// scriptaddress: String,
// scriptHash: Array,
// scriptAddress: String,
// m: Number,
// n: Number,
// height: Number,
// flags: Array,
// text: String,
// lockTime: Number,
// locktime: Number,
// value: bn,
// script: Array,
// seq: Number,
@ -281,6 +285,49 @@ Input.prototype.getLocktime = function getLocktime() {
return bcoin.script.getLocktime(redeem);
};
Input.prototype.createScript = function createScript(pub, redeem) {
return this.tx.scriptInput(this, pub, redeem);
};
Input.prototype.signatureHash = function signatureHash(s, type) {
return this.tx.signatureHash(this, s, type);
};
Input.prototype.createSignature = function createSignature(key, type) {
return this.tx.createSignature(this, key, type);
};
Input.prototype.sign = function sign(key, type) {
return this.tx.signInput(this, key, type);
};
Input.prototype.scriptSig = function scriptSig(key, pub, redeem, type) {
return this.tx.scriptSig(this, key, pub, redeem, type);
};
Input.prototype.isSigned = function isSigned(required) {
return this.tx.isSigned(this, required);
};
Input.prototype.verify = function verify(force, flags) {
return this.tx.verify(this, force, flags);
};
Input.prototype.isCoinbase = function isCoinbase() {
return this.tx.isCoinbase();
};
Input.prototype.test = function test(addressTable, collect) {
return this.tx.testInputs(addressTable, this, collect);
};
Input.prototype.getSigops = function getSigops(scriptHash, accurate) {
var n = bcoin.script.getSigops(this.script, accurate);
if (scriptHash && !this.tx.isCoinbase())
n += bcoin.script.getScripthashSigops(this.script);
return n;
};
Input.prototype.inspect = function inspect() {
var output = this.output
? this.output.inspect()
@ -297,10 +344,10 @@ Input.prototype.inspect = function inspect() {
keys: this.keys.map(utils.toHex),
hashes: this.hashes.map(utils.toHex),
addresses: this.addresses,
scriptaddress: this.scriptaddress,
scriptAddress: this.scriptAddress,
signatures: this.signatures.map(utils.toHex),
text: this.text,
lockTime: this.lockTime,
locktime: this.locktime,
value: utils.btc(output.value),
script: bcoin.script.format(this.script)[0],
redeem: this.redeem ? bcoin.script.format(this.redeem)[0] : null,

View File

@ -25,6 +25,7 @@ function Output(options) {
if (typeof value === 'number' && (value | 0) === value)
value = new bn(value);
this.tx = options.tx;
this.value = utils.satoshi(value || new bn(0));
this.script = options.script ? options.script.slice() : [];
@ -47,8 +48,10 @@ Output.prototype.__defineGetter__('data', function() {
data = Output.getData(this);
if (this.script.length && this.value.cmpn(0) !== 0)
utils.hidden(this, '_data', data);
if (!this.tx || this.tx.ps === 0) {
if (this.script.length && this.value.cmpn(0) > 0)
utils.hidden(this, '_data', data);
}
return data;
});
@ -66,11 +69,11 @@ Output.prototype.__defineGetter__('key', function() {
});
Output.prototype.__defineGetter__('hash', function() {
return this.data.scripthash || this.hashes[0];
return this.data.scriptHash || this.hashes[0];
});
Output.prototype.__defineGetter__('address', function() {
return this.data.scriptaddress || this.addresses[0] || this.getID();
return this.data.scriptAddress || this.addresses[0] || this.getID();
});
Output.prototype.__defineGetter__('signatures', function() {
@ -89,12 +92,12 @@ Output.prototype.__defineGetter__('addresses', function() {
return this.data.addresses || [];
});
Output.prototype.__defineGetter__('scripthash', function() {
return this.data.scripthash;
Output.prototype.__defineGetter__('scriptHash', function() {
return this.data.scriptHash;
});
Output.prototype.__defineGetter__('scriptaddress', function() {
return this.data.scriptaddress;
Output.prototype.__defineGetter__('scriptAddress', function() {
return this.data.scriptAddress;
});
Output.prototype.__defineGetter__('m', function() {
@ -105,8 +108,8 @@ Output.prototype.__defineGetter__('n', function() {
return this.data.n || this.m;
});
Output.prototype.__defineGetter__('lockTime', function() {
return bcoin.script.getLockTime(this.script);
Output.prototype.__defineGetter__('locktime', function() {
return bcoin.script.getLocktime(this.script);
});
Output.prototype.__defineGetter__('flags', function() {
@ -117,6 +120,7 @@ Output.prototype.__defineGetter__('text', function() {
return this.data.text;
});
// Legacy
Output.prototype.__defineGetter__('addr', function() {
return this.address;
});
@ -142,7 +146,7 @@ Output.prototype.__defineGetter__('sigs', function() {
});
Output.prototype.__defineGetter__('scriptaddr', function() {
return this.scriptaddress;
return this.scriptAddress;
});
// Schema and defaults for data object:
@ -155,14 +159,14 @@ Output.prototype.__defineGetter__('scriptaddr', function() {
// hashes: Array,
// addresses: Array,
// redeem: Array,
// scripthash: Array,
// scriptaddress: String,
// scriptHash: Array,
// scriptAddress: String,
// m: Number,
// n: Number,
// height: Number,
// flags: Array,
// text: String,
// lockTime: Number,
// locktime: Number,
// value: bn,
// script: Array,
// seq: Number,
@ -192,6 +196,18 @@ Output.prototype.getID = function getID() {
return '[' + this.type + ':' + hash.slice(0, 7) + ']';
};
Output.prototype.createScript = function createScript(options) {
return this.tx.scriptOutput(this, options);
};
Output.prototype.test = function test(addressTable, collect) {
return this.tx.testOutputs(addressTable, this, collect);
};
Output.prototype.getSigops = function getSigops(accurate) {
return bcoin.script.getSigops(this.script, accurate);
};
Output.prototype.inspect = function inspect() {
return {
type: this.type,
@ -199,11 +215,11 @@ Output.prototype.inspect = function inspect() {
keys: this.keys.map(utils.toHex),
hashes: this.hashes.map(utils.toHex),
addresses: this.addresses,
scriptaddress: this.scriptaddress,
scriptAddress: this.scriptAddress,
m: this.m,
n: this.n,
text: this.text,
lockTime: this.lockTime,
locktime: this.locktime,
value: utils.btc(this.value),
script: bcoin.script.format(this.script)[0]
};

View File

@ -301,7 +301,7 @@ Framer.tx = function tx(tx) {
off += utils.writeIntv(p, s.length, off);
off += utils.copy(s, p, off, true);
}
off += utils.writeU32(p, tx.lockTime, off);
off += utils.writeU32(p, tx.locktime, off);
return p;
};

View File

@ -461,7 +461,7 @@ Parser.prototype.parseTX = function parseTX(p) {
version: utils.read32(p, 0),
inputs: txIn,
outputs: txOut,
lockTime: utils.readU32(p, off),
locktime: utils.readU32(p, off),
_off: off + 4,
_size: p.length
};

View File

@ -386,7 +386,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
var key, sig, type, subscript, hash;
var keys, i, j, m;
var succ;
var lockTime, threshold;
var locktime, threshold;
var evalScript;
stack.alt = stack.alt || [];
@ -934,28 +934,28 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
if (!tx || stack.length === 0)
return false;
lockTime = stack[stack.length - 1];
locktime = stack[stack.length - 1];
if (!Array.isArray(lockTime))
if (!Array.isArray(locktime))
return false;
if (lockTime.length > 6)
if (locktime.length > 6)
return false;
lockTime = script.num(lockTime, true);
locktime = script.num(locktime, true);
if (lockTime < 0)
if (locktime < 0)
return false;
threshold = constants.locktimeThreshold;
if (!(
(tx.lockTime < threshold && lockTime < threshold)
|| (tx.lockTime >= threshold && lockTime >= threshold)
(tx.locktime < threshold && locktime < threshold)
|| (tx.locktime >= threshold && locktime >= threshold)
)) {
return false;
}
if (lockTime > tx.lockTime)
if (locktime > tx.locktime)
return false;
if (!tx.inputs[index] || tx.inputs[index].seq === 0xffffffff)
@ -1158,6 +1158,20 @@ script.checkPush = function checkPush(op, value, flags) {
return true;
};
script.createPubkey = function createPubkey(key) {
return [key, 'checksig'];
};
script.createPubkeyhash = function createPubkeyhash(hash) {
return [
'dup',
'hash160',
hash,
'equalverify',
'checksig'
];
};
script.createMultisig = function createMultisig(keys, m, n) {
if (keys.length !== n)
throw new Error(n + ' keys are required to generate multisig script');
@ -1171,15 +1185,21 @@ script.createMultisig = function createMultisig(keys, m, n) {
);
};
script.createScripthash = function createScripthash(s) {
assert(Array.isArray(s));
script.createScripthash = function createScripthash(hash) {
return [
'hash160',
utils.ripesha(script.encode(s)),
hash,
'equal'
];
};
script.createNulldata = function createNulldata(flags) {
return [
'return',
flags
];
};
script.getRedeem = function getRedeem(s) {
if (!Array.isArray(s[s.length - 1]))
return;
@ -1247,7 +1267,7 @@ script.isEncoded = function isEncoded(s) {
return true;
};
script._lockTime = function _lockTime(s) {
script._locktime = function _locktime(s) {
var i;
if (s.length < 2)
@ -1259,22 +1279,22 @@ script._lockTime = function _lockTime(s) {
}
};
script.isLockTime = function isLockTime(s) {
return !!script._lockTime(s);
script.isLocktime = function isLocktime(s) {
return !!script._locktime(s);
};
script.getLockTime = function getLockTime(s) {
var lockTime = script._lockTime(s);
script.getLocktime = function getLocktime(s) {
var locktime = script._locktime(s);
if (!lockTime)
if (!locktime)
return;
lockTime = script.num(lockTime, true);
locktime = script.num(locktime, true);
if (lockTime < constants.locktimeThreshold)
return { type: 'height', value: lockTime };
if (locktime < constants.locktimeThreshold)
return { type: 'height', value: locktime };
return { type: 'time', value: lockTime };
return { type: 'time', value: locktime };
};
script.getInputData = function getData(s, prev) {
@ -1320,7 +1340,7 @@ script.getInputData = function getData(s, prev) {
};
script._getInputData = function _getInputData(s, type) {
var sig, key, hash, raw, redeem, lockTime, hash, address, input, output;
var sig, key, hash, raw, redeem, locktime, hash, address, input, output;
assert(typeof type === 'string');
@ -1363,7 +1383,7 @@ script._getInputData = function _getInputData(s, type) {
if (type === 'scripthash') {
raw = s[s.length - 1];
redeem = script.decode(raw);
lockTime = script.getLockTime(redeem);
locktime = script.getLocktime(redeem);
hash = bcoin.wallet.key2hash(raw);
address = bcoin.wallet.hash2addr(hash, 'scripthash');
output = script.getOutputData(script.getSubscript(redeem));
@ -1374,9 +1394,9 @@ script._getInputData = function _getInputData(s, type) {
side: 'input',
subtype: output.type,
redeem: redeem,
scripthash: hash,
scriptaddress: address,
lockTime: lockTime
scriptHash: hash,
scriptAddress: address,
locktime: locktime
});
}
@ -1433,8 +1453,8 @@ script.getOutputData = function getOutputData(s) {
return {
type: 'scripthash',
side: 'output',
scripthash: hash,
scriptaddress: bcoin.wallet.hash2addr(hash, 'scripthash')
scriptHash: hash,
scriptAddress: bcoin.wallet.hash2addr(hash, 'scripthash')
};
}

View File

@ -233,6 +233,19 @@ TXPool.prototype._removeTX = function _removeTX(tx, noWrite) {
});
};
TXPool.prototype.prune = function prune(pruneOrphans) {
var unspent = Object.keys(this._unspent).reduce(function(key) {
out[key.split('/')[0]] = true;
return out;
}, {});
Object.keys(this._all).forEach(function(key) {
if (!unspent[key])
delete this._all[key];
});
if (pruneOrphans)
this._orphans = {};
};
TXPool.prototype.getAll = function getAll() {
return Object.keys(this._all).map(function(key) {
return this._all[key];
@ -250,43 +263,6 @@ TXPool.prototype.getUnspent = function getUnspent() {
}, this);
};
TXPool.prototype.hasUnspent = function hasUnspent(hash, unspent) {
var has;
if (utils.isBuffer(hash) && hash.length && typeof hash[0] !== 'number') {
unspent = this.getUnspent();
has = hash.map(function(hash) {
var h = this.hasUnspent(hash, unspent);
if (!h)
return false;
return h[0];
}, this).filter(Boolean);
if (has.length !== hash.length)
return null;
return has;
}
if (utils.isBuffer(hash))
hash = utils.toHex(hash);
else if (hash.prevout)
hash = hash.prevout.hash;
else if (hash.tx)
hash = hash.tx.hash('hex');
else if (hash instanceof bcoin.tx)
hash = hash.hash('hex');
unspent = unspent || this.getUnspent();
has = unspent.filter(function(item) {
return item.tx.hash('hex') === hash;
});
if (!has.length)
return null;
return has;
};
TXPool.prototype.getPending = function getPending() {
return Object.keys(this._all).map(function(key) {
return this._all[key];

View File

@ -26,14 +26,14 @@ function TX(data, block) {
this.version = data.version || 1;
this.inputs = [];
this.outputs = [];
this.lockTime = data.lockTime || 0;
this.locktime = data.locktime || 0;
this.ts = data.ts || 0;
this.block = data.block || null;
this._hash = null;
// Legacy
if (data.lock != null)
this.lockTime = data.lock;
this.locktime = data.lock;
this._raw = data._raw || null;
this._size = data._size || 0;
@ -73,19 +73,23 @@ function TX(data, block) {
// ps = Pending Since
this.ps = this.ts === 0 ? utils.now() : 0;
// Discourage fee snipping a la bitcoind
// if (data.lockTime == null && data.lock == null)
// this.avoidFeeSnipping();
}
// Legacy
TX.prototype.__defineSetter__('lock', function(lockTime) {
return this.lockTime = lockTime;
TX.prototype.__defineSetter__('lock', function(locktime) {
return this.locktime = locktime;
});
TX.prototype.__defineGetter__('lock', function() {
return this.lockTime;
return this.locktime;
});
TX.prototype.__defineSetter__('lockTime', function(locktime) {
return this.locktime = locktime;
});
TX.prototype.__defineGetter__('lockTime', function() {
return this.locktime;
});
TX.prototype.clone = function clone() {
@ -530,10 +534,9 @@ TX.prototype.signInput = function signInput(index, key, type) {
TX.prototype.scriptSig = function scriptSig(index, key, pub, redeem, type) {
var input;
if (typeof index !== 'number')
if (index && typeof index === 'object')
index = this.inputs.indexOf(index);
// Get the input
input = this.inputs[index];
assert(input);
@ -551,6 +554,12 @@ TX.prototype.scriptSig = function scriptSig(index, key, pub, redeem, type) {
TX.prototype.isSigned = function isSigned(index, required) {
var i, input, s, len, m, j, total;
if (index && typeof index === 'object')
index = this.inputs.indexOf(index);
if (index != null)
assert(this.inputs[index]);
for (i = 0; i < this.inputs.length; i++) {
input = this.inputs[i];
@ -716,25 +725,19 @@ TX.prototype.scriptOutput = function scriptOutput(index, options) {
return;
script = bcoin.script.createMultisig(keys, m, n);
} else if (bcoin.wallet.validateAddress(options.address, 'scripthash')) {
} else if (bcoin.address.validate(options.address, 'scripthash')) {
// P2SH Transaction
// https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki
// hash160 [20-byte-redeemscript-hash] equal
script = [
'hash160',
bcoin.wallet.addr2hash(options.address, 'scripthash'),
'equal'
];
script = bcoin.script.createScripthash(
bcoin.wallet.addr2hash(options.address, 'scripthash')
);
} else if (options.address) {
// P2PKH Transaction
// dup hash160 [pubkey-hash] equalverify checksig
script = [
'dup',
'hash160',
bcoin.wallet.addr2hash(options.address, 'pubkeyhash'),
'equalverify',
'checksig'
];
script = bcoin.script.createPubkeyhash(
bcoin.wallet.addr2hash(options.address, 'pubkeyhash')
);
} else if (options.key) {
// P2PK Transaction
// [pubkey] checksig
@ -750,36 +753,37 @@ TX.prototype.scriptOutput = function scriptOutput(index, options) {
flags = utils.ascii2array(flags);
assert(utils.isBuffer(flags));
assert(flags.length <= constants.script.maxOpReturn);
script = [
'return',
flags
];
script = bcoin.script.createNulldata(flags);
}
// P2SH Transaction
// hash160 [hash] eq
if (options.scripthash) {
if (options.lockTime != null) {
if (options.locktime != null) {
script = [
bcoin.script.array(options.lockTime),
bcoin.script.array(options.locktime),
'checklocktimeverify',
'drop',
'codeseparator'
].concat(script);
}
hash = utils.ripesha(bcoin.script.encode(script));
script = [
'hash160',
hash,
'equal'
];
script = bcoin.script.createScripthash(hash);
}
output.script = script;
};
TX.prototype.getSubscript = function getSubscript(index) {
var script = this.outputs[index].script;
var script;
if (typeof index !== 'number')
index = this.outputs.indexOf(index);
assert(this.outputs[index]);
script = this.outputs[index].script;
return bcoin.script.getSubscript(script);
};
@ -877,8 +881,10 @@ TX.prototype.tbsHash = function tbsHash(enc, force) {
return this.hash(enc);
if (!this._tbsHash || force) {
for (i = 0; i < copy.inputs.length; i++)
copy.inputs[i].script = [];
for (i = 0; i < copy.inputs.length; i++) {
if (!copy.isCoinbase())
copy.inputs[i].script = [];
}
this._tbsHash = utils.dsha256(copy.render(true));
}
@ -896,10 +902,16 @@ TX.prototype.verify = function verify(index, force, flags) {
if (this.inputs.length === 0)
return false;
if (index && typeof index === 'object')
index = this.inputs.indexOf(index);
if (index != null)
assert(this.inputs[index]);
return this.inputs.every(function(input, i) {
var output;
if (index != null && index !== i)
if (index != null && i !== index)
return true;
if (!input.prevout.tx)
@ -1248,24 +1260,24 @@ TX.prototype.funds = TX.prototype.getFunds;
TX.prototype.getTargetTime = function getTargetTime() {
var bestValue = 0;
var i, lockTime, bestType;
var i, locktime, bestType;
for (i = 0; i < this.inputs.length; i++) {
lockTime = this.inputs[i].getLocktime();
locktime = this.inputs[i].getLocktime();
if (!lockTime)
if (!locktime)
continue;
// Incompatible types
if (bestType && bestType !== lockTime.type)
if (bestType && bestType !== locktime.type)
return;
bestType = lockTime.type;
bestType = locktime.type;
if (lockTime.value < bestValue)
if (locktime.value < bestValue)
continue;
bestValue = lockTime.value;
bestValue = locktime.value;
}
return {
@ -1288,6 +1300,12 @@ TX.prototype.testInputs = function testInputs(addressTable, index, collect) {
}, {});
}
if (index && typeof index === 'object')
index = this.inputs.indexOf(index);
if (index != null)
assert(this.inputs[index]);
for (i = 0; i < this.inputs.length; i++) {
if (index != null && i !== index)
continue;
@ -1311,8 +1329,8 @@ TX.prototype.testInputs = function testInputs(addressTable, index, collect) {
}
}
if (data.scriptaddress) {
if (addressTable[data.scriptaddress] != null) {
if (data.scriptAddress) {
if (addressTable[data.scriptAddress] != null) {
if (!collect)
return true;
inputs.push(input);
@ -1343,6 +1361,12 @@ TX.prototype.testOutputs = function testOutputs(addressTable, index, collect) {
}, {});
}
if (index && typeof index === 'object')
index = this.outputs.indexOf(index);
if (index != null)
assert(this.outputs[index]);
for (i = 0; i < this.outputs.length; i++) {
if (index != null && i !== index)
continue;
@ -1361,8 +1385,8 @@ TX.prototype.testOutputs = function testOutputs(addressTable, index, collect) {
}
}
if (data.scriptaddress) {
if (addressTable[data.scriptaddress] != null) {
if (data.scriptAddress) {
if (addressTable[data.scriptAddress] != null) {
if (!collect)
return true;
outputs.push(output);
@ -1383,16 +1407,16 @@ TX.prototype.avoidFeeSnipping = function avoidFeeSnipping() {
if (!this.chain)
return;
this.lockTime = this.chain.height();
this.locktime = this.chain.height();
if ((Math.random() * 10 | 0) === 0)
this.lockTime = Math.max(0, this.lockTime - (Math.random() * 100 | 0));
this.locktime = Math.max(0, this.locktime - (Math.random() * 100 | 0));
};
TX.prototype.setLockTime = function setLockTime(lockTime) {
TX.prototype.setLocktime = function setLocktime(locktime) {
var i, input;
this.lockTime = lockTime;
this.locktime = locktime;
for (i = 0; i < this.inputs.length; i++) {
input = this.inputs[i];
@ -1489,10 +1513,10 @@ TX.prototype.isFinal = function isFinal(height, ts) {
if (!this.chain)
return true;
if (this.lockTime === 0)
if (this.locktime === 0)
return true;
if (this.lockTime < (this.lockTime < threshold ? height : ts))
if (this.locktime < (this.locktime < threshold ? height : ts))
return true;
for (i = 0; i < this.inputs.length; i++) {
@ -1503,11 +1527,11 @@ TX.prototype.isFinal = function isFinal(height, ts) {
return true;
};
TX.prototype.getSigops = function getSigops(scripthash, accurate) {
TX.prototype.getSigops = function getSigops(scriptHash, accurate) {
var n = 0;
this.inputs.forEach(function(input) {
n += bcoin.script.getSigops(input.script, accurate);
if (scripthash && !this.isCoinbase())
if (scriptHash && !this.isCoinbase())
n += bcoin.script.getScripthashSigops(input.script);
}, this);
this.outputs.forEach(function(output) {

View File

@ -133,7 +133,7 @@ Wallet.prototype._init = function init() {
});
};
Wallet.prototype.__defineGetter__('address', function() {
Wallet.prototype.__defineGetter__('primary', function() {
return this.addresses[0];
});
@ -233,7 +233,7 @@ Wallet.prototype.addAddress = function addAddress(address) {
index = this.addresses.push(address) - 1;
address.on('scriptaddress', address._onUpdate = function(old, cur) {
address.on('update script', address._onUpdate = function(old, cur) {
self._addressTable[cur] = self._addressTable[old];
delete self._addressTable[old];
self.emit('add address', address);
@ -267,7 +267,7 @@ Wallet.prototype.removeAddress = function removeAddress(address) {
this.addresses.splice(i, 1);
address.removeListener('scriptaddress', address._onUpdate);
address.removeListener('update script', address._onUpdate);
this._addressTable = this._getAddressTable();
@ -283,47 +283,51 @@ Wallet.prototype.removeAddress = function removeAddress(address) {
};
Wallet.prototype.addKey = function addKey(key, i) {
return this.address.addKey(key);
return this.primary.addKey(key);
};
Wallet.prototype.removeKey = function removeKey(key) {
return this.address.removeKey(key);
return this.primary.removeKey(key);
};
Wallet.prototype.getPrivateKey = function getPrivateKey(enc) {
return this.address.getPrivateKey(enc);
return this.primary.getPrivateKey(enc);
};
Wallet.prototype.getScript = function getScript() {
return this.address.getScript();
return this.primary.getScript();
};
Wallet.prototype.getScriptHash = function getScriptHash() {
return this.address.getScriptHash();
Wallet.prototype.getScriptHash =
Wallet.prototype.getScripthash = function getScripthash() {
return this.primary.getScriptHash();
};
Wallet.prototype.getScriptAddress = function getScriptAddress() {
return this.address.getScriptAddress();
Wallet.prototype.getScriptAddress =
Wallet.prototype.getScriptaddress = function getScriptaddress() {
return this.primary.getScriptAddress();
};
Wallet.prototype.getPublicKey = function getPublicKey(enc) {
return this.address.getPublicKey(enc);
return this.primary.getPublicKey(enc);
};
Wallet.prototype.getKeyHash = function getKeyHash() {
return this.address.getKeyHash();
Wallet.prototype.getKeyHash =
Wallet.prototype.getKeyhash = function getKeyhash() {
return this.primary.getKeyHash();
};
Wallet.prototype.getKeyAddress = function getKeyAddress() {
return this.address.getKeyAddress();
Wallet.prototype.getKeyAddress =
Wallet.prototype.getKeyaddress = function getKeyaddress() {
return this.primary.getKeyAddress();
};
Wallet.prototype.getHash = function getHash() {
return this.address.getHash();
return this.primary.getHash();
};
Wallet.prototype.getAddress = function getAddress() {
return this.address.getAddress();
return this.primary.getAddress();
};
Wallet.prototype.ownInput = function ownInput(tx, index) {
@ -347,12 +351,12 @@ Wallet.prototype.fill = function fill(tx, address, fee) {
items = unspent.filter(function(item) {
var output = item.tx.outputs[item.index];
if (bcoin.script.isScripthash(output.script)) {
if (this.address.type === 'scripthash')
if (this.primary.type === 'scripthash')
return true;
return false;
}
if (bcoin.script.isMultisig(output.script)) {
if (this.address.n > 1)
if (this.primary.n > 1)
return true;
return false;
}
@ -405,7 +409,7 @@ Wallet.prototype.createTX = function createTX(outputs, fee) {
return;
if (target.value > 0)
tx.setLockTime(target.value);
tx.setLocktime(target.value);
else
tx.avoidFeeSnipping();
@ -415,83 +419,23 @@ Wallet.prototype.createTX = function createTX(outputs, fee) {
};
Wallet.prototype.scriptInputs = function scriptInputs(tx) {
var self = this;
this.fillPrevout(tx);
return this.addresses.reduce(function(total, address) {
var pub = address.getPublicKey();
var redeem = address.getScript();
tx.inputs.forEach(function(input, i) {
if (!input.prevout.tx && self.tx._all[input.prevout.hash])
input.prevout.tx = self.tx._all[input.prevout.hash];
if (!input.prevout.tx)
return;
if (!address.ownOutput(input.prevout.tx, input.prevout.index))
return;
if (tx.scriptInput(i, pub, redeem))
total++;
});
return total;
return total + address.signInputs(tx);
}, 0);
};
Wallet.prototype.signInputs = function signInputs(tx, type) {
var self = this;
this.fillPrevout(tx);
return this.addresses.reduce(function(total, address) {
if (!address.key.priv)
return total;
tx.inputs.forEach(function(input, i) {
if (!input.prevout.tx && self.tx._all[input.prevout.hash])
input.prevout.tx = self.tx._all[input.prevout.hash];
if (!input.prevout.tx)
return;
if (!address.ownOutput(input.prevout.tx, input.prevout.index))
return;
if (tx.signInput(i, address.key, type))
total++;
});
return total;
return total + address.signInputs(tx, type);
}, 0);
};
Wallet.prototype.sign = function sign(tx, type) {
var self = this;
this.fillPrevout(tx);
return this.addresses.reduce(function(total, address) {
var pub = address.getPublicKey();
var redeem = address.getScript();
var key = address.key;
if (!key.priv)
return total;
// Add signature script to each input
tx.inputs.forEach(function(input, i) {
if (!input.prevout.tx && self.tx._all[input.prevout.hash])
input.prevout.tx = self.tx._all[input.prevout.hash];
// Filter inputs that this wallet own
if (!input.prevout.tx)
return;
if (!address.ownOutput(input.prevout.tx, input.prevout.index))
return;
if (tx.scriptSig(i, key, pub, redeem, type))
total++;
});
return total;
return total + address.sign(tx, type);
}, 0);
};
@ -552,6 +496,42 @@ Wallet.prototype.toAddress = function toAddress() {
};
};
Wallet.prototype.__defineGetter__('script', function() {
return this.getScript();
});
Wallet.prototype.__defineGetter__('scriptHash', function() {
return this.getScriptHash();
});
Wallet.prototype.__defineGetter__('scriptAddress', function() {
return this.getScriptAddress();
});
Wallet.prototype.__defineGetter__('privateKey', function() {
return this.getPrivateKey();
});
Wallet.prototype.__defineGetter__('publicKey', function() {
return this.getPublicKey();
});
Wallet.prototype.__defineGetter__('keyHash', function() {
return this.getKeyHash();
});
Wallet.prototype.__defineGetter__('keyAddress', function() {
return this.getKeyAddress();
});
Wallet.prototype.__defineGetter__('hash', function() {
return this.getHash();
});
Wallet.prototype.__defineGetter__('address', function() {
return this.getAddress();
});
Wallet.prototype.toJSON = function toJSON(encrypt) {
return {
v: 3,

View File

@ -286,6 +286,7 @@ describe('Wallet', function() {
n: 3
}
});
w3 = bcoin.wallet.fromJSON(w3.toJSON());
var receive = bcoin.wallet();