script: implement bip114v2.

This commit is contained in:
Christopher Jeffrey 2016-09-18 14:15:46 -07:00
parent 9561b3ee74
commit ab052eb81d
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
2 changed files with 138 additions and 54 deletions

View File

@ -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);
}

View File

@ -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.