script: implement bip114v2.
This commit is contained in:
parent
9561b3ee74
commit
ab052eb81d
@ -476,7 +476,7 @@ Address.prototype.fromData = function fromData(data, type, version, network) {
|
||||
data = crypto.sha256(data);
|
||||
} else if (version === 1) {
|
||||
assert(Array.isArray(data));
|
||||
data = crypto.getMerkleRoot(data);
|
||||
throw new Error('MASTv2 creation not implemented.');
|
||||
} else {
|
||||
throw new Error('Cannot create from version=' + version);
|
||||
}
|
||||
|
||||
@ -3229,14 +3229,14 @@ Script.verify = function verify(input, witness, output, tx, i, flags) {
|
||||
* @param {VerifyFlags} flags
|
||||
* @param {TX} tx
|
||||
* @param {Number} i
|
||||
* @returns {Boolean}
|
||||
* @throws {ScriptError}
|
||||
*/
|
||||
|
||||
Script.verifyProgram = function verifyProgram(witness, output, flags, tx, i) {
|
||||
var program = output.toProgram();
|
||||
var stack = witness.toStack();
|
||||
var witnessScript, redeem, j;
|
||||
var hash, pathdata, depth, path, posdata, pos, root;
|
||||
var j, witnessScript, redeem;
|
||||
|
||||
assert(program, 'verifyProgram called on non-witness-program.');
|
||||
assert((flags & constants.flags.VERIFY_WITNESS) !== 0);
|
||||
@ -3262,57 +3262,7 @@ Script.verifyProgram = function verifyProgram(witness, output, flags, tx, i) {
|
||||
throw new ScriptError('WITNESS_PROGRAM_WRONG_LENGTH');
|
||||
}
|
||||
} else if ((flags & constants.flags.VERIFY_MAST) && program.version === 1) {
|
||||
if (program.data.length !== 32)
|
||||
throw new ScriptError('WITNESS_PROGRAM_WRONG_LENGTH');
|
||||
|
||||
if (stack.length < 3)
|
||||
throw new ScriptError('WITNESS_PROGRAM_MISMATCH');
|
||||
|
||||
witnessScript = stack.pop();
|
||||
redeem = new Script(witnessScript);
|
||||
|
||||
hash = crypto.hash256(witnessScript);
|
||||
pathdata = stack.pop();
|
||||
|
||||
if (pathdata.length & 0x1f)
|
||||
throw new ScriptError('WITNESS_PROGRAM_MISMATCH');
|
||||
|
||||
depth = pathdata.length >>> 5;
|
||||
|
||||
if (depth > 32)
|
||||
throw new ScriptError('WITNESS_PROGRAM_MISMATCH');
|
||||
|
||||
path = [];
|
||||
|
||||
for (j = 0; j < depth; j++)
|
||||
path.push(pathdata.slice(j * 32, j * 32 + 32));
|
||||
|
||||
posdata = stack.pop();
|
||||
|
||||
if (posdata.length > 4)
|
||||
throw new ScriptError('WITNESS_PROGRAM_MISMATCH');
|
||||
|
||||
pos = 0;
|
||||
if (posdata.length > 0) {
|
||||
if (posdata[posdata.length - 1] === 0x00)
|
||||
throw new ScriptError('WITNESS_PROGRAM_MISMATCH');
|
||||
|
||||
for (j = 0; j < posdata.length; j++)
|
||||
pos |= posdata[i] << 8 * j;
|
||||
|
||||
if (pos < 0)
|
||||
pos += 0x100000000;
|
||||
}
|
||||
|
||||
if (depth < 32) {
|
||||
if (pos >= ((1 << depth) >>> 0))
|
||||
throw new ScriptError('WITNESS_PROGRAM_MISMATCH');
|
||||
}
|
||||
|
||||
root = crypto.checkMerkleBranch(hash, path, pos);
|
||||
|
||||
if (!utils.equal(root, program.data))
|
||||
throw new ScriptError('WITNESS_PROGRAM_MISMATCH');
|
||||
return Script.verifyMast(program, stack, output, flags, tx, i);
|
||||
} else {
|
||||
// Anyone can spend (we can return true here
|
||||
// if we want to always relay these transactions).
|
||||
@ -3342,6 +3292,140 @@ Script.verifyProgram = function verifyProgram(witness, output, flags, tx, i) {
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify a MAST witness program.
|
||||
* @param {Program} program
|
||||
* @param {Stack} stack
|
||||
* @param {Script} output
|
||||
* @param {VerifyFlags} flags
|
||||
* @param {TX} tx
|
||||
* @param {Number} i
|
||||
* @returns {Boolean}
|
||||
* @throws {ScriptError}
|
||||
*/
|
||||
|
||||
Script.verifyMast = function verifyMast(program, stack, output, flags, tx, i) {
|
||||
var mastRoot = new BufferWriter();
|
||||
var scriptRoot = new BufferWriter();
|
||||
var scripts = new BufferWriter();
|
||||
var version = 0;
|
||||
var pathdata, depth, path, posdata, pos;
|
||||
var metadata, subscripts, ops, script;
|
||||
var j;
|
||||
|
||||
assert(program.version === 1);
|
||||
assert((flags & constants.flags.VERIFY_MAST) !== 0);
|
||||
|
||||
if (stack.length < 4)
|
||||
throw new ScriptError('INVALID_MAST_STACK');
|
||||
|
||||
metadata = stack.pop();
|
||||
if (metadata.length < 1 || metadata.length > 5)
|
||||
throw new ScriptError('INVALID_MAST_STACK');
|
||||
|
||||
subscripts = metadata[0];
|
||||
if (subscripts === 0 || stack.length < subscripts + 2)
|
||||
throw new ScriptError('INVALID_MAST_STACK');
|
||||
|
||||
ops = subscripts;
|
||||
scriptRoot.writeU8(subscripts);
|
||||
|
||||
if (metadata[metadata.length - 1] === 0x00)
|
||||
throw new ScriptError('INVALID_MAST_STACK');
|
||||
|
||||
for (j = 1; j < metadata.length; j++)
|
||||
version |= metadata[i] << 8 * (j - 1);
|
||||
|
||||
if (version < 0)
|
||||
version += 0x100000000;
|
||||
|
||||
if (version > 0) {
|
||||
if (flags & constants.flags.DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)
|
||||
throw new ScriptError('DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM');
|
||||
}
|
||||
|
||||
mastRoot.writeU32(version);
|
||||
|
||||
pathdata = stack.pop();
|
||||
|
||||
if (pathdata.length & 0x1f)
|
||||
throw new ScriptError('INVALID_MAST_STACK');
|
||||
|
||||
depth = pathdata.length >>> 5;
|
||||
|
||||
if (depth > 32)
|
||||
throw new ScriptError('INVALID_MAST_STACK');
|
||||
|
||||
ops += depth;
|
||||
if (version === 0) {
|
||||
if (ops > constants.script.MAX_OPS)
|
||||
throw new ScriptError('OP_COUNT');
|
||||
}
|
||||
|
||||
path = [];
|
||||
|
||||
for (j = 0; j < depth; j++)
|
||||
path.push(pathdata.slice(j * 32, j * 32 + 32));
|
||||
|
||||
posdata = stack.pop();
|
||||
|
||||
if (posdata.length > 4)
|
||||
throw new ScriptError('INVALID_MAST_STACK');
|
||||
|
||||
pos = 0;
|
||||
if (posdata.length > 0) {
|
||||
if (posdata[posdata.length - 1] === 0x00)
|
||||
throw new ScriptError('INVALID_MAST_STACK');
|
||||
|
||||
for (j = 0; j < posdata.length; j++)
|
||||
pos |= posdata[i] << 8 * j;
|
||||
|
||||
if (pos < 0)
|
||||
pos += 0x100000000;
|
||||
}
|
||||
|
||||
if (depth < 32) {
|
||||
if (pos >= ((1 << depth) >>> 0))
|
||||
throw new ScriptError('INVALID_MAST_STACK');
|
||||
}
|
||||
|
||||
scripts.writeBytes(output.raw);
|
||||
|
||||
for (j = 0; j < subscripts; j++) {
|
||||
script = stack.pop();
|
||||
if (version === 0) {
|
||||
if ((scripts.written + script.length) > constants.script.MAX_SIZE)
|
||||
throw new ScriptError('SCRIPT_SIZE');
|
||||
}
|
||||
scriptRoot.writeBytes(crypto.hash256(script));
|
||||
scripts.writeBytes(script);
|
||||
}
|
||||
|
||||
scriptRoot = crypto.hash256(scriptRoot.render());
|
||||
scriptRoot = crypto.checkMerkleBranch(scriptRoot, path, pos);
|
||||
|
||||
mastRoot.writeBytes(scriptRoot);
|
||||
mastRoot = crypto.hash256(mastRoot.render());
|
||||
|
||||
if (!utils.equal(mastRoot, program.data))
|
||||
throw new ScriptError('WITNESS_PROGRAM_MISMATCH');
|
||||
|
||||
if (version === 0) {
|
||||
for (j = 0; j < stack.length; j++) {
|
||||
if (stack.get(j).length > constants.script.MAX_PUSH)
|
||||
throw new ScriptError('PUSH_SIZE');
|
||||
}
|
||||
}
|
||||
|
||||
output = new bcoin.script(scripts.render());
|
||||
output.execute(stack, flags, tx, i, 1);
|
||||
|
||||
if (stack.length !== 0)
|
||||
throw new ScriptError('EVAL_FALSE');
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify a signature, taking into account sighash type
|
||||
* and whether the signature is historical.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user