456 lines
8.8 KiB
JavaScript
456 lines
8.8 KiB
JavaScript
'use strict';
|
|
|
|
const util = require('../lib/utils/util');
|
|
const Script = require('../lib/script/script');
|
|
const Stack = require('../lib/script/stack');
|
|
const Witness = require('../lib/script/witness');
|
|
const Input = require('../lib/primitives/input');
|
|
const Output = require('../lib/primitives/output');
|
|
const Outpoint = require('../lib/primitives/outpoint');
|
|
const TX = require('../lib/primitives/tx');
|
|
const random = require('../lib/crypto/random');
|
|
|
|
const MANDATORY = Script.flags.MANDATORY_VERIFY_FLAGS
|
|
| Script.flags.VERIFY_WITNESS;
|
|
const STANDARD = Script.flags.STANDARD_VERIFY_FLAGS;
|
|
|
|
function randomOutpoint() {
|
|
const hash = random.randomBytes(32).toString('hex');
|
|
return new Outpoint(hash, util.random(0, 0xffffffff));
|
|
}
|
|
|
|
function randomInput() {
|
|
const input = Input.fromOutpoint(randomOutpoint());
|
|
|
|
if (util.random(0, 5) === 0)
|
|
input.sequence = util.random(0, 0xffffffff);
|
|
|
|
return input;
|
|
}
|
|
|
|
function randomOutput() {
|
|
return Output.fromScript(randomScript(), util.random(0, 1e8));
|
|
}
|
|
|
|
function randomTX() {
|
|
const tx = new TX();
|
|
const inputs = util.random(1, 5);
|
|
const outputs = util.random(0, 5);
|
|
let i;
|
|
|
|
tx.version = util.random(0, 0xffffffff);
|
|
|
|
for (i = 0; i < inputs; i++)
|
|
tx.inputs.push(randomInput());
|
|
|
|
for (i = 0; i < outputs; i++)
|
|
tx.inputs.push(randomOutput());
|
|
|
|
if (util.random(0, 5) === 0)
|
|
tx.locktime = util.random(0, 0xffffffff);
|
|
|
|
tx.refresh();
|
|
|
|
return tx;
|
|
}
|
|
|
|
function randomWitness(redeem) {
|
|
const size = util.random(1, 100);
|
|
const witness = new Witness();
|
|
let i, len;
|
|
|
|
for (i = 0; i < size; i++) {
|
|
len = util.random(0, 100);
|
|
witness.push(random.randomBytes(len));
|
|
}
|
|
|
|
if (redeem)
|
|
witness.push(redeem);
|
|
|
|
witness.compile();
|
|
|
|
return witness;
|
|
}
|
|
|
|
function randomInputScript(redeem) {
|
|
const size = util.random(1, 100);
|
|
const script = new Script();
|
|
let i, len;
|
|
|
|
for (i = 0; i < size; i++) {
|
|
len = util.random(0, 100);
|
|
script.push(random.randomBytes(len));
|
|
}
|
|
|
|
if (redeem)
|
|
script.push(redeem);
|
|
|
|
script.compile();
|
|
|
|
return script;
|
|
}
|
|
|
|
function randomOutputScript() {
|
|
const size = util.random(1, 10000);
|
|
return Script.fromRaw(random.randomBytes(size));
|
|
}
|
|
|
|
function isPushOnly(script) {
|
|
let i, op;
|
|
|
|
if (script.isPushOnly())
|
|
return true;
|
|
|
|
for (i = 0; i < script.code.length; i++) {
|
|
op = script.code[i];
|
|
|
|
if (op.value === Script.opcodes.NOP)
|
|
continue;
|
|
|
|
if (op.value === Script.opcodes.NOP_1)
|
|
continue;
|
|
|
|
if (op.value > Script.opcodes.NOP_3)
|
|
continue;
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function randomPubkey() {
|
|
const len = util.random(0, 2) === 0 ? 33 : 65;
|
|
return Script.fromPubkey(random.randomBytes(len));
|
|
}
|
|
|
|
function randomPubkeyhash() {
|
|
return Script.fromPubkeyhash(random.randomBytes(20));
|
|
}
|
|
|
|
function randomMultisig() {
|
|
const n = util.random(1, 16);
|
|
const m = util.random(1, n);
|
|
const keys = [];
|
|
let i, len;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
len = util.random(0, 2) === 0 ? 33 : 65;
|
|
keys.push(random.randomBytes(len));
|
|
}
|
|
|
|
return Script.fromMultisig(m, n, keys);
|
|
}
|
|
|
|
function randomScripthash() {
|
|
return Script.fromScripthash(random.randomBytes(20));
|
|
}
|
|
|
|
function randomWitnessPubkeyhash() {
|
|
return Script.fromProgram(0, random.randomBytes(20));
|
|
}
|
|
|
|
function randomWitnessScripthash() {
|
|
return Script.fromProgram(0, random.randomBytes(32));
|
|
}
|
|
|
|
function randomProgram() {
|
|
const version = util.random(0, 16);
|
|
const size = util.random(2, 41);
|
|
return Script.fromProgram(version, random.randomBytes(size));
|
|
}
|
|
|
|
function randomRedeem() {
|
|
switch (util.random(0, 5)) {
|
|
case 0:
|
|
return randomPubkey();
|
|
case 1:
|
|
return randomPubkeyhash();
|
|
case 2:
|
|
return randomMultisig();
|
|
case 3:
|
|
return randomWitnessPubkeyhash();
|
|
case 4:
|
|
return randomProgram();
|
|
}
|
|
throw new Error();
|
|
}
|
|
|
|
function randomScript() {
|
|
switch (util.random(0, 7)) {
|
|
case 0:
|
|
return randomPubkey();
|
|
case 1:
|
|
return randomPubkeyhash();
|
|
case 2:
|
|
return randomMultisig();
|
|
case 3:
|
|
return randomScripthash();
|
|
case 4:
|
|
return randomWitnessPubkeyhash();
|
|
case 5:
|
|
return randomWitnessScripthash();
|
|
case 6:
|
|
return randomProgram();
|
|
}
|
|
throw new Error();
|
|
}
|
|
|
|
function randomPubkeyContext() {
|
|
return {
|
|
input: randomInputScript(),
|
|
witness: new Witness(),
|
|
output: randomPubkey(),
|
|
redeem: null
|
|
};
|
|
}
|
|
|
|
function randomPubkeyhashContext() {
|
|
return {
|
|
input: randomInputScript(),
|
|
witness: new Witness(),
|
|
output: randomPubkeyhash(),
|
|
redeem: null
|
|
};
|
|
}
|
|
|
|
function randomScripthashContext() {
|
|
const redeem = randomRedeem();
|
|
return {
|
|
input: randomInputScript(redeem.toRaw()),
|
|
witness: new Witness(),
|
|
output: Script.fromScripthash(redeem.hash160()),
|
|
redeem: redeem
|
|
};
|
|
}
|
|
|
|
function randomWitnessPubkeyhashContext() {
|
|
return {
|
|
input: new Script(),
|
|
witness: randomWitness(),
|
|
output: randomWitnessPubkeyhash(),
|
|
redeem: null
|
|
};
|
|
}
|
|
|
|
function randomWitnessScripthashContext() {
|
|
const redeem = randomRedeem();
|
|
return {
|
|
input: new Script(),
|
|
witness: randomWitness(redeem.toRaw()),
|
|
output: Script.fromProgram(0, redeem.sha256()),
|
|
redeem: redeem
|
|
};
|
|
}
|
|
|
|
function randomWitnessNestedContext() {
|
|
const redeem = randomRedeem();
|
|
const program = Script.fromProgram(0, redeem.sha256());
|
|
return {
|
|
input: new Script([program.toRaw()]),
|
|
witness: randomWitness(redeem.toRaw()),
|
|
output: Script.fromScripthash(program.hash160()),
|
|
redeem: redeem
|
|
};
|
|
}
|
|
|
|
function randomContext() {
|
|
switch (util.random(0, 6)) {
|
|
case 0:
|
|
return randomPubkeyContext();
|
|
case 1:
|
|
return randomPubkeyhashContext();
|
|
case 2:
|
|
return randomScripthashContext();
|
|
case 3:
|
|
return randomWitnessPubkeyhashContext();
|
|
case 4:
|
|
return randomWitnessScripthashContext();
|
|
case 5:
|
|
return randomWitnessNestedContext();
|
|
}
|
|
throw new Error();
|
|
}
|
|
|
|
function fuzzSimple(flags) {
|
|
let tx = randomTX();
|
|
let total = -1;
|
|
let stack, input, output;
|
|
|
|
for (;;) {
|
|
if (++total % 1000 === 0)
|
|
util.log('Fuzzed %d scripts.', total);
|
|
|
|
if (total % 500 === 0)
|
|
tx = randomTX();
|
|
|
|
stack = new Stack();
|
|
input = randomInputScript();
|
|
|
|
try {
|
|
input.execute(stack, flags, tx, 0, 0, 0);
|
|
} catch (e) {
|
|
if (e.type === 'ScriptError')
|
|
continue;
|
|
throw e;
|
|
}
|
|
|
|
output = randomOutputScript();
|
|
|
|
try {
|
|
output.execute(stack, flags, tx, 0, 0, 0);
|
|
} catch (e) {
|
|
if (e.type === 'ScriptError')
|
|
continue;
|
|
throw e;
|
|
}
|
|
|
|
if (stack.length === 0)
|
|
continue;
|
|
|
|
if (!stack.bool(-1))
|
|
continue;
|
|
|
|
if (isPushOnly(output))
|
|
continue;
|
|
|
|
util.log('Produced valid scripts:');
|
|
|
|
util.log('Input:');
|
|
util.log(input);
|
|
|
|
util.log('Output:');
|
|
util.log(output);
|
|
|
|
util.log('Stack:');
|
|
util.log(stack);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
function fuzzVerify(flags) {
|
|
let tx = randomTX();
|
|
let total = -1;
|
|
let input, output, witness;
|
|
|
|
for (;;) {
|
|
if (++total % 1000 === 0)
|
|
util.log('Fuzzed %d scripts.', total);
|
|
|
|
if (total % 500 === 0)
|
|
tx = randomTX();
|
|
|
|
input = randomInputScript();
|
|
witness = randomWitness();
|
|
output = randomOutputScript();
|
|
|
|
try {
|
|
Script.verify(
|
|
input,
|
|
witness,
|
|
output,
|
|
tx,
|
|
0,
|
|
0,
|
|
flags
|
|
);
|
|
} catch (e) {
|
|
if (e.type === 'ScriptError')
|
|
continue;
|
|
throw e;
|
|
}
|
|
|
|
if (isPushOnly(output))
|
|
continue;
|
|
|
|
util.log('Produced valid scripts:');
|
|
|
|
util.log('Input:');
|
|
util.log(input);
|
|
|
|
util.log('Witness:');
|
|
util.log(witness);
|
|
|
|
util.log('Output:');
|
|
util.log(output);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
function fuzzLess(flags) {
|
|
let tx = randomTX();
|
|
let total = -1;
|
|
let ctx;
|
|
|
|
for (;;) {
|
|
if (++total % 1000 === 0)
|
|
util.log('Fuzzed %d scripts.', total);
|
|
|
|
if (total % 500 === 0)
|
|
tx = randomTX();
|
|
|
|
ctx = randomContext();
|
|
|
|
try {
|
|
Script.verify(
|
|
ctx.input,
|
|
ctx.witness,
|
|
ctx.output,
|
|
tx,
|
|
0,
|
|
0,
|
|
flags
|
|
);
|
|
} catch (e) {
|
|
if (e.type === 'ScriptError')
|
|
continue;
|
|
throw e;
|
|
}
|
|
|
|
util.log('Produced valid scripts:');
|
|
|
|
util.log('Input:');
|
|
util.log(ctx.input);
|
|
|
|
util.log('Witness:');
|
|
util.log(ctx.witness);
|
|
|
|
util.log('Output:');
|
|
util.log(ctx.output);
|
|
|
|
if (ctx.redeem) {
|
|
util.log('Redeem:');
|
|
util.log(ctx.redeem);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
function main() {
|
|
const flags = process.argv.indexOf('--standard') !== -1
|
|
? STANDARD
|
|
: MANDATORY;
|
|
|
|
switch (process.argv[2]) {
|
|
case 'simple':
|
|
fuzzSimple(flags);
|
|
break;
|
|
case 'verify':
|
|
fuzzVerify(flags);
|
|
break;
|
|
case 'less':
|
|
fuzzLess(flags);
|
|
break;
|
|
default:
|
|
util.log('Please select a mode:');
|
|
util.log('simple, verify, less');
|
|
util.log('Optional `--standard` flag.');
|
|
break;
|
|
}
|
|
}
|
|
|
|
main();
|