transaction improvements.
This commit is contained in:
parent
0b7ca266ee
commit
eb1a3ea6d2
@ -7,6 +7,7 @@
|
|||||||
var bn = require('bn.js');
|
var bn = require('bn.js');
|
||||||
var bcoin = require('../bcoin');
|
var bcoin = require('../bcoin');
|
||||||
var utils = bcoin.utils;
|
var utils = bcoin.utils;
|
||||||
|
var constants = bcoin.protocol.constants;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Input
|
* Input
|
||||||
@ -180,6 +181,7 @@ Input.prototype.__defineGetter__('scriptaddr', function() {
|
|||||||
// Schema and defaults for data object:
|
// Schema and defaults for data object:
|
||||||
// {
|
// {
|
||||||
// type: null,
|
// type: null,
|
||||||
|
// subtype: null,
|
||||||
// side: 'input',
|
// side: 'input',
|
||||||
// signatures: [],
|
// signatures: [],
|
||||||
// keys: [],
|
// keys: [],
|
||||||
@ -263,7 +265,7 @@ Input.getData = function getData(input) {
|
|||||||
} else if (data.type === 'scripthash') {
|
} else if (data.type === 'scripthash') {
|
||||||
// We work backwards here: scripthash is one of the few cases
|
// We work backwards here: scripthash is one of the few cases
|
||||||
// where we get more data from the input than the output.
|
// where we get more data from the input than the output.
|
||||||
val = Input.getData({
|
val = bcoin.input.getData({
|
||||||
out: { hash: input.out.hash, index: input.out.index },
|
out: { hash: input.out.hash, index: input.out.index },
|
||||||
script: input.script,
|
script: input.script,
|
||||||
seq: input.seq
|
seq: input.seq
|
||||||
@ -313,19 +315,16 @@ Input.getData = function getData(input) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bcoin.script.isScripthashInput(s)) {
|
if (bcoin.script.isScripthashInput(s)) {
|
||||||
signature = sub.slice(1, -1);
|
|
||||||
redeem = sub[sub.length - 1];
|
redeem = sub[sub.length - 1];
|
||||||
hash = utils.ripesha(redeem);
|
hash = utils.ripesha(redeem);
|
||||||
address = bcoin.wallet.hash2addr(hash, 'scripthash');
|
address = bcoin.wallet.hash2addr(hash, 'scripthash');
|
||||||
redeem = bcoin.script.decode(redeem);
|
redeem = bcoin.script.decode(redeem);
|
||||||
data = bcoin.output.getData({
|
val = bcoin.input.getData(sub.slice(0, -1));
|
||||||
script: redeem,
|
data = bcoin.output.getData(redeem);
|
||||||
value: new bn(0)
|
return utils.merge(val, data, {
|
||||||
});
|
|
||||||
return utils.merge(data, {
|
|
||||||
type: 'scripthash',
|
type: 'scripthash',
|
||||||
|
subtype: data.type,
|
||||||
side: 'input',
|
side: 'input',
|
||||||
signatures: signature,
|
|
||||||
redeem: redeem,
|
redeem: redeem,
|
||||||
scripthash: hash,
|
scripthash: hash,
|
||||||
scriptaddress: address,
|
scriptaddress: address,
|
||||||
@ -351,6 +350,7 @@ Input.prototype.inspect = function inspect() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
type: this.type,
|
type: this.type,
|
||||||
|
subtype: this.data.subtype,
|
||||||
address: this.address,
|
address: this.address,
|
||||||
signatures: this.signatures.map(utils.toHex),
|
signatures: this.signatures.map(utils.toHex),
|
||||||
keys: this.keys.map(utils.toHex),
|
keys: this.keys.map(utils.toHex),
|
||||||
@ -358,6 +358,7 @@ Input.prototype.inspect = function inspect() {
|
|||||||
lock: this.lock,
|
lock: this.lock,
|
||||||
value: utils.btc(output.value),
|
value: utils.btc(output.value),
|
||||||
script: bcoin.script.format(this.script)[0],
|
script: bcoin.script.format(this.script)[0],
|
||||||
|
redeem: this.data.redeem ? bcoin.script.format(this.data.redeem)[0] : null,
|
||||||
seq: this.seq,
|
seq: this.seq,
|
||||||
output: output
|
output: output
|
||||||
};
|
};
|
||||||
|
|||||||
@ -147,6 +147,7 @@ Output.prototype.__defineGetter__('scriptaddr', function() {
|
|||||||
// Schema and defaults for data object:
|
// Schema and defaults for data object:
|
||||||
// {
|
// {
|
||||||
// type: null,
|
// type: null,
|
||||||
|
// subtype: null,
|
||||||
// side: 'output',
|
// side: 'output',
|
||||||
// signatures: [],
|
// signatures: [],
|
||||||
// keys: [],
|
// keys: [],
|
||||||
@ -215,14 +216,13 @@ Output.getData = function getData(output) {
|
|||||||
address = bcoin.wallet.hash2addr(hash);
|
address = bcoin.wallet.hash2addr(hash);
|
||||||
return utils.merge(def, {
|
return utils.merge(def, {
|
||||||
type: 'pubkeyhash',
|
type: 'pubkeyhash',
|
||||||
side: 'output',
|
|
||||||
hashes: [hash],
|
hashes: [hash],
|
||||||
addresses: [address]
|
addresses: [address]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
keys = bcoin.script.isMultisig(s);
|
if (bcoin.script.isMultisig(s)) {
|
||||||
if (keys) {
|
keys = sub.slice(1, -2);
|
||||||
hash = keys.map(function(key) {
|
hash = keys.map(function(key) {
|
||||||
return utils.ripesha(key);
|
return utils.ripesha(key);
|
||||||
});
|
});
|
||||||
@ -272,9 +272,6 @@ Output.prototype.inspect = function inspect() {
|
|||||||
keys: this.keys.map(utils.toHex),
|
keys: this.keys.map(utils.toHex),
|
||||||
hashes: this.hashes.map(utils.toHex),
|
hashes: this.hashes.map(utils.toHex),
|
||||||
addresses: this.addresses,
|
addresses: this.addresses,
|
||||||
redeem: this.type === 'scripthash'
|
|
||||||
? bcoin.script.format(this.data.redeem)[0]
|
|
||||||
: null,
|
|
||||||
m: this.m,
|
m: this.m,
|
||||||
n: this.n,
|
n: this.n,
|
||||||
text: this.text,
|
text: this.text,
|
||||||
|
|||||||
@ -47,7 +47,6 @@ Parser.prototype.feed = function feed(data) {
|
|||||||
|
|
||||||
while (this.pendingTotal >= this.waiting) {
|
while (this.pendingTotal >= this.waiting) {
|
||||||
// Concat chunks
|
// Concat chunks
|
||||||
// chunk = new Array(this.waiting);
|
|
||||||
chunk = new Buffer(this.waiting);
|
chunk = new Buffer(this.waiting);
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
@ -55,7 +54,6 @@ Parser.prototype.feed = function feed(data) {
|
|||||||
len = 0;
|
len = 0;
|
||||||
|
|
||||||
for (; off < chunk.length; i++) {
|
for (; off < chunk.length; i++) {
|
||||||
// len = utils.copy(this.pending[0], chunk, off);
|
|
||||||
len = this.pending[0].copy(chunk, off, 0, this.pending[0].length);
|
len = this.pending[0].copy(chunk, off, 0, this.pending[0].length);
|
||||||
if (len === this.pending[0].length)
|
if (len === this.pending[0].length)
|
||||||
this.pending.shift();
|
this.pending.shift();
|
||||||
|
|||||||
@ -150,6 +150,64 @@ script.encode = function encode(s) {
|
|||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
script.verify = function verify(input, output, tx, i, flags) {
|
||||||
|
var copy, res, redeem;
|
||||||
|
var stack = [];
|
||||||
|
|
||||||
|
if (!flags)
|
||||||
|
flags = {};
|
||||||
|
|
||||||
|
// Execute the input script
|
||||||
|
script.execute(input, stack, tx, i, flags);
|
||||||
|
|
||||||
|
// Copy the stack for P2SH
|
||||||
|
if (flags.verifyp2sh !== false)
|
||||||
|
copy = stack.slice();
|
||||||
|
|
||||||
|
// Execute the previous output script
|
||||||
|
res = script.execute(output, stack, tx, i, flags);
|
||||||
|
|
||||||
|
// Verify the script did not fail as well as the stack values
|
||||||
|
if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If the script is P2SH, execute the real output script
|
||||||
|
if (flags.verifyp2sh !== false && script.isScripthash(output)) {
|
||||||
|
// P2SH can only have push ops in the scriptSig
|
||||||
|
if (!script.pushOnly(input))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Reset the stack
|
||||||
|
stack = copy;
|
||||||
|
|
||||||
|
// Stack should _never_ be empty at this point
|
||||||
|
assert(stack.length !== 0);
|
||||||
|
|
||||||
|
// Grab the real redeem script
|
||||||
|
redeem = stack.pop();
|
||||||
|
|
||||||
|
if (!Array.isArray(redeem))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
redeem = script.decode(redeem);
|
||||||
|
|
||||||
|
// Execute the redeem script
|
||||||
|
res = script.execute(redeem, stack, tx, i, flags);
|
||||||
|
|
||||||
|
// Verify the script did not fail as well as the stack values
|
||||||
|
if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure there is nothing left on the stack
|
||||||
|
if (flags.cleanstack !== false) {
|
||||||
|
if (stack.length !== 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
script.subscript = function subscript(s, lastSep) {
|
script.subscript = function subscript(s, lastSep) {
|
||||||
var i, res;
|
var i, res;
|
||||||
|
|
||||||
@ -179,7 +237,7 @@ script.subscript = function subscript(s, lastSep) {
|
|||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
script.verify = function verify(hash, sig, pub) {
|
script.checksig = function checksig(hash, sig, pub) {
|
||||||
var k;
|
var k;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -249,7 +307,7 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) {
|
|||||||
var n, n1, n2, n3;
|
var n, n1, n2, n3;
|
||||||
var res;
|
var res;
|
||||||
var key, sig, type, subscript, hash;
|
var key, sig, type, subscript, hash;
|
||||||
var keys, i, j, key, m;
|
var keys, i, j, m;
|
||||||
var succ;
|
var succ;
|
||||||
var lock, threshold;
|
var lock, threshold;
|
||||||
var evalScript;
|
var evalScript;
|
||||||
@ -700,7 +758,7 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) {
|
|||||||
subscript = script.subscript(s, lastSep);
|
subscript = script.subscript(s, lastSep);
|
||||||
hash = tx.subscriptHash(index, subscript, type);
|
hash = tx.subscriptHash(index, subscript, type);
|
||||||
|
|
||||||
res = script.verify(hash, sig.slice(0, -1), key);
|
res = script.checksig(hash, sig.slice(0, -1), key);
|
||||||
if (o === 'checksigverify') {
|
if (o === 'checksigverify') {
|
||||||
if (!res)
|
if (!res)
|
||||||
return false;
|
return false;
|
||||||
@ -763,13 +821,19 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) {
|
|||||||
|
|
||||||
res = false;
|
res = false;
|
||||||
for (; !res && j < n; j++)
|
for (; !res && j < n; j++)
|
||||||
res = script.verify(hash, sig.slice(0, -1), keys[j]);
|
res = script.checksig(hash, sig.slice(0, -1), keys[j]);
|
||||||
|
|
||||||
if (res)
|
if (res)
|
||||||
succ++;
|
succ++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extra value
|
// Extra value
|
||||||
|
if (stack.length < 1)
|
||||||
|
return false;
|
||||||
|
if (flags.verifynulldummy !== false) {
|
||||||
|
if (stack[stack.length - 1].length > 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
stack.pop();
|
stack.pop();
|
||||||
|
|
||||||
// Too many signatures on stack
|
// Too many signatures on stack
|
||||||
@ -866,20 +930,6 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
script.exec = function exec(input, output, tx, i, flags) {
|
|
||||||
var stack = [];
|
|
||||||
var res;
|
|
||||||
|
|
||||||
script.execute(input, stack, tx, i, flags);
|
|
||||||
|
|
||||||
res = script.execute(output, stack, tx, i, flags);
|
|
||||||
|
|
||||||
if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
script.redeem = function redeem(keys, m, n) {
|
script.redeem = function redeem(keys, m, n) {
|
||||||
if (keys.length !== n)
|
if (keys.length !== n)
|
||||||
throw new Error(n + ' keys are required to generate redeem script');
|
throw new Error(n + ' keys are required to generate redeem script');
|
||||||
@ -892,9 +942,9 @@ script.redeem = function redeem(keys, m, n) {
|
|||||||
|
|
||||||
keys = utils.sortKeys(keys);
|
keys = utils.sortKeys(keys);
|
||||||
|
|
||||||
return [ m ].concat(
|
return [m].concat(
|
||||||
keys,
|
keys,
|
||||||
[ n, 'checkmultisig' ]
|
[n, 'checkmultisig']
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -908,11 +958,8 @@ script.standard = function standard(s) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
script.isStandard = function isStandard(s) {
|
script.isStandard = function isStandard(s) {
|
||||||
var m, n;
|
|
||||||
var type = script.standard(s);
|
var type = script.standard(s);
|
||||||
|
var m, n;
|
||||||
if (!type)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (type === 'multisig') {
|
if (type === 'multisig') {
|
||||||
m = new bn(s[0]).toNumber();
|
m = new bn(s[0]).toNumber();
|
||||||
@ -982,7 +1029,7 @@ script.isPubkey = function isPubkey(s, key) {
|
|||||||
if (key)
|
if (key)
|
||||||
return utils.isEqual(s[0], key);
|
return utils.isEqual(s[0], key);
|
||||||
|
|
||||||
return s[0];
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
script.isPubkeyhash = function isPubkeyhash(s, hash) {
|
script.isPubkeyhash = function isPubkeyhash(s, hash) {
|
||||||
@ -1008,11 +1055,11 @@ script.isPubkeyhash = function isPubkeyhash(s, hash) {
|
|||||||
if (hash)
|
if (hash)
|
||||||
return utils.isEqual(s[2], hash);
|
return utils.isEqual(s[2], hash);
|
||||||
|
|
||||||
return s[2];
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
script.isMultisig = function isMultisig(s, pubs) {
|
script.isMultisig = function isMultisig(s, pubs) {
|
||||||
var m, n, keys, isArray, total;
|
var m, n, keys, total;
|
||||||
|
|
||||||
s = script.subscript(s);
|
s = script.subscript(s);
|
||||||
|
|
||||||
@ -1048,15 +1095,11 @@ script.isMultisig = function isMultisig(s, pubs) {
|
|||||||
|
|
||||||
keys = s.slice(1, 1 + n);
|
keys = s.slice(1, 1 + n);
|
||||||
|
|
||||||
isArray = keys.every(function(k) {
|
if (!keys.every(Array.isArray))
|
||||||
return Array.isArray(k);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!isArray)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!pubs)
|
if (!pubs)
|
||||||
return keys;
|
return true;
|
||||||
|
|
||||||
total = keys.filter(function(k) {
|
total = keys.filter(function(k) {
|
||||||
return pubs.some(function(pub) {
|
return pubs.some(function(pub) {
|
||||||
@ -1133,7 +1176,7 @@ script.isPubkeyInput = function isPubkeyInput(s, key, tx, i) {
|
|||||||
// checksig script to see if this is our input.
|
// checksig script to see if this is our input.
|
||||||
// This will only work if the script verifies.
|
// This will only work if the script verifies.
|
||||||
if (key)
|
if (key)
|
||||||
return script.exec(s, [key, 'checksig'], tx, i);
|
return script.verify(s, [key, 'checksig'], tx, i);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
@ -1153,10 +1196,10 @@ script.isPubkeyhashInput = function isPubkeyhashInput(s, key) {
|
|||||||
if (key)
|
if (key)
|
||||||
return utils.isEqual(s[1], key);
|
return utils.isEqual(s[1], key);
|
||||||
|
|
||||||
return s[1];
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
script.isMultisigInput = function isMultisigInput(s, pubs, tx, i) {
|
script.isMultisigInput = function isMultisigInput(s, keys, tx, i) {
|
||||||
var i, res, o;
|
var i, res, o;
|
||||||
|
|
||||||
// We need to rule out scripthash
|
// We need to rule out scripthash
|
||||||
@ -1180,40 +1223,58 @@ script.isMultisigInput = function isMultisigInput(s, pubs, tx, i) {
|
|||||||
// Execute the script against our pubkeys'
|
// Execute the script against our pubkeys'
|
||||||
// redeem script to see if this is our input.
|
// redeem script to see if this is our input.
|
||||||
// This will only work if the script verifies.
|
// This will only work if the script verifies.
|
||||||
if (pubs && pubs.length >= 2) {
|
if (keys && keys.length >= 2) {
|
||||||
o = script.redeem(pubs, 2, pubs.length);
|
o = script.redeem(keys, s.length - 1, keys.length);
|
||||||
return script.exec(s, o, tx, i);
|
return script.verify(s, o, tx, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
script.isScripthashInput = function isScripthashInput(s, redeem) {
|
script.isScripthashInput = function isScripthashInput(s, data) {
|
||||||
var i, res, r, keys;
|
var raw, redeem;
|
||||||
|
|
||||||
s = script.subscript(s);
|
s = script.subscript(s);
|
||||||
|
|
||||||
if (s.length < 4)
|
// Grab the raw redeem script.
|
||||||
|
raw = s[s.length - 1];
|
||||||
|
|
||||||
|
// Need at least one data element with
|
||||||
|
// the redeem script.
|
||||||
|
if (s.length < 2)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!Array.isArray(s[0]) || s[0].length !== 0)
|
// Last data element should be an array
|
||||||
|
// for the redeem script.
|
||||||
|
if (!Array.isArray(raw))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (i = 1; i < s.length - 1; i++) {
|
// P2SH redeem scripts can be nonstandard: make
|
||||||
if (!script.isSig(s[i]))
|
// it easier for other functions to parse this.
|
||||||
return false;
|
redeem = script.decode(raw);
|
||||||
|
redeem = script.subscript(redeem);
|
||||||
|
if (script.lockTime(redeem))
|
||||||
|
redeem = redeem.slice(3);
|
||||||
|
|
||||||
|
// Get the "real" scriptSig
|
||||||
|
s = s.slice(0, -1);
|
||||||
|
|
||||||
|
// Do some sanity checking on the inputs
|
||||||
|
if (!script.isPubkeyInput(s)
|
||||||
|
&& !script.isPubkeyhashInput(s)
|
||||||
|
&& !script.isMultisigInput(s)) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = Array.isArray(s[s.length - 1]) && s[s.length - 1];
|
// Check data against last array in case
|
||||||
if (r[r.length - 1] !== constants.opcodes.checkmultisig)
|
// a raw redeem script was passed in.
|
||||||
return false;
|
if (data && utils.isEqual(data, raw))
|
||||||
|
return true;
|
||||||
|
|
||||||
if (redeem)
|
// Test against all other script types
|
||||||
return utils.isEqual(redeem, r);
|
return script.isPubkey(redeem, data)
|
||||||
|
|| script.isPubkeyhash(redeem, data)
|
||||||
keys = script.decode(r).slice(1, -2);
|
|| script.isMultisig(redeem, data);
|
||||||
|
|
||||||
return keys;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
script.coinbaseBits = function coinbaseBits(s, block) {
|
script.coinbaseBits = function coinbaseBits(s, block) {
|
||||||
@ -1526,7 +1587,7 @@ script.sigopsScripthash = function sigopsScripthash(s) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
script.args = function args(s) {
|
script.args = function args(s) {
|
||||||
var type, pubs, m;
|
var type, keys, m;
|
||||||
|
|
||||||
s = bcoin.script.subscript(s);
|
s = bcoin.script.subscript(s);
|
||||||
|
|
||||||
@ -1542,11 +1603,11 @@ script.args = function args(s) {
|
|||||||
return 2;
|
return 2;
|
||||||
|
|
||||||
if (type === 'multisig') {
|
if (type === 'multisig') {
|
||||||
pubs = bcoin.script.isMultisig(s);
|
keys = bcoin.script.isMultisig(s);
|
||||||
if (!pub)
|
if (!pub)
|
||||||
return -1;
|
return -1;
|
||||||
m = new bn(s[0]).toNumber();
|
m = new bn(s[0]).toNumber();
|
||||||
if (pubs.length < 1 || m < 1)
|
if (keys.length < 1 || m < 1)
|
||||||
return -1;
|
return -1;
|
||||||
return m + 1;
|
return m + 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -123,7 +123,6 @@ TXPool.prototype.add = function add(tx, noWrite) {
|
|||||||
// signature checking code to ownInput for p2sh and p2pk,
|
// signature checking code to ownInput for p2sh and p2pk,
|
||||||
// we could in theory use ownInput here (and down below)
|
// we could in theory use ownInput here (and down below)
|
||||||
// instead.
|
// instead.
|
||||||
// if (this._wallet.ownInput(input.out.tx, input.out.index))
|
|
||||||
if (input.out.tx) {
|
if (input.out.tx) {
|
||||||
if (!this._wallet.ownOutput(input.out.tx, input.out.index))
|
if (!this._wallet.ownOutput(input.out.tx, input.out.index))
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@ -596,8 +596,11 @@ TX.prototype.verify = function verify(index, force, flags) {
|
|||||||
if (this.inputs.length === 0)
|
if (this.inputs.length === 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (!flags)
|
||||||
|
flags = {};
|
||||||
|
|
||||||
return this.inputs.every(function(input, i) {
|
return this.inputs.every(function(input, i) {
|
||||||
var stack, prev, push, res, redeem;
|
var output;
|
||||||
|
|
||||||
if (index != null && index !== i)
|
if (index != null && index !== i)
|
||||||
return true;
|
return true;
|
||||||
@ -605,41 +608,16 @@ TX.prototype.verify = function verify(index, force, flags) {
|
|||||||
if (!input.out.tx)
|
if (!input.out.tx)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
output = input.out.tx.outputs[input.out.index];
|
||||||
|
|
||||||
|
assert(input.out.tx.outputs.length > input.out.index);
|
||||||
assert.equal(input.out.tx.hash('hex'), input.out.hash);
|
assert.equal(input.out.tx.hash('hex'), input.out.hash);
|
||||||
|
|
||||||
// Transaction cannot reference itself
|
// Transaction cannot reference itself
|
||||||
if (input.out.tx.hash('hex') === this.hash('hex'))
|
if (input.out.tx.hash('hex') === this.hash('hex'))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
assert(input.out.tx.outputs.length > input.out.index);
|
return bcoin.script.verify(input.script, output.script, this, i, flags);
|
||||||
|
|
||||||
stack = [];
|
|
||||||
prev = input.out.tx.outputs[input.out.index].script;
|
|
||||||
|
|
||||||
if (bcoin.script.isScripthash(prev)) {
|
|
||||||
// P2SH transactions cannot have anything
|
|
||||||
// other than pushdata ops in the scriptSig.
|
|
||||||
if (!bcoin.script.pushOnly(input.script))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bcoin.script.execute(input.script, stack, this, i, flags);
|
|
||||||
res = bcoin.script.execute(prev, stack, this, i, flags);
|
|
||||||
|
|
||||||
if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (bcoin.script.isScripthash(prev)) {
|
|
||||||
redeem = input.script[input.script.length - 1];
|
|
||||||
if (!Array.isArray(redeem))
|
|
||||||
return false;
|
|
||||||
redeem = bcoin.script.decode(redeem);
|
|
||||||
res = bcoin.script.execute(redeem, stack, this, i, flags);
|
|
||||||
if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}, this);
|
}, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -660,7 +638,7 @@ TX.prototype.maxSize = function maxSize() {
|
|||||||
|
|
||||||
// Add size for signatures and public keys
|
// Add size for signatures and public keys
|
||||||
copy.inputs.forEach(function(input, i) {
|
copy.inputs.forEach(function(input, i) {
|
||||||
var s, m, n, script, redeem;
|
var s, m, n;
|
||||||
|
|
||||||
// Get the previous output's subscript
|
// Get the previous output's subscript
|
||||||
s = input.out.tx.getSubscript(input.out.index);
|
s = input.out.tx.getSubscript(input.out.index);
|
||||||
@ -694,34 +672,16 @@ TX.prototype.maxSize = function maxSize() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bcoin.script.isScripthash(s)) {
|
if (bcoin.script.isScripthash(s)) {
|
||||||
script = this.inputs[i].script;
|
|
||||||
if (script.length) {
|
|
||||||
redeem = bcoin.script.decode(script[script.length - 1]);
|
|
||||||
m = redeem[0];
|
|
||||||
n = redeem[redeem.length - 2];
|
|
||||||
// If using pushdata instead of OP_1-16:
|
|
||||||
if (Array.isArray(m))
|
|
||||||
m = m[0] || 0;
|
|
||||||
if (Array.isArray(n))
|
|
||||||
n = n[0] || 0;
|
|
||||||
} else {
|
|
||||||
// May end up in a higher fee if we
|
|
||||||
// do not have the redeem script available.
|
|
||||||
m = 15;
|
|
||||||
n = 15;
|
|
||||||
}
|
|
||||||
assert(m >= 1 && m <= n);
|
|
||||||
assert(n >= 1 && n <= 15);
|
|
||||||
// Multisig
|
// Multisig
|
||||||
// Empty byte
|
// Empty byte
|
||||||
size += 1;
|
size += 1;
|
||||||
// Signature + len
|
// Signature + len
|
||||||
size += 74 * m;
|
size += 74 * 15;
|
||||||
// Redeem script
|
// Redeem script
|
||||||
// m byte
|
// m byte
|
||||||
size += 1;
|
size += 1;
|
||||||
// 1 byte length + 65 byte pubkey
|
// 1 byte length + 65 byte pubkey
|
||||||
size += 66 * n;
|
size += 66 * 15;
|
||||||
// n byte
|
// n byte
|
||||||
size += 1;
|
size += 1;
|
||||||
// checkmultisig byte
|
// checkmultisig byte
|
||||||
@ -848,7 +808,7 @@ TX.prototype._recalculateFee = function recalculateFee() {
|
|||||||
output = this.outputs[this.outputs.length - 1];
|
output = this.outputs[this.outputs.length - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
var byteSize = this.maxSize();
|
var byteSize = this.render().length;
|
||||||
var newFee = Math.ceil(byteSize / 1024) * TX.fee;
|
var newFee = Math.ceil(byteSize / 1024) * TX.fee;
|
||||||
var currentFee = this.getFee().toNumber();
|
var currentFee = this.getFee().toNumber();
|
||||||
|
|
||||||
|
|||||||
@ -366,8 +366,10 @@ utils.array2utf8 = function array2utf8(arr) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
utils.copy = function copy(src, dst, off, force) {
|
utils.copy = function copy(src, dst, off, force) {
|
||||||
if (Buffer.isBuffer(src) && Buffer.isBuffer(dst) && !force)
|
if (Buffer.isBuffer(src) && Buffer.isBuffer(dst)) {
|
||||||
|
assert(!force);
|
||||||
return src.copy(dst, off, 0, src.length);
|
return src.copy(dst, off, 0, src.length);
|
||||||
|
}
|
||||||
|
|
||||||
var len = src.length;
|
var len = src.length;
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
|||||||
@ -405,7 +405,7 @@ Wallet.prototype.ownInput = function ownInput(tx, index) {
|
|||||||
if (bcoin.script.isPubkeyhashInput(input.script, key))
|
if (bcoin.script.isPubkeyhashInput(input.script, key))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// if (bcoin.script.isMultisigInput(input.script, key, tx, i))
|
// if (bcoin.script.isMultisigInput(input.script, keys, tx, i))
|
||||||
// return true;
|
// return true;
|
||||||
|
|
||||||
if (bcoin.script.isScripthashInput(input.script, redeem))
|
if (bcoin.script.isScripthashInput(input.script, redeem))
|
||||||
@ -449,10 +449,9 @@ Wallet.prototype.fillUnspent = function fillUnspent(tx, changeAddress) {
|
|||||||
return tx.fillUnspent(this.unspent(), changeAddress);
|
return tx.fillUnspent(this.unspent(), changeAddress);
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.scriptInputs = function scriptInputs(tx, inputs) {
|
Wallet.prototype.scriptInputs = function scriptInputs(tx) {
|
||||||
var pub = this.getFullPublicKey();
|
var pub = this.getFullPublicKey();
|
||||||
|
var inputs = tx.inputs;
|
||||||
inputs = inputs || tx.inputs;
|
|
||||||
|
|
||||||
inputs = inputs.filter(function(input, i) {
|
inputs = inputs.filter(function(input, i) {
|
||||||
if (!input.out.tx && this.tx._all[input.out.hash])
|
if (!input.out.tx && this.tx._all[input.out.hash])
|
||||||
@ -470,14 +469,13 @@ Wallet.prototype.scriptInputs = function scriptInputs(tx, inputs) {
|
|||||||
return inputs.length;
|
return inputs.length;
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.signInputs = function signInputs(tx, type, inputs) {
|
Wallet.prototype.signInputs = function signInputs(tx, type) {
|
||||||
var key = this.key;
|
var key = this.key;
|
||||||
|
var inputs = tx.inputs;
|
||||||
|
|
||||||
if (!type)
|
if (!type)
|
||||||
type = 'all';
|
type = 'all';
|
||||||
|
|
||||||
inputs = inputs || tx.inputs;
|
|
||||||
|
|
||||||
inputs = inputs.filter(function(input, i) {
|
inputs = inputs.filter(function(input, i) {
|
||||||
if (!input.out.tx && this.tx._all[input.out.hash])
|
if (!input.out.tx && this.tx._all[input.out.hash])
|
||||||
input.out.tx = this.tx._all[input.out.hash];
|
input.out.tx = this.tx._all[input.out.hash];
|
||||||
@ -493,14 +491,13 @@ Wallet.prototype.signInputs = function signInputs(tx, type, inputs) {
|
|||||||
return inputs.length;
|
return inputs.length;
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.sign = function sign(tx, type, inputs) {
|
Wallet.prototype.sign = function sign(tx, type) {
|
||||||
if (!type)
|
if (!type)
|
||||||
type = 'all';
|
type = 'all';
|
||||||
|
|
||||||
var pub = this.getFullPublicKey();
|
var pub = this.getFullPublicKey();
|
||||||
var key = this.key;
|
var key = this.key;
|
||||||
|
var inputs = tx.inputs;
|
||||||
inputs = inputs || tx.inputs;
|
|
||||||
|
|
||||||
// Add signature script to each input
|
// Add signature script to each input
|
||||||
inputs = inputs.filter(function(input, i) {
|
inputs = inputs.filter(function(input, i) {
|
||||||
@ -623,11 +620,7 @@ Wallet.prototype.toJSON = function toJSON(encrypt) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Wallet.fromJSON = function fromJSON(json, decrypt) {
|
Wallet.fromJSON = function fromJSON(json, decrypt) {
|
||||||
var compressed, key, w;
|
var priv, pub, xprivkey, multisig, compressed, key, w;
|
||||||
var priv = json.priv;
|
|
||||||
var pub = json.pub;
|
|
||||||
var xprivkey = json.xprivkey;
|
|
||||||
var multisig = json.multisig;
|
|
||||||
|
|
||||||
assert.equal(json.v, 2);
|
assert.equal(json.v, 2);
|
||||||
assert.equal(json.type, 'wallet');
|
assert.equal(json.type, 'wallet');
|
||||||
@ -638,7 +631,8 @@ Wallet.fromJSON = function fromJSON(json, decrypt) {
|
|||||||
if (json.encrypted && !decrypt)
|
if (json.encrypted && !decrypt)
|
||||||
throw new Error('Cannot decrypt wallet');
|
throw new Error('Cannot decrypt wallet');
|
||||||
|
|
||||||
if (priv) {
|
if (json.priv) {
|
||||||
|
priv = json.priv;
|
||||||
if (json.encrypted)
|
if (json.encrypted)
|
||||||
priv = decrypt(priv);
|
priv = decrypt(priv);
|
||||||
|
|
||||||
@ -656,20 +650,21 @@ Wallet.fromJSON = function fromJSON(json, decrypt) {
|
|||||||
compressed = false;
|
compressed = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pub = bcoin.utils.fromBase58(pub);
|
pub = bcoin.utils.fromBase58(json.pub);
|
||||||
compressed = pub[0] !== 0x04;
|
compressed = pub[0] !== 0x04;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (multisig) {
|
if (json.multisig) {
|
||||||
multisig = {
|
multisig = {
|
||||||
type: multisig.type,
|
type: json.multisig.type,
|
||||||
keys: multisig.keys.map(utils.fromBase58),
|
keys: json.multisig.keys.map(utils.fromBase58),
|
||||||
m: multisig.m,
|
m: json.multisig.m,
|
||||||
n: multisig.n
|
n: json.multisig.n
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xprivkey) {
|
if (json.xprivkey) {
|
||||||
|
xprivkey = json.xprivkey;
|
||||||
if (json.encrypted)
|
if (json.encrypted)
|
||||||
xprivkey = decrypt(xprivkey);
|
xprivkey = decrypt(xprivkey);
|
||||||
priv = bcoin.hd.priv(xprivkey);
|
priv = bcoin.hd.priv(xprivkey);
|
||||||
|
|||||||
@ -42,7 +42,7 @@ describe('Script', function() {
|
|||||||
var hex = '6a28590c080112220a1b353930632e6f7267282a5f5e294f7665726c6179404f7261636c65103b1a010c'
|
var hex = '6a28590c080112220a1b353930632e6f7267282a5f5e294f7665726c6179404f7261636c65103b1a010c'
|
||||||
var encoded = bcoin.utils.toArray(hex, 'hex')
|
var encoded = bcoin.utils.toArray(hex, 'hex')
|
||||||
var decoded = bcoin.script.decode(encoded);
|
var decoded = bcoin.script.decode(encoded);
|
||||||
assert(bcoin.script.isColored(decoded))
|
assert(bcoin.script.isNulldata(decoded))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should handle if statements correctly', function () {
|
it('should handle if statements correctly', function () {
|
||||||
|
|||||||
@ -254,8 +254,8 @@ describe('Wallet', function() {
|
|||||||
tx.input(unspent1[1]);
|
tx.input(unspent1[1]);
|
||||||
tx.input(unspent1[2]);
|
tx.input(unspent1[2]);
|
||||||
tx.input(unspent2[1]);
|
tx.input(unspent2[1]);
|
||||||
assert.equal(w1.sign(tx, 'all', tx.inputs.slice(), 0), 2);
|
assert.equal(w1.sign(tx, 'all', tx.inputs.slice()), 2);
|
||||||
assert.equal(w2.sign(tx, 'all', tx.inputs.slice(2), 2), 1);
|
assert.equal(w2.sign(tx, 'all', tx.inputs.slice(2)), 1);
|
||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
assert.equal(tx.verify(), true);
|
assert.equal(tx.verify(), true);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user