wallet: multisig signing

This commit is contained in:
Fedor Indutny 2014-05-06 14:58:23 +04:00
parent fa48b02d96
commit f139ce2d91
7 changed files with 117 additions and 18 deletions

View File

@ -7,6 +7,7 @@ bcoin.bloom = require('./bcoin/bloom');
bcoin.protocol = require('./bcoin/protocol');
bcoin.script = require('./bcoin/script');
bcoin.tx = require('./bcoin/tx');
bcoin.txPool = require('./bcoin/tx-pool');
bcoin.block = require('./bcoin/block');
bcoin.chain = require('./bcoin/chain');
bcoin.wallet = require('./bcoin/wallet');

View File

@ -112,8 +112,8 @@ script.subscript = function subscript(s) {
};
script.execute = function execute(s, stack, tx) {
for (var i = 0; i < s.length; i++) {
var o = s[i];
for (var pc = 0; pc < s.length; pc++) {
var o = s[pc];
if (Array.isArray(o)) {
stack.push(o);
} else if (o === 'dup') {
@ -148,13 +148,67 @@ script.execute = function execute(s, stack, tx) {
if (type !== 1)
return false;
var res = bcoin.ecdsa.verify(tx, sig, pub);
var res = bcoin.ecdsa.verify(tx, sig.slice(0, -1), pub);
if (o === 'checksigverify') {
if (!res)
return false;
} else {
stack.push(res ? [ 1 ] : []);
}
} else if (o === 'checkmultisigverify' || o === 'checkmultisig') {
if (!tx || stack.length < 3)
return false;
var n = stack.pop();
if (n.length !== 1 || !(1 <= n[0] && n[0] <= 3))
return false;
n = n[0];
if (stack.length < n + 1)
return false;
var keys = [];
for (var i = 0; i < n; i++) {
var key = stack.pop();
if (!(33 <= key.length && key.length <= 65))
return false;
keys.push(key);
}
var m = stack.pop();
if (m.length !== 1 || !(1 <= m[0] && m[0] <= n))
return false;
m = m[0];
if (stack.length < m + 1)
return false;
// Get signatures
var succ = 0;
for (var i = 0, j = 0; i < m, j < n; i++) {
var sig = stack.pop();
var type = sig[sig.length - 1];
if (type !== 1)
return false;
var res = false;
for (; !res && j < n; j++)
res = bcoin.ecdsa.verify(tx, sig.slice(0, -1), keys[j]);
if (res)
succ++;
}
// Extra value
stack.pop();
var res = succ >= m;
if (o === 'checkmultisigverify') {
if (!res)
return false;
} else {
stack.push(res ? [ 1 ] : []);
}
} else {
// Unknown operation
return false;
@ -180,7 +234,7 @@ script.isPubkeyhash = function isPubkeyhash(s, hash) {
return utils.isEqual(s[2], hash);
else
return s[2];
}
};
script.isMultisig = function isMultisig(s, key) {
if (s.length < 4)
@ -191,14 +245,18 @@ script.isMultisig = function isMultisig(s, key) {
return false;
m = m[0];
if (m + 3 !== s.length || s[s.length - 1] !== 'checkmultisig')
if (s[s.length - 1] !== 'checkmultisig')
return false;
var n = s[s.length - 2];
if (!Array.isArray(n) || n.length !== 1)
return false;
n = n[0];
var keys = s.slice(1, m);
if (n + 3 !== s.length)
return false;
var keys = s.slice(1, 1 + n);
var isArray = keys.every(function(k) {
return Array.isArray(k);
});
@ -211,4 +269,4 @@ script.isMultisig = function isMultisig(s, key) {
return m === keys.filter(function(k) {
return utils.isEqual(k, key);
}).length;
}
};

0
lib/bcoin/tx-pool.js Normal file
View File

View File

@ -98,8 +98,17 @@ TX.prototype.out = function out(output, value) {
var script = output.script ? output.script.slice() : [];
// Multisig script if given addresses
if (Array.isArray(output.keys || output.address)) {
var keys = output.keys || output.address;
script = [
[ output.minSignatures || keys.length ]
].concat(
keys,
[ [ keys.length ], 'checkmultisig' ]
);
// Default script if given address
if (output.address) {
} else if (output.address) {
script = [
'dup',
'hash160',

View File

@ -99,7 +99,8 @@ utils.fromBase58 = function fromBase58(str) {
var res = new bn(0);
for (var i = zeroes; i < str.length; i++) {
var c = base58.indexOf(str[i]);
assert(c >= 0 && c < 58);
if (!(c >= 0 && c < 58))
return [];
q *= 58;
w *= 58;

View File

@ -77,20 +77,20 @@ Wallet.addr2hash = function addr2hash(addr) {
addr = utils.fromBase58(addr);
if (addr.length !== 25)
return false;
return [];
if (addr[0] !== 0)
return false;
return [];
var chk = utils.checksum(addr.slice(0, -4));
if (utils.readU32(chk, 0) !== utils.readU32(addr, 21))
return false;
return [];
return addr.slice(1, -4);
};
Wallet.prototype.validateAddress = function validateAddress(addr) {
var p = Wallet.addr2hash(addr);
return !!p;
return p.length !== 0;
};
Wallet.validateAddress = Wallet.prototype.validateAddress;
@ -128,14 +128,17 @@ Wallet.prototype.sign = function sign(tx, type) {
// Add signature script to each input
inputs.forEach(function(input, i) {
var s = input.out.tx.getSubscript(input.out.index);
var hash = tx.subscriptHash(i, s, type);
var signature = bcoin.ecdsa.sign(hash, this.key).toDER();
signature = signature.concat(bcoin.protocol.constants.hashType[type]);
input.script = [
signature.concat(bcoin.protocol.constants.hashType[type]),
pub
];
if (bcoin.script.isPubkeyhash(s)) {
input.script = [ signature, pub ];
return;
}
// Multisig
input.script = [ [], signature ];
}, this);
return inputs.length;

View File

@ -43,4 +43,31 @@ describe('Wallet', function() {
w.sign(tx);
assert(tx.verify());
});
it('should multisign/verify TX', function() {
var w = bcoin.wallet();
// Input transcation
var src = bcoin.tx({
outputs: [{
value: 5460 * 2,
minSignatures: 1,
address: [ w.getPublicKey(), w.getPublicKey().concat(1) ]
}, {
value: 5460 * 2,
address: w.getAddress() + 'x'
}]
});
assert(w.own(src));
assert.equal(w.own(src).reduce(function(acc, out) {
return acc.iadd(out.value);
}, new bn(0)).toString(10), 5460 * 2);
var tx = bcoin.tx()
.input(src, 0)
.out(w.getAddress(), 5460);
w.sign(tx);
assert(tx.verify());
});
});