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);
|
data = crypto.sha256(data);
|
||||||
} else if (version === 1) {
|
} else if (version === 1) {
|
||||||
assert(Array.isArray(data));
|
assert(Array.isArray(data));
|
||||||
data = crypto.getMerkleRoot(data);
|
throw new Error('MASTv2 creation not implemented.');
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Cannot create from version=' + version);
|
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 {VerifyFlags} flags
|
||||||
* @param {TX} tx
|
* @param {TX} tx
|
||||||
* @param {Number} i
|
* @param {Number} i
|
||||||
|
* @returns {Boolean}
|
||||||
* @throws {ScriptError}
|
* @throws {ScriptError}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Script.verifyProgram = function verifyProgram(witness, output, flags, tx, i) {
|
Script.verifyProgram = function verifyProgram(witness, output, flags, tx, i) {
|
||||||
var program = output.toProgram();
|
var program = output.toProgram();
|
||||||
var stack = witness.toStack();
|
var stack = witness.toStack();
|
||||||
var witnessScript, redeem, j;
|
var j, witnessScript, redeem;
|
||||||
var hash, pathdata, depth, path, posdata, pos, root;
|
|
||||||
|
|
||||||
assert(program, 'verifyProgram called on non-witness-program.');
|
assert(program, 'verifyProgram called on non-witness-program.');
|
||||||
assert((flags & constants.flags.VERIFY_WITNESS) !== 0);
|
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');
|
throw new ScriptError('WITNESS_PROGRAM_WRONG_LENGTH');
|
||||||
}
|
}
|
||||||
} else if ((flags & constants.flags.VERIFY_MAST) && program.version === 1) {
|
} else if ((flags & constants.flags.VERIFY_MAST) && program.version === 1) {
|
||||||
if (program.data.length !== 32)
|
return Script.verifyMast(program, stack, output, flags, tx, i);
|
||||||
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');
|
|
||||||
} else {
|
} else {
|
||||||
// Anyone can spend (we can return true here
|
// Anyone can spend (we can return true here
|
||||||
// if we want to always relay these transactions).
|
// if we want to always relay these transactions).
|
||||||
@ -3342,6 +3292,140 @@ Script.verifyProgram = function verifyProgram(witness, output, flags, tx, i) {
|
|||||||
return true;
|
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
|
* Verify a signature, taking into account sighash type
|
||||||
* and whether the signature is historical.
|
* and whether the signature is historical.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user