Verifying a spend
The Interpreter runs an Input’s Unlocking Script together with the Locking Script it spends, just as a node does. Running it before broadcasting catches wrong keys, wrong amounts and malformed scripts locally, before they ever reach the network.
The examples use the keys and the fundingTransaction helper described in About the examples.
Verifying an Input
Section titled “Verifying an Input”correctlySpends takes the Unlocking Script, the Locking Script, the Transaction, the Input index, the
verification flags and the amount of the Output being spent. It returns normally when the spend is valid, and
throws a ScriptException when it is not.
// spentOutput: the Output this Input spends (from the funding Transaction).bool inputIsValid(Transaction tx, int index, TransactionOutput spentOutput) { try { Interpreter().correctlySpends( tx.inputs[index].script!, spentOutput.script, tx, index, {VerifyFlag.SIGHASH_FORKID, VerifyFlag.UTXO_AFTER_GENESIS}, Coin.ofSat(spentOutput.satoshis), ); return true; } on ScriptException catch (e) { print('input $index rejected: ${e.error}'); // e.g. ScriptError.SCRIPT_ERR_EVAL_FALSE return false; }}We verify each Input separately, each against the Output it spends. This means we need the spent Outputs’ Locking Scripts and amounts. A Transaction does not contain them, so we either keep them from when we built it, or fetch them along with the funding Transactions.
The amount matters. With
SIGHASH_FORKIDthe Signature commits to it, so passing a different amount fails even for a correctly signed Input.
The flags {VerifyFlag.SIGHASH_FORKID, VerifyFlag.UTXO_AFTER_GENESIS} apply the current Bitcoin SV rules to an
Output created after Genesis. The Interpreter page lists the
other flags.
Why it failed
Section titled “Why it failed”ScriptException.error is a ScriptError value naming the rule that failed, and cause is a message.
try { Interpreter().correctlySpends(tx.inputs[0].script!, fundingTx.outputs[0].script, tx, 0, {VerifyFlag.SIGHASH_FORKID, VerifyFlag.UTXO_AFTER_GENESIS}, Coin.ofSat(fundingTx.outputs[0].satoshis));} on ScriptException catch (e) { e.error; // ScriptError.SCRIPT_ERR_EQUALVERIFY: the public key does not hash to the address e.cause; // a human-readable message}The common errors are:
SCRIPT_ERR_EQUALVERIFY: in P2PKH, the Public Key does not match the Address.SCRIPT_ERR_EVAL_FALSE: the scripts ran but left false, often because of a bad Signature.SCRIPT_ERR_INVALID_STACK_OPERATION: the Unlocking Script is missing items, often because the Input was never signed.
On failure the Interpreter prints a stack trace to the console before throwing. That output is noise, not a second error.
Structural checks
Section titled “Structural checks”Transaction.verify() runs the checks that need no context. It complements the Interpreter, but it does not
replace it.
// Context-free checks: Inputs and Outputs present, amounts in range, no duplicate Inputs.// It does not check Signatures or scripts.try { tx.verify();} on VerificationException catch (e) { print(e.cause);}
