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

View File

@ -89,7 +89,8 @@ function Pool(options) {
this.chain = new bcoin.chain({ this.chain = new bcoin.chain({
storage: this.storage, storage: this.storage,
fullNode: this.options.fullNode fullNode: this.options.fullNode,
multiplePeers: this.options.multiplePeers
}); });
this.watchMap = {}; 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( testnet.powLimit = new bn(
'00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff', '00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
'hex' 'hex'

View File

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

View File

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

View File

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