tx: wip sign/verify

This commit is contained in:
Fedor Indutny 2014-05-05 03:14:31 +04:00
parent 471a78b0fa
commit 9fd54758a2
6 changed files with 122 additions and 18 deletions

View File

@ -70,3 +70,69 @@ script.encode = function encode(s) {
return res;
};
script.subscript = function subscript(s) {
if (!s)
return [];
var lastSep = -1;
for (var i = 0; i < s.length; i++) {
if (s[i] === 'codesep')
lastSep = i;
else if (s[i] === 'checksig' ||
s[i] === 'checksigverify' ||
s[i] === 'checkmultisig' ||
s[i] === 'checkmultisigverify') {
break;
}
}
var res = [];
for (var i = lastSep + 1; i < s.length; i++)
if (s[i] !== 'codesep')
res.push(s[i]);
return res;
};
script.execute = function execute(s, stack, tx) {
for (var i = 0; i < s.length; i++) {
var o = s[i];
if (Array.isArray(o)) {
stack.push(o);
} else if (o === 'dup') {
if (stack.length === 0)
return false;
stack.push(stack[stack.length - 1]);
} else if (o === 'hash160') {
if (stack.length === 0)
return false;
stack.push(bcoin.utils.ripesha(stack.pop()));
} else if (o === 'eqverify' || o === 'eq') {
if (stack.length < 2)
return false;
var res = bcoin.utils.isEqual(stack.pop(), stack.pop());
stack.push([ res ? 1 : 0 ]);
if (!res && o === 'eqverify')
return false;
} else if (o === 'checksigverify' || o === 'checksig') {
if (!tx || stack.length < 2)
return false;
var pub = stack.pop();
var sig = stack.pop();
var res = bcoin.ecdsa.verify(tx, sig, pub);
stack.push([ res ? 1 : 0 ]);
if (!res && o ==='checksigverify')
return false;
} else {
// Unknown operation
return false;
}
}
return true;
};

View File

@ -1,3 +1,4 @@
var assert = require('assert');
var bn = require('bn.js');
var bcoin = require('../bcoin');
@ -53,7 +54,7 @@ TX.prototype.render = function render() {
TX.prototype.input = function input(i, index) {
if (i instanceof TX)
i = { tx: i, index: i };
i = { tx: i, index: index };
var hash;
if (i.tx)
@ -106,10 +107,37 @@ TX.prototype.out = function out(output, value) {
};
TX.prototype.getSubscript = function getSubscript(index) {
var input = this.inputs[index];
assert(input);
var output = this.outputs[index];
assert(output);
var script = input.script;
for (var i = 0; input.script.
var script = output.script;
return bcoin.script.subscript(script);
};
TX.prototype.subscriptHash = function subscriptHash(index, s, type) {
var copy = this.clone();
copy.inputs.forEach(function(input, i) {
input.script = index === i ? s : [];
});
var verifyStr = copy.render();
verifyStr = verifyStr.concat(bcoin.protocol.constants.hashType[type]);
var hash = utils.dsha256(verifyStr);
return hash;
};
TX.prototype.validate = function validate() {
return this.inputs.every(function(input, i) {
assert(input.out.tx);
assert(input.out.tx.outputs.length > input.out.index);
var subscript = input.out.tx.getSubscript(input.out.index);
var hash = this.subscriptHash(i, subscript, 'all');
var stack = [];
bcoin.script.execute(input.script, stack);
var prev = input.out.tx.outputs[input.out.index].script;
return bcoin.script.execute(prev, stack, hash);
}, this);
};

View File

@ -272,6 +272,17 @@ function testTarget(target, hash) {
}
utils.testTarget = testTarget;
utils.isEqual = function isEqual(a, b) {
if (a.length !== b.length)
return false;
for (var i = 0; i < a.length; i++)
if (a[i] !== b[i])
return false;
return true;
};
// TODO(indutny): use process.nextTick in node.js
utils.nextTick = function nextTick(fn) {
setTimeout(fn, 0);

View File

@ -56,7 +56,7 @@ Wallet.prototype.own = function own(tx) {
return output.script.length === 5 &&
output.script[0] === 'dup' &&
output.script[1] === 'hash160' &&
utils.toHex(output.script[2]) === utils.toHex(this.getHash()) &&
utils.isEqual(output.script[2], this.getHash()) &&
output.script[3] === 'eqverify' &&
output.script[4] === 'checksig';
}, this);
@ -75,17 +75,15 @@ Wallet.prototype.sign = function sign(tx, type) {
// Add signature script to each input
inputs.forEach(function(input, i) {
var copy = tx.clone();
var s = input.out.tx.getSubscript(input.out.index);
copy.inputs.forEach(function(input, j) {
input.script = i === j ? s : [];
});
var hash = tx.subscriptHash(i, s, type);
var signature = bcoin.ecdsa.sign(hash, this.key).toDER();
var verifyStr = copy.render();
verifyStr = verifyStr.concat(bcoin.protocol.constants.hashType[type]);
var hash = utils.dsha256(verifyStr);
var signature = this.key.sign(hash).toDER();
input.script = [
signature,
pub
];
}, this);
return inputs.length;

View File

@ -22,8 +22,8 @@
"homepage": "https://github.com/indutny/bcoin",
"dependencies": {
"async": "^0.8.0",
"bn.js": "^0.2.0",
"elliptic": "^0.7.0",
"bn.js": "^0.3.0",
"elliptic": "^0.8.0",
"hash.js": "^0.2.0"
},
"devDependencies": {

View File

@ -29,9 +29,10 @@ describe('Wallet', function() {
assert(w.own(src));
var tx = bcoin.tx()
.input(src, 1)
.input(src, 0)
.out(w.getAddress(), 5460);
w.sign(tx);
assert(tx.validate());
});
});