wallet: multisig signing
This commit is contained in:
parent
fa48b02d96
commit
f139ce2d91
@ -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');
|
||||
|
||||
@ -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
0
lib/bcoin/tx-pool.js
Normal 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',
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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());
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user