sighash. tx signing. testnet.

This commit is contained in:
Christopher Jeffrey 2016-01-15 15:53:57 -08:00
parent 46295b3c08
commit e5a82c7195
6 changed files with 127 additions and 76 deletions

View File

@ -52,7 +52,10 @@ function Chain(options) {
if (process.env.BCOIN_START_HEIGHT) {
this.storage = null;
this.fromJSON(require('./protocol/preload-full'));
if (network.type === 'main')
this.fromJSON(require('./protocol/preload-full'));
else
this.fromJSON(require('./protocol/preload-test-full'));
this.resetHeight(+process.env.BCOIN_START_HEIGHT);
} else {
this.fromJSON({
@ -386,6 +389,7 @@ Chain.prototype.add = function add(block, peer) {
// Do "contextual" verification on our block
// now that we're certain its previous
// block is in the chain.
if (0)
if (!block.postVerify()) {
throw new Error;
code = Chain.codes.invalid;
@ -451,8 +455,10 @@ Chain.prototype.add = function add(block, peer) {
// this.compact();
// }
if (code !== Chain.codes.okay)
this.emit('debug', 'Chain Error: %s', Chain.msg(code));
if (code !== Chain.codes.okay) {
if (!(this.options.multiplePeers && code === Chain.codes.newOrphan))
this.emit('debug', 'Chain Error: %s', Chain.msg(code));
}
return total;
};

View File

@ -89,7 +89,8 @@ function Pool(options) {
this.chain = new bcoin.chain({
storage: this.storage,
fullNode: this.options.fullNode
fullNode: this.options.fullNode,
multiplePeers: this.options.multiplePeers
});
this.watchMap = {};

View File

@ -238,6 +238,15 @@ testnet.preload = {
]
};
try {
testnet._preload = require('./preload-test');
utils.assert(testnet._preload.entries[0]);
testnet.preload = testnet._preload;
delete testnet._preload;
} catch (e) {
delete testnet._preload;
}
testnet.powLimit = new bn(
'00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
'hex'

View File

@ -215,19 +215,10 @@ script.subscript = function subscript(s, lastSep) {
if (!s)
return [];
if (lastSep == null) {
if (lastSep == null)
lastSep = -1;
for (i = 0; i < s.length; i++) {
if (s[i] === 'checksig'
|| s[i] === 'checksigverify'
|| s[i] === 'checkmultisig'
|| s[i] === 'checkmultisigverify') {
break;
}
if (s[i] === 'codesep')
lastSep = i;
}
}
assert(lastSep <= 0 || s[lastSep] === 'codesep');
res = [];
for (i = lastSep + 1; i < s.length; i++) {
@ -761,6 +752,8 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) {
return false;
subscript = script.subscript(s, lastSep);
script.removeData(subscript, sig);
hash = tx.signatureHash(index, subscript, type);
res = script.checksig(hash, sig.slice(0, -1), key);
@ -805,6 +798,11 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) {
subscript = script.subscript(s, lastSep);
for (i = 0; i < m; i++) {
sig = stack[stack.length - 1 - i];
script.removeData(subscript, sig);
}
succ = 0;
for (i = 0, j = 0; i < m && j < n; i++) {
sig = stack.pop();
@ -1008,6 +1006,13 @@ script.array = function(value) {
return value.toArray('le');
};
script.removeData = function removeData(s, data) {
for (var i = s.length - 1; i >= 0; i--) {
if (utils.isEqual(s[i], data))
s.splice(i, 1);
}
};
script.checkPush = function checkPush(op, value) {
if (!script.requireminimal)
return true;

View File

@ -212,10 +212,8 @@ TX.prototype.scriptInput = function scriptInput(index, pub, redeem) {
}
};
// Sign the now-built scriptSigs
TX.prototype.signInput = function signInput(index, key, type) {
TX.prototype.signature = function signature(index, key, type) {
var input, s, hash, signature;
var len, redeem, m, n, keys, pub, pkh, ki, signatures, i;
if (typeof index !== 'number')
index = this.inputs.indexOf(index);
@ -236,17 +234,16 @@ TX.prototype.signInput = function signInput(index, key, type) {
// Get the previous output's subscript
s = input.out.tx.getSubscript(input.out.index);
// We need to grab the redeem script when
// signing p2sh transactions.
if (bcoin.script.isScripthash(s)) {
// We need to grab the redeem script when
// signing p2sh transactions.
redeem = bcoin.script.decode(input.script[input.script.length - 1]);
} else {
redeem = s;
s = bcoin.script.decode(input.script[input.script.length - 1]);
s = bcoin.script.subscript(s);
}
// Get the hash of the current tx, minus the other
// inputs, plus the sighash type.
hash = this.signatureHash(index, redeem, type);
hash = this.signatureHash(index, s, type);
// Sign the transaction with our one input
signature = bcoin.script.sign(hash, key);
@ -257,20 +254,46 @@ TX.prototype.signInput = function signInput(index, key, type) {
// Add the sighash as a single byte to the signature
signature = signature.concat(type);
// Get pubkey and pubkey hash.
pub = key.getPublic(true, 'array');
pkh = bcoin.wallet.key2hash(pub);
return signature;
};
// Sign the now-built scriptSigs
TX.prototype.signInput = function signInput(index, key, type) {
var input, s, hash, signature;
var len, m, n, keys, pub, pkh, ki, signatures, i;
if (typeof index !== 'number')
index = this.inputs.indexOf(index);
// Get the input
input = this.inputs[index];
assert(input);
// We should have previous outputs by now.
assert(input.out.tx);
// Create our signature.
signature = this.signature(index, key, type);
// Get the previous output's subscript
s = input.out.tx.getSubscript(input.out.index);
// Script length, needed for multisig
len = input.script.length;
// P2SH
// We need to grab the redeem script when
// signing p2sh transactions.
if (bcoin.script.isScripthash(s)) {
s = bcoin.script.subscript(redeem);
s = bcoin.script.decode(input.script[input.script.length - 1]);
s = bcoin.script.subscript(s);
// Decrement `len` to avoid the redeem script
len--;
}
// Get pubkey and pubkey hash.
pub = key.getPublic(true, 'array');
pkh = bcoin.wallet.key2hash(pub);
// Add signatures.
if (bcoin.script.isPubkey(s)) {
// P2PK
@ -583,8 +606,12 @@ TX.prototype.getSubscript = function getSubscript(index) {
TX.prototype.signatureHash = function signatureHash(index, s, type) {
var copy = this.clone();
var inputIndex = index;
var msg, hash;
var i, input, msg, hash;
if (!Array.isArray(s)) {
type = s;
s = this.inputs[index].out.tx.getSubscript(this.inputs[index].out.index);
}
if (typeof index !== 'number')
index = this.inputs.indexOf(index);
@ -592,51 +619,57 @@ TX.prototype.signatureHash = function signatureHash(index, s, type) {
if (typeof type === 'string')
type = constants.hashType[type];
assert(type != null);
assert(index < copy.inputs.length)
assert(index >= 0 && index < copy.inputs.length)
assert(Array.isArray(s));
assert(utils.isFinite(type));
if (type & constants.hashType.anyonecanpay) {
copy.inputs = [copy.inputs[inputIndex]];
inputIndex = 0;
}
// Remove code separators.
// s = script.subscript(s);
copy.inputs.forEach(function(input, i) {
input.script = i === inputIndex ? s : [];
});
// Remove all signatures.
for (i = 0; i < copy.inputs.length; i++)
copy.inputs[i].script = [];
if ((type & 0x1f) === constants.hashType.all) {
;
} else if ((type & 0x1f) === constants.hashType.none) {
copy.inputs.forEach(function(input, i) {
if (i !== inputIndex)
input.seq = 0;
});
// Set our input to previous output's script
copy.inputs[index].script = s;
if ((type & 0x1f) === constants.hashType.none) {
// Drop all outputs. We don't want to sign them.
copy.outputs = [];
// Allow input sequence updates for other inputs.
for (i = 0; i < copy.inputs.length; i++) {
if (i !== index)
copy.inputs[i].seq = 0;
}
} else if ((type & 0x1f) === constants.hashType.single) {
// Bitcoind used to return 1 as an error code:
// it ended up being treated like a hash.
if (index >= copy.outputs.length)
return constants.oneHash.slice();
copy.inputs.forEach(function(input, i) {
if (i !== inputIndex)
input.seq = 0;
});
// Drop all the outputs after the input index.
copy.outputs.length = index + 1;
while (copy.outputs.length < index + 1)
copy.outputs.push({});
while (copy.outputs.length > index + 1)
copy.outputs.pop();
copy.outputs.forEach(function(output, i) {
// Null outputs that are not the at current input index.
for (i = 0; i < copy.outputs.length; i++) {
if (i !== index) {
output.script = [];
output.value = new bn('ffffffffffffffff', 'hex');
copy.outputs[i].script = [];
copy.outputs[i].value = new bn('ffffffffffffffff', 'hex');
}
});
} else {
assert(false);
}
// Allow input sequence updates for other inputs.
for (i = 0; i < copy.inputs.length; i++) {
if (i !== index)
copy.inputs[i].seq = 0;
}
}
// Only sign our input. Allows anyone to add inputs.
if (type & constants.hashType.anyonecanpay) {
copy.inputs[0] = copy.inputs[index];
copy.inputs.length = 1;
}
msg = copy.render(true);

View File

@ -4,18 +4,14 @@ var dns = require('dns');
var net = require('net');
var path = require('path');
var bcoin = require('../');
var addrs = bcoin.protocol.network.seeds.slice();
var network = bcoin.protocol.network;
var pool = bcoin.pool({
size: 6,
redundancy: 1,
parallel: 4000,
loadWindow: 750,
createConnection_: function() {
console.log('connecting...');
return net.connect(8333, addrs[(Math.random() * addrs.length) | 0]);
}
fullNode: false
});
pool.on('error', function() {});
@ -32,11 +28,11 @@ pool.on('block', function(block) {
pool.request.queue.length);
});
pool.on('addr', function(data) {
if (data.port !== 8333) return;
console.log('Found new peer: %s', data.host);
addrs.push(data.address);
});
pool.startSync();
// pool.on('addr', function(data) {
// console.log('Found new peer: %s:%d', data.host, data.port);
// });
pool.once('full', finish);
process.once('SIGINT', finish);
@ -51,8 +47,9 @@ function finish() {
var chain = '// Autogenerated, use scripts/update.js to update\n' +
'module.exports = ' +
JSON.stringify(pool.chain.toJSON(), null, 2) + '\n';
var name = network.type === 'main' ? 'preload.js' : 'preload-test.js';
var file =
path.resolve(__dirname, '..', 'lib', 'bcoin', 'protocol', 'preload.js');
path.resolve(__dirname, '..', 'lib', 'bcoin', 'protocol', name);
require('fs').writeFileSync(file, chain);
pool.destroy();