improve script functionality and usage.
This commit is contained in:
parent
cf20326e98
commit
b1e7cde3dd
@ -1,6 +1,7 @@
|
|||||||
var bcoin = require('../bcoin');
|
var bcoin = require('../bcoin');
|
||||||
var constants = bcoin.protocol.constants;
|
var constants = bcoin.protocol.constants;
|
||||||
var utils = bcoin.utils;
|
var utils = bcoin.utils;
|
||||||
|
var assert = bcoin.utils.assert;
|
||||||
var script = exports;
|
var script = exports;
|
||||||
|
|
||||||
script.decode = function decode(s) {
|
script.decode = function decode(s) {
|
||||||
@ -129,14 +130,15 @@ script.verify = function verify(hash, sig, pub) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
script.execute = function execute(s, stack, hasher) {
|
script.execute = function execute(s, stack, hasher) {
|
||||||
if (s.length > 10000) {
|
if (s.length > 10000)
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
for (var pc = 0; pc < s.length; pc++) {
|
for (var pc = 0; pc < s.length; pc++) {
|
||||||
var o = s[pc];
|
var o = s[pc];
|
||||||
if (Array.isArray(o)) {
|
if (Array.isArray(o)) {
|
||||||
stack.push(o);
|
stack.push(o);
|
||||||
|
} else if (typeof o === 'number' && o >= 1 && o <= 16) {
|
||||||
|
stack.push([o]);
|
||||||
} else if (o === 'dup') {
|
} else if (o === 'dup') {
|
||||||
if (stack.length === 0)
|
if (stack.length === 0)
|
||||||
return false;
|
return false;
|
||||||
@ -247,21 +249,27 @@ script.execute = function execute(s, stack, hasher) {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
script.redemption = function(publicKeys, m, n) {
|
script.multisig = function(keys, m, n) {
|
||||||
if (publicKeys.length !== m) {
|
if (keys.length < m)
|
||||||
throw new Error('wrong amount of pubkeys for redeem script');
|
throw new Error('wrong amount of pubkeys for multisig script');
|
||||||
}
|
|
||||||
var mcode = constants.opcodes['1'] + (m - 1);
|
assert(m >= 1 && m <= n);
|
||||||
var ncode = constants.opcodes['1'] + (n - 1);
|
assert(n >= 1 && n <= 7);
|
||||||
var redemption = [];
|
|
||||||
redemption.push(mcode);
|
// Format:
|
||||||
publicKeys.forEach(function(pubkey) {
|
// op_[m] [pubkey1-len] [pubkey1] ... op_[n] op_checkmultisig
|
||||||
redemption.push(pubkey.length);
|
|
||||||
redemption = redemption.concat(pubkey);
|
// Using pushdata ops for m and n:
|
||||||
});
|
// return [ [ m ] ].concat(
|
||||||
redemption.push(ncode);
|
// keys,
|
||||||
redemption.push(constants.opcodes.checkmultisig);
|
// [ [ n ], 'checkmultisig' ]
|
||||||
return redemption;
|
// );
|
||||||
|
|
||||||
|
// Using OP_1-16 for m and n:
|
||||||
|
return [ m ].concat(
|
||||||
|
keys,
|
||||||
|
[ n, 'checkmultisig' ]
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
script.isPubkeyhash = function isPubkeyhash(s, hash) {
|
script.isPubkeyhash = function isPubkeyhash(s, hash) {
|
||||||
@ -301,6 +309,8 @@ script.isMultisig = function isMultisig(s, key) {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
var m = s[0];
|
var m = s[0];
|
||||||
|
if (typeof m === 'number' && m >= 1 && m <= 16)
|
||||||
|
m = [m];
|
||||||
if (!Array.isArray(m) || m.length !== 1)
|
if (!Array.isArray(m) || m.length !== 1)
|
||||||
return false;
|
return false;
|
||||||
m = m[0];
|
m = m[0];
|
||||||
@ -309,6 +319,8 @@ script.isMultisig = function isMultisig(s, key) {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
var n = s[s.length - 2];
|
var n = s[s.length - 2];
|
||||||
|
if (typeof n === 'number' && n >= 1 && n <= 16)
|
||||||
|
n = [n];
|
||||||
if (!Array.isArray(n) || n.length !== 1)
|
if (!Array.isArray(n) || n.length !== 1)
|
||||||
return false;
|
return false;
|
||||||
n = n[0];
|
n = n[0];
|
||||||
|
|||||||
@ -42,8 +42,6 @@ function TX(data, block) {
|
|||||||
// ps = Pending Since
|
// ps = Pending Since
|
||||||
this.ps = this.ts === 0 ? +new Date() / 1000 : 0;
|
this.ps = this.ts === 0 ? +new Date() / 1000 : 0;
|
||||||
|
|
||||||
this.m = data.m || null;
|
|
||||||
this.n = data.n || null;
|
|
||||||
this.change = data.change || null;
|
this.change = data.change || null;
|
||||||
this.fee = data.fee || 10000;
|
this.fee = data.fee || 10000;
|
||||||
this.dust = 5460;
|
this.dust = 5460;
|
||||||
@ -152,7 +150,6 @@ TX.prototype.scriptInput = function(input, pub, nsigs) {
|
|||||||
|
|
||||||
// P2PKH and simple tx
|
// P2PKH and simple tx
|
||||||
if (bcoin.script.isPubkeyhash(s) || bcoin.script.isSimplePubkeyhash(s)) {
|
if (bcoin.script.isPubkeyhash(s) || bcoin.script.isSimplePubkeyhash(s)) {
|
||||||
//input.script = [ constants.opcodes['0'], pub ];
|
|
||||||
input.script = [ [], pub ];
|
input.script = [ [], pub ];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -160,13 +157,12 @@ TX.prototype.scriptInput = function(input, pub, nsigs) {
|
|||||||
// Multisig
|
// Multisig
|
||||||
// raw format: OP_FALSE [sig-1] [sig-2] ...
|
// raw format: OP_FALSE [sig-1] [sig-2] ...
|
||||||
if (bcoin.script.isMultisig(s)) {
|
if (bcoin.script.isMultisig(s)) {
|
||||||
nsigs = nsigs || this.m;
|
|
||||||
if (!nsigs)
|
|
||||||
throw new Error('`nsigs` is required for multisig');
|
|
||||||
//input.script = [ constants.opcodes['false'] ];
|
|
||||||
input.script = [ [] ];
|
input.script = [ [] ];
|
||||||
for (var i = 0; i < nsigs; i++)
|
var m = s[0];
|
||||||
//input.script[i + 1] = constants.opcodes['0'];
|
// If using pushdata instead of OP_1-16:
|
||||||
|
if (Array.isArray(m))
|
||||||
|
m = m[0];
|
||||||
|
for (var i = 0; i < m; i++)
|
||||||
input.script[i + 1] = [];
|
input.script[i + 1] = [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -174,11 +170,9 @@ TX.prototype.scriptInput = function(input, pub, nsigs) {
|
|||||||
// P2SH multisig
|
// P2SH multisig
|
||||||
// p2sh format: OP_FALSE [sig-1] [sig-2] ... [redeem-script]
|
// p2sh format: OP_FALSE [sig-1] [sig-2] ... [redeem-script]
|
||||||
if (bcoin.script.isScripthash(s)) {
|
if (bcoin.script.isScripthash(s)) {
|
||||||
//input.script = [ constants.opcodes['false'] ];
|
|
||||||
input.script = [ [] ];
|
input.script = [ [] ];
|
||||||
var m = pub[0] - constants.opcodes['1'] + 1;
|
var m = pub[0] - constants.opcodes['1'] + 1;
|
||||||
for (var i = 0; i < m; i++)
|
for (var i = 0; i < m; i++)
|
||||||
//input.script[i + 1] = constants.opcodes['0'];
|
|
||||||
input.script[i + 1] = [];
|
input.script[i + 1] = [];
|
||||||
// P2SH requires the redeem script after signatures
|
// P2SH requires the redeem script after signatures
|
||||||
input.script.push(pub);
|
input.script.push(pub);
|
||||||
@ -228,7 +222,6 @@ TX.prototype.signInput = function(input, key, type) {
|
|||||||
if (utils.isEqual(input.script[i], signature))
|
if (utils.isEqual(input.script[i], signature))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
//if (input.script[i] === constants.opcodes['0']) {
|
|
||||||
if (input.script[i].length === 0) {
|
if (input.script[i].length === 0) {
|
||||||
input.script[i] = signature;
|
input.script[i] = signature;
|
||||||
break;
|
break;
|
||||||
@ -283,26 +276,42 @@ TX.prototype.scriptOutput = function(options) {
|
|||||||
// https://github.com/bitcoin/bips/blob/master/bip-0019.mediawiki
|
// https://github.com/bitcoin/bips/blob/master/bip-0019.mediawiki
|
||||||
// [required-sigs] [pubkey-hash1] [pubkey-hash2] ... [number-of-keys] checkmultisig
|
// [required-sigs] [pubkey-hash1] [pubkey-hash2] ... [number-of-keys] checkmultisig
|
||||||
var keys = options.keys || options.address;
|
var keys = options.keys || options.address;
|
||||||
|
|
||||||
if (keys === options.address) {
|
if (keys === options.address) {
|
||||||
keys = keys.map(function(address) {
|
keys = keys.map(function(address) {
|
||||||
return bcoin.wallet.addr2hash(address, 'normal');
|
return bcoin.wallet.addr2hash(address, 'normal');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
keys = keys.map(function(key) {
|
keys = keys.map(function(key) {
|
||||||
if (typeof key === 'string') {
|
if (typeof key === 'string') {
|
||||||
return utils.toKeyArray(key);
|
return utils.toKeyArray(key);
|
||||||
}
|
}
|
||||||
return key;
|
return key;
|
||||||
});
|
});
|
||||||
script = [
|
|
||||||
[ options.minSignatures || keys.length ]
|
// compat:
|
||||||
].concat(
|
options.m = options.minSignatures || options.m;
|
||||||
keys,
|
var m = options.m || keys.length;
|
||||||
[ [ keys.length ], 'checkmultisig' ]
|
var n = options.n || keys.length;
|
||||||
);
|
|
||||||
// outputs: [ [ 2 ], 'key1', 'key2', [ 2 ], 'checkmultisig' ]
|
assert(m >= 1 && m <= n);
|
||||||
// in reality:
|
if (options.hash)
|
||||||
// outputs: [ [ 2 ], [0,1,...], [2,3,...], [ 2 ], 'checkmultisig' ]
|
assert(n >= 1 && n <= 7);
|
||||||
|
else
|
||||||
|
assert(n >= 1 && n <= 3);
|
||||||
|
|
||||||
|
script = bcoin.script.multisig(keys, m, n);
|
||||||
|
|
||||||
|
// make it p2sh
|
||||||
|
if (options.hash) {
|
||||||
|
var hash = utils.ripesha(bcoin.script.encode(script));
|
||||||
|
script = [
|
||||||
|
'hash160',
|
||||||
|
hash,
|
||||||
|
'eq'
|
||||||
|
];
|
||||||
|
}
|
||||||
} else if (bcoin.wallet.validateAddress(options.address, 'p2sh')) {
|
} else if (bcoin.wallet.validateAddress(options.address, 'p2sh')) {
|
||||||
// p2sh transaction
|
// p2sh transaction
|
||||||
// https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki
|
// https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki
|
||||||
@ -413,8 +422,11 @@ TX.prototype.maxSize = function maxSize() {
|
|||||||
// Empty byte
|
// Empty byte
|
||||||
size += 1;
|
size += 1;
|
||||||
// Signature + len
|
// Signature + len
|
||||||
var m = s[0][0] || this.m;
|
var m = s[0];
|
||||||
assert(m >= 1 && m <= 7);
|
// If using pushdata instead of OP_1-16:
|
||||||
|
if (Array.isArray(m))
|
||||||
|
m = m[0];
|
||||||
|
assert(m >= 1 && m <= 3);
|
||||||
size += 74 * m;
|
size += 74 * m;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -424,11 +436,18 @@ TX.prototype.maxSize = function maxSize() {
|
|||||||
var redeem, m, n;
|
var redeem, m, n;
|
||||||
if (script) {
|
if (script) {
|
||||||
redeem = script[script.length - 1];
|
redeem = script[script.length - 1];
|
||||||
m = redeem[0] - constants.opcodes['1'] + 1;
|
m = redeem[0];
|
||||||
n = redeem[redeem.length - 2] - constants.opcodes['1'] + 1;
|
n = redeem[redeem.length - 2];
|
||||||
|
// If using pushdata instead of OP_1-16:
|
||||||
|
if (Array.isArray(m))
|
||||||
|
m = m[0];
|
||||||
|
if (Array.isArray(n))
|
||||||
|
n = n[0];
|
||||||
} else {
|
} else {
|
||||||
m = this.m;
|
// May end up in a higher fee if we
|
||||||
n = this.n;
|
// do not have the redeem script available.
|
||||||
|
m = 7;
|
||||||
|
n = 7;
|
||||||
}
|
}
|
||||||
assert(m >= 1 && m <= n);
|
assert(m >= 1 && m <= n);
|
||||||
assert(n >= 1 && n <= 7);
|
assert(n >= 1 && n <= 7);
|
||||||
|
|||||||
@ -149,11 +149,12 @@ Wallet.prototype.getPrivateKey = function getPrivateKey(enc) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.getPublicKey = function getPublicKey(enc) {
|
Wallet.prototype.getPublicKey = function getPublicKey(enc) {
|
||||||
var pub;
|
var pub = this.key.getPublic(this.compressed, 'array');
|
||||||
if (this.addressType === 'p2sh')
|
|
||||||
pub = this.getRedemption();
|
if (this.addressType === 'p2sh') {
|
||||||
else
|
var keys = this.getPublicKeys();
|
||||||
pub = this.key.getPublic(this.compressed, 'array');
|
pub = bcoin.script.encode(bcoin.script.multisig(keys, m, n));
|
||||||
|
}
|
||||||
|
|
||||||
if (enc === 'base58')
|
if (enc === 'base58')
|
||||||
return utils.toBase58(pub);
|
return utils.toBase58(pub);
|
||||||
@ -163,6 +164,15 @@ Wallet.prototype.getPublicKey = function getPublicKey(enc) {
|
|||||||
return pub;
|
return pub;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Wallet.prototype.getPublicKeys = function() {
|
||||||
|
var keys = this.sharedKeys.slice().map(utils.toKeyArray);
|
||||||
|
if (keys.length < this.m) {
|
||||||
|
var pub = this.key.getPublic(this.compressed, 'array');
|
||||||
|
keys.push(pub);
|
||||||
|
}
|
||||||
|
return keys;
|
||||||
|
};
|
||||||
|
|
||||||
Wallet.prototype.getHash = function getHash() {
|
Wallet.prototype.getHash = function getHash() {
|
||||||
return utils.ripesha(this.getPublicKey());
|
return utils.ripesha(this.getPublicKey());
|
||||||
};
|
};
|
||||||
@ -328,16 +338,6 @@ Wallet.prototype.fill = function fill(tx, cb) {
|
|||||||
return tx;
|
return tx;
|
||||||
};
|
};
|
||||||
|
|
||||||
// P2SH Multisig redeem script
|
|
||||||
Wallet.prototype.getRedemption = function() {
|
|
||||||
var sharedKeys = this.sharedKeys.slice().map(utils.toKeyArray);
|
|
||||||
if (sharedKeys.length < this.m) {
|
|
||||||
var pub = this.key.getPublic(this.compressed, 'array');
|
|
||||||
sharedKeys.push(pub);
|
|
||||||
}
|
|
||||||
return bcoin.script.redemption(sharedKeys, m, n);
|
|
||||||
};
|
|
||||||
|
|
||||||
Wallet.prototype.toJSON = function toJSON() {
|
Wallet.prototype.toJSON = function toJSON() {
|
||||||
return {
|
return {
|
||||||
v: 1,
|
v: 1,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user