From 628f78343df0a79d787132f164ae6234a8d99e38 Mon Sep 17 00:00:00 2001 From: mainnet-pat Date: Thu, 11 Sep 2025 08:50:41 +0000 Subject: [PATCH 1/8] Rework template building to support p2pkh-only inputs, replace privatekey with signature and public key --- packages/cashscript/src/LibauthTemplate.ts | 116 +- packages/cashscript/src/TransactionBuilder.ts | 6 - packages/cashscript/src/debugging.ts | 12 +- packages/cashscript/src/utils.ts | 17 + packages/cashscript/test/debugging.test.ts | 15 +- .../multi-contract-fixtures.ts | 1158 +++++++++++++---- 6 files changed, 1061 insertions(+), 263 deletions(-) diff --git a/packages/cashscript/src/LibauthTemplate.ts b/packages/cashscript/src/LibauthTemplate.ts index 3dbeae33..d3c033df 100644 --- a/packages/cashscript/src/LibauthTemplate.ts +++ b/packages/cashscript/src/LibauthTemplate.ts @@ -3,6 +3,7 @@ import { binToHex, decodeCashAddress, hexToBin, + Input, isHex, TransactionBch, utf8ToBin, @@ -43,7 +44,7 @@ import { Utxo, } from './interfaces.js'; import SignatureTemplate from './SignatureTemplate.js'; -import { addressToLockScript, extendedStringify, zip } from './utils.js'; +import { addressToLockScript, extendedStringify, getSignatureAndPubkeyFromP2PKHInput, zip } from './utils.js'; import { TransactionBuilder } from './TransactionBuilder.js'; import { deflate } from 'pako'; @@ -64,13 +65,18 @@ export const generateTemplateEntitiesP2PKH = ( return { [`signer_${inputIndex}`]: { scripts: [lockScriptName, unlockScriptName], - description: `placeholder_key_${inputIndex}`, + description: `P2PKH data for input ${inputIndex}`, name: `P2PKH Signer (input #${inputIndex})`, variables: { - [`placeholder_key_${inputIndex}`]: { + [`signature_${inputIndex}`]: { description: '', - name: `P2PKH Placeholder Key (input #${inputIndex})`, - type: 'Key', + name: `P2PKH Signature (input #${inputIndex})`, + type: 'WalletData', + }, + [`public_key_${inputIndex}`]: { + description: '', + name: `P2PKH public key (input #${inputIndex})`, + type: 'WalletData', }, }, }, @@ -152,30 +158,29 @@ const createWalletTemplateVariables = ( * */ export const generateTemplateScriptsP2PKH = ( - template: SignatureTemplate, inputIndex: number, ): WalletTemplate['scripts'] => { const scripts: WalletTemplate['scripts'] = {}; const lockScriptName = `p2pkh_placeholder_lock_${inputIndex}`; const unlockScriptName = `p2pkh_placeholder_unlock_${inputIndex}`; - const placeholderKeyName = `placeholder_key_${inputIndex}`; - const signatureAlgorithmName = getSignatureAlgorithmName(template.getSignatureAlgorithm()); - const hashtypeName = getHashTypeName(template.getHashType(false)); - const signatureString = `${placeholderKeyName}.${signatureAlgorithmName}.${hashtypeName}`; + const signatureString = `signature_${inputIndex}`; + const publicKeyString = `public_key_${inputIndex}`; + // add extra unlocking and locking script for P2PKH inputs spent alongside our contract // this is needed for correct cross-references in the template scripts[unlockScriptName] = { + passes: [`P2PKH_spend_input${inputIndex}_evaluate`], name: `P2PKH Unlock (input #${inputIndex})`, script: - `<${signatureString}>\n<${placeholderKeyName}.public_key>`, + `<${signatureString}>\n<${publicKeyString}>`, unlocks: lockScriptName, }; scripts[lockScriptName] = { lockingType: 'standard', name: `P2PKH Lock (input #${inputIndex})`, script: - `OP_DUP\nOP_HASH160 <$(<${placeholderKeyName}.public_key> OP_HASH160\n)> OP_EQUALVERIFY\nOP_CHECKSIG`, + `OP_DUP\nOP_HASH160 <$(<${publicKeyString}> OP_HASH160\n)> OP_EQUALVERIFY\nOP_CHECKSIG`, }; return scripts; @@ -293,7 +298,7 @@ export const generateTemplateScenarios = ( }, }, transaction: generateTemplateScenarioTransaction(contract, libauthTransaction, csTransaction, inputIndex), - sourceOutputs: generateTemplateScenarioSourceOutputs(csTransaction, inputIndex), + sourceOutputs: generateTemplateScenarioSourceOutputs(csTransaction, libauthTransaction, inputIndex), }, }; @@ -308,20 +313,54 @@ export const generateTemplateScenarios = ( return scenarios; }; +export const generateTemplateScenariosP2PKH = ( + libauthTransaction: TransactionBch, + csTransaction: TransactionType, + inputIndex: number, +): WalletTemplate['scenarios'] => { + // const artifact = contract.artifact; + // const encodedConstructorArgs = contract.encodedConstructorArgs; + const scenarioIdentifier = `P2PKH_spend_input${inputIndex}_evaluate`; + + const { signature, publicKey } = getSignatureAndPubkeyFromP2PKHInput(libauthTransaction.inputs[inputIndex]); + + const scenarios = { + // single scenario to spend out transaction under test given the CashScript parameters provided + [scenarioIdentifier]: { + name: `Evaluate P2PKH spend (input #${inputIndex})`, + description: 'An example evaluation where this script execution passes.', + data: { + // encode values for the variables defined above in `entities` property + bytecode: { + [`signature_${inputIndex}`]: `0x${binToHex(signature)}`, + [`public_key_${inputIndex}`]: `0x${binToHex(publicKey)}`, + }, + currentBlockHeight: 2, + currentBlockTime: Math.round(+new Date() / 1000), + }, + transaction: generateTemplateScenarioTransaction(undefined, libauthTransaction, csTransaction, inputIndex), + sourceOutputs: generateTemplateScenarioSourceOutputs(csTransaction, libauthTransaction, inputIndex), + }, + }; + + return scenarios; +}; + const generateTemplateScenarioTransaction = ( - contract: Contract, + contract: Contract | undefined, libauthTransaction: TransactionBch, csTransaction: TransactionType, slotIndex: number, ): WalletTemplateScenario['transaction'] => { const inputs = libauthTransaction.inputs.map((input, inputIndex) => { const csInput = csTransaction.inputs[inputIndex] as Utxo; + const libauthInput = libauthTransaction.inputs[inputIndex]; return { outpointIndex: input.outpointIndex, outpointTransactionHash: binToHex(input.outpointTransactionHash), sequenceNumber: input.sequenceNumber, - unlockingBytecode: generateTemplateScenarioBytecode(csInput, inputIndex, 'p2pkh_placeholder_unlock', slotIndex === inputIndex), + unlockingBytecode: generateTemplateScenarioBytecode(csInput, libauthInput, inputIndex, 'p2pkh_placeholder_unlock', slotIndex === inputIndex), } as WalletTemplateScenarioInput; }); @@ -330,8 +369,16 @@ const generateTemplateScenarioTransaction = ( const outputs = libauthTransaction.outputs.map((output, index) => { const csOutput = csTransaction.outputs[index]; + if (csOutput && contract) { + return { + lockingBytecode: generateTemplateScenarioTransactionOutputLockingBytecode(csOutput, contract), + token: serialiseTokenDetails(output.token), + valueSatoshis: Number(output.valueSatoshis), + } as WalletTemplateScenarioTransactionOutput; + } + return { - lockingBytecode: generateTemplateScenarioTransactionOutputLockingBytecode(csOutput, contract), + lockingBytecode: `${binToHex(output.lockingBytecode)}`, token: serialiseTokenDetails(output.token), valueSatoshis: Number(output.valueSatoshis), } as WalletTemplateScenarioTransactionOutput; @@ -344,11 +391,14 @@ const generateTemplateScenarioTransaction = ( const generateTemplateScenarioSourceOutputs = ( csTransaction: TransactionType, + libauthTransaction: TransactionBch, slotIndex: number, ): Array> => { return csTransaction.inputs.map((input, inputIndex) => { + const libauthInput = libauthTransaction.inputs[inputIndex]; + return { - lockingBytecode: generateTemplateScenarioBytecode(input, inputIndex, 'p2pkh_placeholder_lock', inputIndex === slotIndex), + lockingBytecode: generateTemplateScenarioBytecode(input, libauthInput, inputIndex, 'p2pkh_placeholder_lock', inputIndex === slotIndex), valueSatoshis: Number(input.satoshis), token: serialiseTokenDetails(input.token), }; @@ -414,7 +464,9 @@ export const getLibauthTemplates = ( // @ts-ignore TODO: Remove UtxoP2PKH type and only use UnlockableUtxo in Libauth Template generation input.template = input.unlocker?.template; // Added to support P2PKH inputs in buildTemplate Object.assign(p2pkhEntities, generateTemplateEntitiesP2PKH(inputIndex)); - Object.assign(p2pkhScripts, generateTemplateScriptsP2PKH(input.unlocker.template, inputIndex)); + Object.assign(p2pkhScripts, generateTemplateScriptsP2PKH(inputIndex)); + + Object.assign(scenarios, generateTemplateScenariosP2PKH(libauthTransaction, csTransaction as any, inputIndex)); continue; } @@ -575,17 +627,19 @@ const generateLockingScriptParams = ( export const generateUnlockingScriptParams = ( csInput: StandardUnlockableUtxo, + libauthInput: Input, p2pkhScriptNameTemplate: string, inputIndex: number, ): WalletTemplateScenarioBytecode => { if (isP2PKHUnlocker(csInput.unlocker)) { + const { signature, publicKey } = getSignatureAndPubkeyFromP2PKHInput(libauthInput); + return { script: `${p2pkhScriptNameTemplate}_${inputIndex}`, overrides: { - keys: { - privateKeys: { - [`placeholder_key_${inputIndex}`]: binToHex(csInput.unlocker.template.privateKey), - }, + bytecode: { + [`signature_${inputIndex}`]: `0x${binToHex(signature)}`, + [`public_key_${inputIndex}`]: `0x${binToHex(publicKey)}`, }, }, }; @@ -745,29 +799,33 @@ export const generateTemplateScenarioKeys = ( // Used for generating the locking / unlocking bytecode for source outputs and inputs export const generateTemplateScenarioBytecode = ( - input: Utxo, inputIndex: number, p2pkhScriptNameTemplate: string, insertSlot?: boolean, + input: Utxo, + libauthInput: Input, + inputIndex: number, + p2pkhScriptNameTemplate: string, + insertSlot?: boolean, ): WalletTemplateScenarioBytecode | ['slot'] => { if (insertSlot) return ['slot']; const p2pkhScriptName = `${p2pkhScriptNameTemplate}_${inputIndex}`; - const placeholderKeyName = `placeholder_key_${inputIndex}`; // This is for P2PKH inputs in the old transaction builder (TODO: remove when we remove old transaction builder) if (isUtxoP2PKH(input)) { + const { signature, publicKey } = getSignatureAndPubkeyFromP2PKHInput(libauthInput); + return { script: p2pkhScriptName, overrides: { - keys: { - privateKeys: { - [placeholderKeyName]: binToHex(input.template.privateKey), - }, + bytecode: { + [`signature_${inputIndex}`]: `0x${binToHex(signature)}`, + [`public_key_${inputIndex}`]: `0x${binToHex(publicKey)}`, }, }, }; } if (isUnlockableUtxo(input) && isStandardUnlockableUtxo(input)) { - return generateUnlockingScriptParams(input, p2pkhScriptNameTemplate, inputIndex); + return generateUnlockingScriptParams(input, libauthInput, p2pkhScriptNameTemplate, inputIndex); } // 'slot' means that we are currently evaluating this specific input, diff --git a/packages/cashscript/src/TransactionBuilder.ts b/packages/cashscript/src/TransactionBuilder.ts index 4a8303d3..32cac564 100644 --- a/packages/cashscript/src/TransactionBuilder.ts +++ b/packages/cashscript/src/TransactionBuilder.ts @@ -17,7 +17,6 @@ import { isUnlockableUtxo, isStandardUnlockableUtxo, StandardUnlockableUtxo, - isP2PKHUnlocker, } from './interfaces.js'; import { NetworkProvider } from './network/index.js'; import { @@ -157,11 +156,6 @@ export class TransactionBuilder { } debug(): DebugResults { - // do not debug a pure P2PKH-spend transaction - if (this.inputs.every((input) => isP2PKHUnlocker(input.unlocker))) { - return {}; - } - if (this.inputs.some((input) => !isStandardUnlockableUtxo(input))) { throw new Error('Cannot debug a transaction with custom unlocker'); } diff --git a/packages/cashscript/src/debugging.ts b/packages/cashscript/src/debugging.ts index 15a6daba..11ae6865 100644 --- a/packages/cashscript/src/debugging.ts +++ b/packages/cashscript/src/debugging.ts @@ -25,15 +25,9 @@ export const debugTemplate = (template: WalletTemplate, artifacts: Artifact[]): for (const unlockingScriptId of unlockingScriptIds) { const scenarioIds = (template.scripts[unlockingScriptId] as WalletTemplateScriptUnlocking).passes ?? []; - // There are no scenarios defined for P2PKH placeholder scripts, so we skip them - if (scenarioIds.length === 0) continue; const matchingArtifact = artifacts.find((artifact) => unlockingScriptId.startsWith(artifact.contractName)); - if (!matchingArtifact) { - throw new Error(`No artifact found for unlocking script ${unlockingScriptId}`); - } - for (const scenarioId of scenarioIds) { results[`${unlockingScriptId}.${scenarioId}`] = debugSingleScenario(template, matchingArtifact, unlockingScriptId, scenarioId); } @@ -45,12 +39,16 @@ export const debugTemplate = (template: WalletTemplate, artifacts: Artifact[]): }; const debugSingleScenario = ( - template: WalletTemplate, artifact: Artifact, unlockingScriptId: string, scenarioId: string, + template: WalletTemplate, artifact: Artifact | undefined, unlockingScriptId: string, scenarioId: string, ): DebugResult => { const { vm, program } = createProgram(template, unlockingScriptId, scenarioId); const fullDebugSteps = vm.debug(program); + if (!artifact) { + return fullDebugSteps; + } + // P2SH executions have 3 phases, we only want the last one (locking script execution) // https://libauth.org/types/AuthenticationVirtualMachine.html#__type.debug const lockingScriptDebugResult = fullDebugSteps.slice(findLastIndex(fullDebugSteps, (state) => state.ip === 0)); diff --git a/packages/cashscript/src/utils.ts b/packages/cashscript/src/utils.ts index 8b23ff41..23c61df3 100644 --- a/packages/cashscript/src/utils.ts +++ b/packages/cashscript/src/utils.ts @@ -14,6 +14,11 @@ import { bigIntToCompactUint, NonFungibleTokenCapability, bigIntToVmNumber, + assertSuccess, + AuthenticationInstructionPush, + AuthenticationInstructions, + decodeAuthenticationInstructions, + Input, } from '@bitauth/libauth'; import { encodeInt, @@ -368,3 +373,15 @@ export const isFungibleTokenUtxo = (utxo: Utxo): boolean => ( export const isNonTokenUtxo = (utxo: Utxo): boolean => utxo.token === undefined; export const delay = (ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)); + +export const getSignatureAndPubkeyFromP2PKHInput = ( + libauthInput: Input, +): { signature: Uint8Array; publicKey: Uint8Array } => { + const inputData = (assertSuccess( + decodeAuthenticationInstructions(libauthInput.unlockingBytecode)) as AuthenticationInstructions + ) as AuthenticationInstructionPush[]; + const signature = inputData[0].data; + const publicKey = inputData[1].data; + + return { signature, publicKey }; +}; diff --git a/packages/cashscript/test/debugging.test.ts b/packages/cashscript/test/debugging.test.ts index adc394c1..fd97bd1e 100644 --- a/packages/cashscript/test/debugging.test.ts +++ b/packages/cashscript/test/debugging.test.ts @@ -1,5 +1,5 @@ import { Contract, MockNetworkProvider, SignatureAlgorithm, SignatureTemplate, TransactionBuilder } from '../src/index.js'; -import { alicePriv, alicePub, bobPriv, bobPub } from './fixture/vars.js'; +import { aliceAddress, alicePriv, alicePub, bobPriv, bobPub } from './fixture/vars.js'; import '../src/test/JestExtensions.js'; import { randomUtxo } from '../src/utils.js'; import { AuthenticationErrorCommon, binToHex, hexToBin } from '@bitauth/libauth'; @@ -620,4 +620,17 @@ describe('Debugging tests', () => { ).toThrow(/Contract function failed a require statement\.*\nReceived string: (.|\n)*?1 should equal 2/); }); }); + + describe('P2PKH only transaction', () => { + it('should debug a transaction with only P2PKH inputs', async () => { + const provider = new MockNetworkProvider(); + + const result = new TransactionBuilder({ provider }) + .addInputs(await provider.getUtxos(aliceAddress), new SignatureTemplate(alicePriv).unlockP2PKH()) + .addOutput({ to: aliceAddress, amount: 5000n }) + .debug(); + + expect(Object.keys(result).length).toBeGreaterThan(0); + }); + }); }); diff --git a/packages/cashscript/test/fixture/libauth-template/multi-contract-fixtures.ts b/packages/cashscript/test/fixture/libauth-template/multi-contract-fixtures.ts index 1631b1dc..f4488599 100644 --- a/packages/cashscript/test/fixture/libauth-template/multi-contract-fixtures.ts +++ b/packages/cashscript/test/fixture/libauth-template/multi-contract-fixtures.ts @@ -167,122 +167,866 @@ export const fixtures: Fixture[] = [ 'p2pkh_placeholder_lock_0', 'p2pkh_placeholder_unlock_0', ], - 'description': 'placeholder_key_0', + 'description': 'P2PKH data for input 0', 'name': 'P2PKH Signer (input #0)', 'variables': { - 'placeholder_key_0': { + 'signature_0': { 'description': '', - 'name': 'P2PKH Placeholder Key (input #0)', - 'type': 'Key', + 'name': 'P2PKH Signature (input #0)', + 'type': 'WalletData', + }, + 'public_key_0': { + 'description': '', + 'name': 'P2PKH public key (input #0)', + 'type': 'WalletData', + }, + }, + }, + 'signer_1': { + 'scripts': [ + 'p2pkh_placeholder_lock_1', + 'p2pkh_placeholder_unlock_1', + ], + 'description': 'P2PKH data for input 1', + 'name': 'P2PKH Signer (input #1)', + 'variables': { + 'signature_1': { + 'description': '', + 'name': 'P2PKH Signature (input #1)', + 'type': 'WalletData', + }, + 'public_key_1': { + 'description': '', + 'name': 'P2PKH public key (input #1)', + 'type': 'WalletData', + }, + }, + }, + 'signer_2': { + 'scripts': [ + 'p2pkh_placeholder_lock_2', + 'p2pkh_placeholder_unlock_2', + ], + 'description': 'P2PKH data for input 2', + 'name': 'P2PKH Signer (input #2)', + 'variables': { + 'signature_2': { + 'description': '', + 'name': 'P2PKH Signature (input #2)', + 'type': 'WalletData', + }, + 'public_key_2': { + 'description': '', + 'name': 'P2PKH public key (input #2)', + 'type': 'WalletData', + }, + }, + }, + }, + 'scripts': { + 'Bar_funcA_input3_unlock': { + 'passes': [ + 'Bar_funcA_input3_evaluate', + ], + 'name': 'funcA (input #3)', + 'script': '// "funcA" function parameters\n// none\n\n// function index in contract\n // int = <0>\n', + 'unlocks': 'Bar_dfa9a690eb3692ca0655d91a1bebf908bd27f73faf31ec7fe316bde6c0fbed2e_lock', + }, + 'Bar_dfa9a690eb3692ca0655d91a1bebf908bd27f73faf31ec7fe316bde6c0fbed2e_lock': { + 'lockingType': 'p2sh32', + 'name': 'Bar', + 'script': "// \"Bar\" contract constructor parameters\n // bytes20 = <0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970>\n\n// bytecode\n /* pragma cashscript >=0.10.2; */\n /* */\n /* contract Bar(bytes20 pkh_bar) { */\nOP_OVER OP_0 OP_NUMEQUAL OP_IF /* function funcA() { */\nOP_2 OP_2 OP_NUMEQUAL /* require(2==2); */\nOP_NIP OP_NIP OP_ELSE /* } */\n /* */\nOP_OVER OP_1 OP_NUMEQUAL OP_IF /* function funcB() { */\nOP_2 OP_2 OP_NUMEQUAL /* require(2==2); */\nOP_NIP OP_NIP OP_ELSE /* } */\n /* */\nOP_SWAP OP_2 OP_NUMEQUALVERIFY /* function execute(pubkey pk, sig s) { */\n /* console.log(\"Bar 'execute' function called.\"); */\nOP_OVER OP_HASH160 OP_EQUALVERIFY /* require(hash160(pk) == pkh_bar); */\nOP_CHECKSIG /* require(checkSig(s, pk)); */\n /* } */\nOP_ENDIF OP_ENDIF /* } */\n /* */", + }, + 'Bar_execute_input4_unlock': { + 'passes': [ + 'Bar_execute_input4_evaluate', + ], + 'name': 'execute (input #4)', + 'script': '// "execute" function parameters\n // sig\n // pubkey = <0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088>\n\n// function index in contract\n // int = <2>\n', + 'unlocks': 'Bar_dfa9a690eb3692ca0655d91a1bebf908bd27f73faf31ec7fe316bde6c0fbed2e_lock', + }, + 'Foo_execute_input5_unlock': { + 'passes': [ + 'Foo_execute_input5_evaluate', + ], + 'name': 'execute (input #5)', + 'script': '// "execute" function parameters\n // sig\n // pubkey = <0x028f1219c918234d6bb06b4782354ff0759bd73036f3c849b88020c79fe013cd38>\n', + 'unlocks': 'Foo_432c93902a8a8e49ef246028e707cddaa67a39af46b2b3c11c196cd09c931746_lock', + }, + 'Foo_432c93902a8a8e49ef246028e707cddaa67a39af46b2b3c11c196cd09c931746_lock': { + 'lockingType': 'p2sh32', + 'name': 'Foo', + 'script': "// \"Foo\" contract constructor parameters\n // bytes20 = <0xb40a2013337edb0dfe307f0a57d5dec5bfe60dd0>\n\n// bytecode\n /* pragma cashscript >=0.10.2; */\n /* */\n /* contract Foo(bytes20 pkh_foo) { */\n /* // Require pk to match stored pkh and signature to match */\n /* function execute(pubkey pk, sig s) { */\n /* console.log(\"Foo 'execute' function called.\"); */\nOP_OVER OP_HASH160 OP_EQUALVERIFY /* require(hash160(pk) == pkh_foo); */\nOP_CHECKSIG /* require(checkSig(s, pk)); */\n /* } */\n /* } */\n /* */", + }, + 'Bar_funcB_input6_unlock': { + 'passes': [ + 'Bar_funcB_input6_evaluate', + ], + 'name': 'funcB (input #6)', + 'script': '// "funcB" function parameters\n// none\n\n// function index in contract\n // int = <1>\n', + 'unlocks': 'Bar_dfa9a690eb3692ca0655d91a1bebf908bd27f73faf31ec7fe316bde6c0fbed2e_lock', + }, + 'p2pkh_placeholder_unlock_0': { + 'passes': [ + 'P2PKH_spend_input0_evaluate', + ], + 'name': 'P2PKH Unlock (input #0)', + 'script': '\n', + 'unlocks': 'p2pkh_placeholder_lock_0', + }, + 'p2pkh_placeholder_lock_0': { + 'lockingType': 'standard', + 'name': 'P2PKH Lock (input #0)', + 'script': 'OP_DUP\nOP_HASH160 <$( OP_HASH160\n)> OP_EQUALVERIFY\nOP_CHECKSIG', + }, + 'p2pkh_placeholder_unlock_1': { + 'passes': [ + 'P2PKH_spend_input1_evaluate', + ], + 'name': 'P2PKH Unlock (input #1)', + 'script': '\n', + 'unlocks': 'p2pkh_placeholder_lock_1', + }, + 'p2pkh_placeholder_lock_1': { + 'lockingType': 'standard', + 'name': 'P2PKH Lock (input #1)', + 'script': 'OP_DUP\nOP_HASH160 <$( OP_HASH160\n)> OP_EQUALVERIFY\nOP_CHECKSIG', + }, + 'p2pkh_placeholder_unlock_2': { + 'passes': [ + 'P2PKH_spend_input2_evaluate', + ], + 'name': 'P2PKH Unlock (input #2)', + 'script': '\n', + 'unlocks': 'p2pkh_placeholder_lock_2', + }, + 'p2pkh_placeholder_lock_2': { + 'lockingType': 'standard', + 'name': 'P2PKH Lock (input #2)', + 'script': 'OP_DUP\nOP_HASH160 <$( OP_HASH160\n)> OP_EQUALVERIFY\nOP_CHECKSIG', + }, + }, + 'scenarios': { + 'P2PKH_spend_input0_evaluate': { + 'name': 'Evaluate P2PKH spend (input #0)', + 'description': 'An example evaluation where this script execution passes.', + 'data': { + 'bytecode': { + 'signature_0': expect.any(String), + 'public_key_0': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + }, + 'currentBlockHeight': 2, + 'currentBlockTime': expect.any(Number), + }, + 'transaction': { + 'inputs': [ + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.any(String), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': [ + 'slot', + ], + }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.any(String), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'p2pkh_placeholder_unlock_1', + 'overrides': { + 'bytecode': { + 'signature_1': expect.any(String), + 'public_key_1': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + }, + }, + }, + }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.any(String), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'p2pkh_placeholder_unlock_2', + 'overrides': { + 'bytecode': { + 'signature_2': expect.any(String), + 'public_key_2': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + }, + }, + }, + }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.any(String), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'Bar_funcA_input3_unlock', + 'overrides': { + 'bytecode': { + 'function_index': '0', + 'pkh_bar': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + 'keys': { + 'privateKeys': {}, + }, + }, + }, + }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.any(String), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'Bar_execute_input4_unlock', + 'overrides': { + 'bytecode': { + 'function_index': '2', + 'pk': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + 'pkh_bar': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + 'keys': { + 'privateKeys': { + 's': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', + }, + }, + }, + }, + }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.any(String), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'Foo_execute_input5_unlock', + 'overrides': { + 'bytecode': { + 'pk': '0x028f1219c918234d6bb06b4782354ff0759bd73036f3c849b88020c79fe013cd38', + 'pkh_foo': '0xb40a2013337edb0dfe307f0a57d5dec5bfe60dd0', + }, + 'keys': { + 'privateKeys': { + 's': '71080d8b52ec7b12adaec909ed54cd989b682ce2c35647eec219a16f5f90c528', + }, + }, + }, + }, + }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.any(String), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'Bar_funcB_input6_unlock', + 'overrides': { + 'bytecode': { + 'function_index': '1', + 'pkh_bar': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + 'keys': { + 'privateKeys': {}, + }, + }, + }, + }, + ], + 'locktime': 0, + 'outputs': [ + { + 'lockingBytecode': { + 'script': 'Foo_432c93902a8a8e49ef246028e707cddaa67a39af46b2b3c11c196cd09c931746_lock', + 'overrides': { + 'bytecode': { + 'pkh_foo': '0xb40a2013337edb0dfe307f0a57d5dec5bfe60dd0', + }, + }, + }, + 'valueSatoshis': 8000, + }, + { + 'lockingBytecode': '76a914512dbb2c8c02efbac8d92431aa0ac33f6b0bf97088ac', + 'token': { + 'amount': '100000000', + 'category': expect.any(String), + }, + 'valueSatoshis': 800, + }, + { + 'lockingBytecode': '76a914512dbb2c8c02efbac8d92431aa0ac33f6b0bf97088ac', + 'token': { + 'amount': '0', + 'category': expect.any(String), + 'nft': { + 'capability': 'minting', + 'commitment': '00', + }, + }, + 'valueSatoshis': 1000, + }, + { + 'lockingBytecode': '6a0568656c6c6f05776f726c64', + 'valueSatoshis': 0, + }, + ], + 'version': 2, + }, + 'sourceOutputs': [ + { + 'lockingBytecode': [ + 'slot', + ], + 'valueSatoshis': expect.any(Number), + }, + { + 'lockingBytecode': { + 'script': 'p2pkh_placeholder_lock_1', + 'overrides': { + 'bytecode': { + 'signature_1': expect.any(String), + 'public_key_1': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + }, + }, + }, + 'valueSatoshis': expect.any(Number), + }, + { + 'lockingBytecode': { + 'script': 'p2pkh_placeholder_lock_2', + 'overrides': { + 'bytecode': { + 'signature_2': expect.any(String), + 'public_key_2': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + }, + }, + }, + 'valueSatoshis': expect.any(Number), + }, + { + 'lockingBytecode': { + 'script': 'Bar_dfa9a690eb3692ca0655d91a1bebf908bd27f73faf31ec7fe316bde6c0fbed2e_lock', + 'overrides': { + 'bytecode': { + 'pkh_bar': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + }, + }, + 'valueSatoshis': expect.any(Number), + }, + { + 'lockingBytecode': { + 'script': 'Bar_dfa9a690eb3692ca0655d91a1bebf908bd27f73faf31ec7fe316bde6c0fbed2e_lock', + 'overrides': { + 'bytecode': { + 'pkh_bar': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + }, + }, + 'valueSatoshis': expect.any(Number), + }, + { + 'lockingBytecode': { + 'script': 'Foo_432c93902a8a8e49ef246028e707cddaa67a39af46b2b3c11c196cd09c931746_lock', + 'overrides': { + 'bytecode': { + 'pkh_foo': '0xb40a2013337edb0dfe307f0a57d5dec5bfe60dd0', + }, + }, + }, + 'valueSatoshis': expect.any(Number), + }, + { + 'lockingBytecode': { + 'script': 'Bar_dfa9a690eb3692ca0655d91a1bebf908bd27f73faf31ec7fe316bde6c0fbed2e_lock', + 'overrides': { + 'bytecode': { + 'pkh_bar': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + }, + }, + 'valueSatoshis': expect.any(Number), + }, + ], + }, + 'P2PKH_spend_input1_evaluate': { + 'name': 'Evaluate P2PKH spend (input #1)', + 'description': 'An example evaluation where this script execution passes.', + 'data': { + 'bytecode': { + 'signature_1': expect.any(String), + 'public_key_1': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + }, + 'currentBlockHeight': 2, + 'currentBlockTime': expect.any(Number), + }, + 'transaction': { + 'inputs': [ + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.any(String), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'p2pkh_placeholder_unlock_0', + 'overrides': { + 'bytecode': { + 'signature_0': expect.any(String), + 'public_key_0': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + }, + }, + }, + }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.any(String), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': [ + 'slot', + ], + }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.any(String), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'p2pkh_placeholder_unlock_2', + 'overrides': { + 'bytecode': { + 'signature_2': expect.any(String), + 'public_key_2': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + }, + }, + }, + }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.any(String), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'Bar_funcA_input3_unlock', + 'overrides': { + 'bytecode': { + 'function_index': '0', + 'pkh_bar': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + 'keys': { + 'privateKeys': {}, + }, + }, + }, + }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.any(String), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'Bar_execute_input4_unlock', + 'overrides': { + 'bytecode': { + 'function_index': '2', + 'pk': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + 'pkh_bar': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + 'keys': { + 'privateKeys': { + 's': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', + }, + }, + }, + }, + }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.any(String), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'Foo_execute_input5_unlock', + 'overrides': { + 'bytecode': { + 'pk': '0x028f1219c918234d6bb06b4782354ff0759bd73036f3c849b88020c79fe013cd38', + 'pkh_foo': '0xb40a2013337edb0dfe307f0a57d5dec5bfe60dd0', + }, + 'keys': { + 'privateKeys': { + 's': '71080d8b52ec7b12adaec909ed54cd989b682ce2c35647eec219a16f5f90c528', + }, + }, + }, + }, + }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.any(String), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'Bar_funcB_input6_unlock', + 'overrides': { + 'bytecode': { + 'function_index': '1', + 'pkh_bar': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + 'keys': { + 'privateKeys': {}, + }, + }, + }, + }, + ], + 'locktime': 0, + 'outputs': [ + { + 'lockingBytecode': { + 'script': 'Foo_432c93902a8a8e49ef246028e707cddaa67a39af46b2b3c11c196cd09c931746_lock', + 'overrides': { + 'bytecode': { + 'pkh_foo': '0xb40a2013337edb0dfe307f0a57d5dec5bfe60dd0', + }, + }, + }, + 'valueSatoshis': 8000, + }, + { + 'lockingBytecode': '76a914512dbb2c8c02efbac8d92431aa0ac33f6b0bf97088ac', + 'token': { + 'amount': '100000000', + 'category': expect.any(String), + }, + 'valueSatoshis': 800, + }, + { + 'lockingBytecode': '76a914512dbb2c8c02efbac8d92431aa0ac33f6b0bf97088ac', + 'token': { + 'amount': '0', + 'category': expect.any(String), + 'nft': { + 'capability': 'minting', + 'commitment': '00', + }, + }, + 'valueSatoshis': 1000, + }, + { + 'lockingBytecode': '6a0568656c6c6f05776f726c64', + 'valueSatoshis': 0, + }, + ], + 'version': 2, + }, + 'sourceOutputs': [ + { + 'lockingBytecode': { + 'script': 'p2pkh_placeholder_lock_0', + 'overrides': { + 'bytecode': { + 'signature_0': expect.any(String), + 'public_key_0': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + }, + }, + }, + 'valueSatoshis': expect.any(Number), + }, + { + 'lockingBytecode': [ + 'slot', + ], + 'valueSatoshis': expect.any(Number), + }, + { + 'lockingBytecode': { + 'script': 'p2pkh_placeholder_lock_2', + 'overrides': { + 'bytecode': { + 'signature_2': expect.any(String), + 'public_key_2': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + }, + }, + }, + 'valueSatoshis': expect.any(Number), + }, + { + 'lockingBytecode': { + 'script': 'Bar_dfa9a690eb3692ca0655d91a1bebf908bd27f73faf31ec7fe316bde6c0fbed2e_lock', + 'overrides': { + 'bytecode': { + 'pkh_bar': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + }, + }, + 'valueSatoshis': expect.any(Number), + }, + { + 'lockingBytecode': { + 'script': 'Bar_dfa9a690eb3692ca0655d91a1bebf908bd27f73faf31ec7fe316bde6c0fbed2e_lock', + 'overrides': { + 'bytecode': { + 'pkh_bar': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + }, + }, + 'valueSatoshis': expect.any(Number), + }, + { + 'lockingBytecode': { + 'script': 'Foo_432c93902a8a8e49ef246028e707cddaa67a39af46b2b3c11c196cd09c931746_lock', + 'overrides': { + 'bytecode': { + 'pkh_foo': '0xb40a2013337edb0dfe307f0a57d5dec5bfe60dd0', + }, + }, + }, + 'valueSatoshis': expect.any(Number), + }, + { + 'lockingBytecode': { + 'script': 'Bar_dfa9a690eb3692ca0655d91a1bebf908bd27f73faf31ec7fe316bde6c0fbed2e_lock', + 'overrides': { + 'bytecode': { + 'pkh_bar': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + }, + }, + 'valueSatoshis': expect.any(Number), + }, + ], + }, + 'P2PKH_spend_input2_evaluate': { + 'name': 'Evaluate P2PKH spend (input #2)', + 'description': 'An example evaluation where this script execution passes.', + 'data': { + 'bytecode': { + 'signature_2': expect.any(String), + 'public_key_2': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + }, + 'currentBlockHeight': 2, + 'currentBlockTime': expect.any(Number), + }, + 'transaction': { + 'inputs': [ + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.any(String), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'p2pkh_placeholder_unlock_0', + 'overrides': { + 'bytecode': { + 'signature_0': expect.any(String), + 'public_key_0': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + }, + }, + }, + }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.any(String), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'p2pkh_placeholder_unlock_1', + 'overrides': { + 'bytecode': { + 'signature_1': expect.any(String), + 'public_key_1': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + }, + }, + }, + }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.any(String), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': [ + 'slot', + ], + }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.any(String), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'Bar_funcA_input3_unlock', + 'overrides': { + 'bytecode': { + 'function_index': '0', + 'pkh_bar': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + 'keys': { + 'privateKeys': {}, + }, + }, + }, + }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.any(String), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'Bar_execute_input4_unlock', + 'overrides': { + 'bytecode': { + 'function_index': '2', + 'pk': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + 'pkh_bar': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + 'keys': { + 'privateKeys': { + 's': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', + }, + }, + }, + }, + }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.any(String), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'Foo_execute_input5_unlock', + 'overrides': { + 'bytecode': { + 'pk': '0x028f1219c918234d6bb06b4782354ff0759bd73036f3c849b88020c79fe013cd38', + 'pkh_foo': '0xb40a2013337edb0dfe307f0a57d5dec5bfe60dd0', + }, + 'keys': { + 'privateKeys': { + 's': '71080d8b52ec7b12adaec909ed54cd989b682ce2c35647eec219a16f5f90c528', + }, + }, + }, + }, + }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.any(String), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'Bar_funcB_input6_unlock', + 'overrides': { + 'bytecode': { + 'function_index': '1', + 'pkh_bar': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + 'keys': { + 'privateKeys': {}, + }, + }, + }, + }, + ], + 'locktime': 0, + 'outputs': [ + { + 'lockingBytecode': { + 'script': 'Foo_432c93902a8a8e49ef246028e707cddaa67a39af46b2b3c11c196cd09c931746_lock', + 'overrides': { + 'bytecode': { + 'pkh_foo': '0xb40a2013337edb0dfe307f0a57d5dec5bfe60dd0', + }, + }, + }, + 'valueSatoshis': 8000, + }, + { + 'lockingBytecode': '76a914512dbb2c8c02efbac8d92431aa0ac33f6b0bf97088ac', + 'token': { + 'amount': '100000000', + 'category': expect.any(String), + }, + 'valueSatoshis': 800, + }, + { + 'lockingBytecode': '76a914512dbb2c8c02efbac8d92431aa0ac33f6b0bf97088ac', + 'token': { + 'amount': '0', + 'category': expect.any(String), + 'nft': { + 'capability': 'minting', + 'commitment': '00', + }, + }, + 'valueSatoshis': 1000, + }, + { + 'lockingBytecode': '6a0568656c6c6f05776f726c64', + 'valueSatoshis': 0, + }, + ], + 'version': 2, + }, + 'sourceOutputs': [ + { + 'lockingBytecode': { + 'script': 'p2pkh_placeholder_lock_0', + 'overrides': { + 'bytecode': { + 'signature_0': expect.any(String), + 'public_key_0': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + }, + }, + }, + 'valueSatoshis': expect.any(Number), }, - }, - }, - 'signer_1': { - 'scripts': [ - 'p2pkh_placeholder_lock_1', - 'p2pkh_placeholder_unlock_1', - ], - 'description': 'placeholder_key_1', - 'name': 'P2PKH Signer (input #1)', - 'variables': { - 'placeholder_key_1': { - 'description': '', - 'name': 'P2PKH Placeholder Key (input #1)', - 'type': 'Key', + { + 'lockingBytecode': { + 'script': 'p2pkh_placeholder_lock_1', + 'overrides': { + 'bytecode': { + 'signature_1': expect.any(String), + 'public_key_1': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + }, + }, + }, + 'valueSatoshis': expect.any(Number), }, - }, - }, - 'signer_2': { - 'scripts': [ - 'p2pkh_placeholder_lock_2', - 'p2pkh_placeholder_unlock_2', - ], - 'description': 'placeholder_key_2', - 'name': 'P2PKH Signer (input #2)', - 'variables': { - 'placeholder_key_2': { - 'description': '', - 'name': 'P2PKH Placeholder Key (input #2)', - 'type': 'Key', + { + 'lockingBytecode': [ + 'slot', + ], + 'valueSatoshis': expect.any(Number), + }, + { + 'lockingBytecode': { + 'script': 'Bar_dfa9a690eb3692ca0655d91a1bebf908bd27f73faf31ec7fe316bde6c0fbed2e_lock', + 'overrides': { + 'bytecode': { + 'pkh_bar': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + }, + }, + 'valueSatoshis': expect.any(Number), + }, + { + 'lockingBytecode': { + 'script': 'Bar_dfa9a690eb3692ca0655d91a1bebf908bd27f73faf31ec7fe316bde6c0fbed2e_lock', + 'overrides': { + 'bytecode': { + 'pkh_bar': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + }, + }, + 'valueSatoshis': expect.any(Number), + }, + { + 'lockingBytecode': { + 'script': 'Foo_432c93902a8a8e49ef246028e707cddaa67a39af46b2b3c11c196cd09c931746_lock', + 'overrides': { + 'bytecode': { + 'pkh_foo': '0xb40a2013337edb0dfe307f0a57d5dec5bfe60dd0', + }, + }, + }, + 'valueSatoshis': expect.any(Number), + }, + { + 'lockingBytecode': { + 'script': 'Bar_dfa9a690eb3692ca0655d91a1bebf908bd27f73faf31ec7fe316bde6c0fbed2e_lock', + 'overrides': { + 'bytecode': { + 'pkh_bar': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + }, + }, + 'valueSatoshis': expect.any(Number), }, - }, - }, - }, - 'scripts': { - 'Bar_funcA_input3_unlock': { - 'passes': [ - 'Bar_funcA_input3_evaluate', - ], - 'name': 'funcA (input #3)', - 'script': '// "funcA" function parameters\n// none\n\n// function index in contract\n // int = <0>\n', - 'unlocks': 'Bar_dfa9a690eb3692ca0655d91a1bebf908bd27f73faf31ec7fe316bde6c0fbed2e_lock', - }, - 'Bar_dfa9a690eb3692ca0655d91a1bebf908bd27f73faf31ec7fe316bde6c0fbed2e_lock': { - 'lockingType': 'p2sh32', - 'name': 'Bar', - 'script': "// \"Bar\" contract constructor parameters\n // bytes20 = <0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970>\n\n// bytecode\n /* pragma cashscript >=0.10.2; */\n /* */\n /* contract Bar(bytes20 pkh_bar) { */\nOP_OVER OP_0 OP_NUMEQUAL OP_IF /* function funcA() { */\nOP_2 OP_2 OP_NUMEQUAL /* require(2==2); */\nOP_NIP OP_NIP OP_ELSE /* } */\n /* */\nOP_OVER OP_1 OP_NUMEQUAL OP_IF /* function funcB() { */\nOP_2 OP_2 OP_NUMEQUAL /* require(2==2); */\nOP_NIP OP_NIP OP_ELSE /* } */\n /* */\nOP_SWAP OP_2 OP_NUMEQUALVERIFY /* function execute(pubkey pk, sig s) { */\n /* console.log(\"Bar 'execute' function called.\"); */\nOP_OVER OP_HASH160 OP_EQUALVERIFY /* require(hash160(pk) == pkh_bar); */\nOP_CHECKSIG /* require(checkSig(s, pk)); */\n /* } */\nOP_ENDIF OP_ENDIF /* } */\n /* */", - }, - 'Bar_execute_input4_unlock': { - 'passes': [ - 'Bar_execute_input4_evaluate', - ], - 'name': 'execute (input #4)', - 'script': '// "execute" function parameters\n // sig\n // pubkey = <0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088>\n\n// function index in contract\n // int = <2>\n', - 'unlocks': 'Bar_dfa9a690eb3692ca0655d91a1bebf908bd27f73faf31ec7fe316bde6c0fbed2e_lock', - }, - 'Foo_execute_input5_unlock': { - 'passes': [ - 'Foo_execute_input5_evaluate', - ], - 'name': 'execute (input #5)', - 'script': '// "execute" function parameters\n // sig\n // pubkey = <0x028f1219c918234d6bb06b4782354ff0759bd73036f3c849b88020c79fe013cd38>\n', - 'unlocks': 'Foo_432c93902a8a8e49ef246028e707cddaa67a39af46b2b3c11c196cd09c931746_lock', - }, - 'Foo_432c93902a8a8e49ef246028e707cddaa67a39af46b2b3c11c196cd09c931746_lock': { - 'lockingType': 'p2sh32', - 'name': 'Foo', - 'script': "// \"Foo\" contract constructor parameters\n // bytes20 = <0xb40a2013337edb0dfe307f0a57d5dec5bfe60dd0>\n\n// bytecode\n /* pragma cashscript >=0.10.2; */\n /* */\n /* contract Foo(bytes20 pkh_foo) { */\n /* // Require pk to match stored pkh and signature to match */\n /* function execute(pubkey pk, sig s) { */\n /* console.log(\"Foo 'execute' function called.\"); */\nOP_OVER OP_HASH160 OP_EQUALVERIFY /* require(hash160(pk) == pkh_foo); */\nOP_CHECKSIG /* require(checkSig(s, pk)); */\n /* } */\n /* } */\n /* */", - }, - 'Bar_funcB_input6_unlock': { - 'passes': [ - 'Bar_funcB_input6_evaluate', ], - 'name': 'funcB (input #6)', - 'script': '// "funcB" function parameters\n// none\n\n// function index in contract\n // int = <1>\n', - 'unlocks': 'Bar_dfa9a690eb3692ca0655d91a1bebf908bd27f73faf31ec7fe316bde6c0fbed2e_lock', - }, - 'p2pkh_placeholder_unlock_0': { - 'name': 'P2PKH Unlock (input #0)', - 'script': '\n', - 'unlocks': 'p2pkh_placeholder_lock_0', - }, - 'p2pkh_placeholder_lock_0': { - 'lockingType': 'standard', - 'name': 'P2PKH Lock (input #0)', - 'script': 'OP_DUP\nOP_HASH160 <$( OP_HASH160\n)> OP_EQUALVERIFY\nOP_CHECKSIG', - }, - 'p2pkh_placeholder_unlock_1': { - 'name': 'P2PKH Unlock (input #1)', - 'script': '\n', - 'unlocks': 'p2pkh_placeholder_lock_1', - }, - 'p2pkh_placeholder_lock_1': { - 'lockingType': 'standard', - 'name': 'P2PKH Lock (input #1)', - 'script': 'OP_DUP\nOP_HASH160 <$( OP_HASH160\n)> OP_EQUALVERIFY\nOP_CHECKSIG', - }, - 'p2pkh_placeholder_unlock_2': { - 'name': 'P2PKH Unlock (input #2)', - 'script': '\n', - 'unlocks': 'p2pkh_placeholder_lock_2', - }, - 'p2pkh_placeholder_lock_2': { - 'lockingType': 'standard', - 'name': 'P2PKH Lock (input #2)', - 'script': 'OP_DUP\nOP_HASH160 <$( OP_HASH160\n)> OP_EQUALVERIFY\nOP_CHECKSIG', }, - }, - 'scenarios': { 'Bar_funcA_input3_evaluate': { 'name': 'Evaluate Bar funcA (input #3)', 'description': 'An example evaluation where this script execution passes.', @@ -291,7 +1035,7 @@ export const fixtures: Fixture[] = [ 'pkh_bar': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', 'function_index': '0', }, - 'currentBlockHeight': expect.any(Number), + 'currentBlockHeight': 2, 'currentBlockTime': expect.any(Number), 'keys': { 'privateKeys': {}, @@ -306,10 +1050,9 @@ export const fixtures: Fixture[] = [ 'unlockingBytecode': { 'script': 'p2pkh_placeholder_unlock_0', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_0': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_0': expect.any(String), + 'public_key_0': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -321,10 +1064,9 @@ export const fixtures: Fixture[] = [ 'unlockingBytecode': { 'script': 'p2pkh_placeholder_unlock_1', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_1': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_1': expect.any(String), + 'public_key_1': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -336,10 +1078,9 @@ export const fixtures: Fixture[] = [ 'unlockingBytecode': { 'script': 'p2pkh_placeholder_unlock_2', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_2': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_2': expect.any(String), + 'public_key_2': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -357,6 +1098,7 @@ export const fixtures: Fixture[] = [ 'outpointTransactionHash': expect.any(String), 'sequenceNumber': 4294967294, 'unlockingBytecode': { + 'script': 'Bar_execute_input4_unlock', 'overrides': { 'bytecode': { 'function_index': '2', @@ -369,7 +1111,6 @@ export const fixtures: Fixture[] = [ }, }, }, - 'script': 'Bar_execute_input4_unlock', }, }, { @@ -377,6 +1118,7 @@ export const fixtures: Fixture[] = [ 'outpointTransactionHash': expect.any(String), 'sequenceNumber': 4294967294, 'unlockingBytecode': { + 'script': 'Foo_execute_input5_unlock', 'overrides': { 'bytecode': { 'pk': '0x028f1219c918234d6bb06b4782354ff0759bd73036f3c849b88020c79fe013cd38', @@ -388,7 +1130,6 @@ export const fixtures: Fixture[] = [ }, }, }, - 'script': 'Foo_execute_input5_unlock', }, }, { @@ -396,6 +1137,7 @@ export const fixtures: Fixture[] = [ 'outpointTransactionHash': expect.any(String), 'sequenceNumber': 4294967294, 'unlockingBytecode': { + 'script': 'Bar_funcB_input6_unlock', 'overrides': { 'bytecode': { 'function_index': '1', @@ -405,11 +1147,10 @@ export const fixtures: Fixture[] = [ 'privateKeys': {}, }, }, - 'script': 'Bar_funcB_input6_unlock', }, }, ], - 'locktime': expect.any(Number), + 'locktime': 0, 'outputs': [ { 'lockingBytecode': { @@ -454,10 +1195,9 @@ export const fixtures: Fixture[] = [ 'lockingBytecode': { 'script': 'p2pkh_placeholder_lock_0', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_0': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_0': expect.any(String), + 'public_key_0': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -467,10 +1207,9 @@ export const fixtures: Fixture[] = [ 'lockingBytecode': { 'script': 'p2pkh_placeholder_lock_1', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_1': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_1': expect.any(String), + 'public_key_1': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -480,10 +1219,9 @@ export const fixtures: Fixture[] = [ 'lockingBytecode': { 'script': 'p2pkh_placeholder_lock_2', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_2': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_2': expect.any(String), + 'public_key_2': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -556,10 +1294,9 @@ export const fixtures: Fixture[] = [ 'unlockingBytecode': { 'script': 'p2pkh_placeholder_unlock_0', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_0': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_0': expect.any(String), + 'public_key_0': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -571,10 +1308,9 @@ export const fixtures: Fixture[] = [ 'unlockingBytecode': { 'script': 'p2pkh_placeholder_unlock_1', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_1': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_1': expect.any(String), + 'public_key_1': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -586,10 +1322,9 @@ export const fixtures: Fixture[] = [ 'unlockingBytecode': { 'script': 'p2pkh_placeholder_unlock_2', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_2': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_2': expect.any(String), + 'public_key_2': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -599,6 +1334,7 @@ export const fixtures: Fixture[] = [ 'outpointTransactionHash': expect.any(String), 'sequenceNumber': 4294967294, 'unlockingBytecode': { + 'script': 'Bar_funcA_input3_unlock', 'overrides': { 'bytecode': { 'function_index': '0', @@ -608,7 +1344,6 @@ export const fixtures: Fixture[] = [ 'privateKeys': {}, }, }, - 'script': 'Bar_funcA_input3_unlock', }, }, { @@ -624,6 +1359,7 @@ export const fixtures: Fixture[] = [ 'outpointTransactionHash': expect.any(String), 'sequenceNumber': 4294967294, 'unlockingBytecode': { + 'script': 'Foo_execute_input5_unlock', 'overrides': { 'bytecode': { 'pk': '0x028f1219c918234d6bb06b4782354ff0759bd73036f3c849b88020c79fe013cd38', @@ -635,7 +1371,6 @@ export const fixtures: Fixture[] = [ }, }, }, - 'script': 'Foo_execute_input5_unlock', }, }, { @@ -643,6 +1378,7 @@ export const fixtures: Fixture[] = [ 'outpointTransactionHash': expect.any(String), 'sequenceNumber': 4294967294, 'unlockingBytecode': { + 'script': 'Bar_funcB_input6_unlock', 'overrides': { 'bytecode': { 'function_index': '1', @@ -652,9 +1388,7 @@ export const fixtures: Fixture[] = [ 'privateKeys': {}, }, }, - 'script': 'Bar_funcB_input6_unlock', }, - }, ], 'locktime': 0, @@ -702,10 +1436,9 @@ export const fixtures: Fixture[] = [ 'lockingBytecode': { 'script': 'p2pkh_placeholder_lock_0', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_0': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_0': expect.any(String), + 'public_key_0': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -715,10 +1448,9 @@ export const fixtures: Fixture[] = [ 'lockingBytecode': { 'script': 'p2pkh_placeholder_lock_1', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_1': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_1': expect.any(String), + 'public_key_1': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -728,10 +1460,9 @@ export const fixtures: Fixture[] = [ 'lockingBytecode': { 'script': 'p2pkh_placeholder_lock_2', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_2': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_2': expect.any(String), + 'public_key_2': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -803,10 +1534,9 @@ export const fixtures: Fixture[] = [ 'unlockingBytecode': { 'script': 'p2pkh_placeholder_unlock_0', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_0': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_0': expect.any(String), + 'public_key_0': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -818,10 +1548,9 @@ export const fixtures: Fixture[] = [ 'unlockingBytecode': { 'script': 'p2pkh_placeholder_unlock_1', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_1': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_1': expect.any(String), + 'public_key_1': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -833,10 +1562,9 @@ export const fixtures: Fixture[] = [ 'unlockingBytecode': { 'script': 'p2pkh_placeholder_unlock_2', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_2': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_2': expect.any(String), + 'public_key_2': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -846,6 +1574,7 @@ export const fixtures: Fixture[] = [ 'outpointTransactionHash': expect.any(String), 'sequenceNumber': 4294967294, 'unlockingBytecode': { + 'script': 'Bar_funcA_input3_unlock', 'overrides': { 'bytecode': { 'function_index': '0', @@ -855,7 +1584,6 @@ export const fixtures: Fixture[] = [ 'privateKeys': {}, }, }, - 'script': 'Bar_funcA_input3_unlock', }, }, { @@ -863,6 +1591,7 @@ export const fixtures: Fixture[] = [ 'outpointTransactionHash': expect.any(String), 'sequenceNumber': 4294967294, 'unlockingBytecode': { + 'script': 'Bar_execute_input4_unlock', 'overrides': { 'bytecode': { 'function_index': '2', @@ -875,7 +1604,6 @@ export const fixtures: Fixture[] = [ }, }, }, - 'script': 'Bar_execute_input4_unlock', }, }, { @@ -891,17 +1619,16 @@ export const fixtures: Fixture[] = [ 'outpointTransactionHash': expect.any(String), 'sequenceNumber': 4294967294, 'unlockingBytecode': { + 'script': 'Bar_funcB_input6_unlock', 'overrides': { 'bytecode': { 'function_index': '1', 'pkh_bar': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', - }, 'keys': { 'privateKeys': {}, }, }, - 'script': 'Bar_funcB_input6_unlock', }, }, ], @@ -950,10 +1677,9 @@ export const fixtures: Fixture[] = [ 'lockingBytecode': { 'script': 'p2pkh_placeholder_lock_0', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_0': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_0': expect.any(String), + 'public_key_0': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -963,10 +1689,9 @@ export const fixtures: Fixture[] = [ 'lockingBytecode': { 'script': 'p2pkh_placeholder_lock_1', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_1': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_1': expect.any(String), + 'public_key_1': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -976,10 +1701,9 @@ export const fixtures: Fixture[] = [ 'lockingBytecode': { 'script': 'p2pkh_placeholder_lock_2', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_2': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_2': expect.any(String), + 'public_key_2': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -1049,10 +1773,9 @@ export const fixtures: Fixture[] = [ 'unlockingBytecode': { 'script': 'p2pkh_placeholder_unlock_0', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_0': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_0': expect.any(String), + 'public_key_0': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -1064,10 +1787,9 @@ export const fixtures: Fixture[] = [ 'unlockingBytecode': { 'script': 'p2pkh_placeholder_unlock_1', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_1': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_1': expect.any(String), + 'public_key_1': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -1079,10 +1801,9 @@ export const fixtures: Fixture[] = [ 'unlockingBytecode': { 'script': 'p2pkh_placeholder_unlock_2', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_2': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_2': expect.any(String), + 'public_key_2': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -1092,6 +1813,7 @@ export const fixtures: Fixture[] = [ 'outpointTransactionHash': expect.any(String), 'sequenceNumber': 4294967294, 'unlockingBytecode': { + 'script': 'Bar_funcA_input3_unlock', 'overrides': { 'bytecode': { 'function_index': '0', @@ -1101,7 +1823,6 @@ export const fixtures: Fixture[] = [ 'privateKeys': {}, }, }, - 'script': 'Bar_funcA_input3_unlock', }, }, { @@ -1109,6 +1830,7 @@ export const fixtures: Fixture[] = [ 'outpointTransactionHash': expect.any(String), 'sequenceNumber': 4294967294, 'unlockingBytecode': { + 'script': 'Bar_execute_input4_unlock', 'overrides': { 'bytecode': { 'function_index': '2', @@ -1121,7 +1843,6 @@ export const fixtures: Fixture[] = [ }, }, }, - 'script': 'Bar_execute_input4_unlock', }, }, { @@ -1129,6 +1850,7 @@ export const fixtures: Fixture[] = [ 'outpointTransactionHash': expect.any(String), 'sequenceNumber': 4294967294, 'unlockingBytecode': { + 'script': 'Foo_execute_input5_unlock', 'overrides': { 'bytecode': { 'pk': '0x028f1219c918234d6bb06b4782354ff0759bd73036f3c849b88020c79fe013cd38', @@ -1140,7 +1862,6 @@ export const fixtures: Fixture[] = [ }, }, }, - 'script': 'Foo_execute_input5_unlock', }, }, { @@ -1197,10 +1918,9 @@ export const fixtures: Fixture[] = [ 'lockingBytecode': { 'script': 'p2pkh_placeholder_lock_0', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_0': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_0': expect.any(String), + 'public_key_0': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -1210,10 +1930,9 @@ export const fixtures: Fixture[] = [ 'lockingBytecode': { 'script': 'p2pkh_placeholder_lock_1', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_1': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_1': expect.any(String), + 'public_key_1': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -1223,10 +1942,9 @@ export const fixtures: Fixture[] = [ 'lockingBytecode': { 'script': 'p2pkh_placeholder_lock_2', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_2': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_2': expect.any(String), + 'public_key_2': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, From 3671a21fb44f14e2137ac937de347babc989c478 Mon Sep 17 00:00:00 2001 From: Rosco Kalis Date: Tue, 16 Sep 2025 10:42:41 +0200 Subject: [PATCH 2/8] Add test for case where incorrect private key is used in P2PKH input (currently fails) --- packages/cashscript/test/debugging.test.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/cashscript/test/debugging.test.ts b/packages/cashscript/test/debugging.test.ts index fd97bd1e..6144d018 100644 --- a/packages/cashscript/test/debugging.test.ts +++ b/packages/cashscript/test/debugging.test.ts @@ -1,4 +1,4 @@ -import { Contract, MockNetworkProvider, SignatureAlgorithm, SignatureTemplate, TransactionBuilder } from '../src/index.js'; +import { Contract, FailedTransactionError, MockNetworkProvider, SignatureAlgorithm, SignatureTemplate, TransactionBuilder } from '../src/index.js'; import { aliceAddress, alicePriv, alicePub, bobPriv, bobPub } from './fixture/vars.js'; import '../src/test/JestExtensions.js'; import { randomUtxo } from '../src/utils.js'; @@ -622,7 +622,7 @@ describe('Debugging tests', () => { }); describe('P2PKH only transaction', () => { - it('should debug a transaction with only P2PKH inputs', async () => { + it('should succeed when spending from P2PKH inputs with the corresponding unlocker', async () => { const provider = new MockNetworkProvider(); const result = new TransactionBuilder({ provider }) @@ -632,5 +632,15 @@ describe('Debugging tests', () => { expect(Object.keys(result).length).toBeGreaterThan(0); }); + + it('should fail when spending from P2PKH inputs with an unlocker for a different public key', async () => { + const provider = new MockNetworkProvider(); + + const transactionBuilder = new TransactionBuilder({ provider }) + .addInputs(await provider.getUtxos(aliceAddress), new SignatureTemplate(bobPriv).unlockP2PKH()) + .addOutput({ to: aliceAddress, amount: 5000n }); + + expect(() => transactionBuilder.debug()).toThrow(FailedTransactionError); + }); }); }); From 28864a3605dd62a093a993b5f896210fd3e06b62 Mon Sep 17 00:00:00 2001 From: Rosco Kalis Date: Tue, 16 Sep 2025 10:48:46 +0200 Subject: [PATCH 3/8] Address some TODOs about removing old code and add some new TODOs --- packages/cashscript/src/LibauthTemplate.ts | 33 ++++--------------- packages/cashscript/src/interfaces.ts | 8 ----- .../src/network/MockNetworkProvider.ts | 1 + 3 files changed, 7 insertions(+), 35 deletions(-) diff --git a/packages/cashscript/src/LibauthTemplate.ts b/packages/cashscript/src/LibauthTemplate.ts index d3c033df..44dfc0a9 100644 --- a/packages/cashscript/src/LibauthTemplate.ts +++ b/packages/cashscript/src/LibauthTemplate.ts @@ -31,10 +31,10 @@ import { Contract } from './Contract.js'; import { DebugResults, debugTemplate } from './debugging.js'; import { HashType, + isContractUnlocker, isP2PKHUnlocker, isStandardUnlockableUtxo, isUnlockableUtxo, - isUtxoP2PKH, LibauthTokenDetails, Output, SignatureAlgorithm, @@ -280,6 +280,7 @@ export const generateTemplateScenarios = ( const encodedConstructorArgs = contract.encodedConstructorArgs; const scenarioIdentifier = `${artifact.contractName}_${abiFunction.name}_input${inputIndex}_evaluate`; + // TODO: Update scenario descriptions const scenarios = { // single scenario to spend out transaction under test given the CashScript parameters provided [scenarioIdentifier]: { @@ -324,6 +325,7 @@ export const generateTemplateScenariosP2PKH = ( const { signature, publicKey } = getSignatureAndPubkeyFromP2PKHInput(libauthTransaction.inputs[inputIndex]); + // TODO: Update scenario descriptions const scenarios = { // single scenario to spend out transaction under test given the CashScript parameters provided [scenarioIdentifier]: { @@ -459,20 +461,14 @@ export const getLibauthTemplates = ( // We can typecast this because we check that all inputs are standard unlockable at the top of this function for (const [inputIndex, input] of (txn.inputs as StandardUnlockableUtxo[]).entries()) { - // If template exists on the input, it indicates this is a P2PKH (Pay to Public Key Hash) input - if ('template' in input.unlocker) { - // @ts-ignore TODO: Remove UtxoP2PKH type and only use UnlockableUtxo in Libauth Template generation - input.template = input.unlocker?.template; // Added to support P2PKH inputs in buildTemplate + if (isP2PKHUnlocker(input.unlocker)) { Object.assign(p2pkhEntities, generateTemplateEntitiesP2PKH(inputIndex)); Object.assign(p2pkhScripts, generateTemplateScriptsP2PKH(inputIndex)); - - Object.assign(scenarios, generateTemplateScenariosP2PKH(libauthTransaction, csTransaction as any, inputIndex)); - + Object.assign(scenarios, generateTemplateScenariosP2PKH(libauthTransaction, csTransaction, inputIndex)); continue; } - // If contract exists on the input, it indicates this is a contract input - if ('contract' in input.unlocker) { + if (isContractUnlocker(input.unlocker)) { const contract = input.unlocker?.contract; const abiFunction = input.unlocker?.abiFunction; @@ -807,23 +803,6 @@ export const generateTemplateScenarioBytecode = ( ): WalletTemplateScenarioBytecode | ['slot'] => { if (insertSlot) return ['slot']; - const p2pkhScriptName = `${p2pkhScriptNameTemplate}_${inputIndex}`; - - // This is for P2PKH inputs in the old transaction builder (TODO: remove when we remove old transaction builder) - if (isUtxoP2PKH(input)) { - const { signature, publicKey } = getSignatureAndPubkeyFromP2PKHInput(libauthInput); - - return { - script: p2pkhScriptName, - overrides: { - bytecode: { - [`signature_${inputIndex}`]: `0x${binToHex(signature)}`, - [`public_key_${inputIndex}`]: `0x${binToHex(publicKey)}`, - }, - }, - }; - } - if (isUnlockableUtxo(input) && isStandardUnlockableUtxo(input)) { return generateUnlockingScriptParams(input, libauthInput, p2pkhScriptNameTemplate, inputIndex); } diff --git a/packages/cashscript/src/interfaces.ts b/packages/cashscript/src/interfaces.ts index 98544289..d5a88c48 100644 --- a/packages/cashscript/src/interfaces.ts +++ b/packages/cashscript/src/interfaces.ts @@ -75,14 +75,6 @@ export function isPlaceholderUnlocker(unlocker: Unlocker): unlocker is Placehold return 'placeholder' in unlocker; } -export interface UtxoP2PKH extends Utxo { - template: SignatureTemplate; -} - -export function isUtxoP2PKH(utxo: Utxo): utxo is UtxoP2PKH { - return 'template' in utxo; -} - export interface Recipient { to: string; amount: bigint; diff --git a/packages/cashscript/src/network/MockNetworkProvider.ts b/packages/cashscript/src/network/MockNetworkProvider.ts index 84ed2d43..fd0f8c5f 100644 --- a/packages/cashscript/src/network/MockNetworkProvider.ts +++ b/packages/cashscript/src/network/MockNetworkProvider.ts @@ -25,6 +25,7 @@ export default class MockNetworkProvider implements NetworkProvider { this.options = { updateUtxoSet: true, ...options }; for (let i = 0; i < 3; i += 1) { + // TODO: Don't seed the MockNetworkProvider with any UTXOs this.addUtxo(aliceAddress, randomUtxo()); this.addUtxo(bobAddress, randomUtxo()); this.addUtxo(carolAddress, randomUtxo()); From 02535a813f7a329f71a4cb50d08a1a746d538eb3 Mon Sep 17 00:00:00 2001 From: Rosco Kalis Date: Tue, 16 Sep 2025 11:16:12 +0200 Subject: [PATCH 4/8] Actually check for debug evaluation errors for P2PKH inputs in debugSingleScenario --- packages/cashscript/src/debugging.ts | 38 ++++++++++++++++++---------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/packages/cashscript/src/debugging.ts b/packages/cashscript/src/debugging.ts index 11ae6865..82023d5d 100644 --- a/packages/cashscript/src/debugging.ts +++ b/packages/cashscript/src/debugging.ts @@ -45,10 +45,6 @@ const debugSingleScenario = ( const fullDebugSteps = vm.debug(program); - if (!artifact) { - return fullDebugSteps; - } - // P2SH executions have 3 phases, we only want the last one (locking script execution) // https://libauth.org/types/AuthenticationVirtualMachine.html#__type.debug const lockingScriptDebugResult = fullDebugSteps.slice(findLastIndex(fullDebugSteps, (state) => state.ip === 0)); @@ -58,12 +54,15 @@ const debugSingleScenario = ( const executedDebugSteps = lockingScriptDebugResult .filter((debugStep) => debugStep.controlStack.every(item => item === true)); - const executedLogs = (artifact.debug?.logs ?? []) - .filter((log) => executedDebugSteps.some((debugStep) => log.ip === debugStep.ip)); + // P2PKH inputs do not have an artifact, so we skip the console.log handling + if (artifact) { + const executedLogs = (artifact.debug?.logs ?? []) + .filter((log) => executedDebugSteps.some((debugStep) => log.ip === debugStep.ip)); - for (const log of executedLogs) { - const inputIndex = extractInputIndexFromScenario(scenarioId); - logConsoleLogStatement(log, executedDebugSteps, artifact, inputIndex); + for (const log of executedLogs) { + const inputIndex = extractInputIndexFromScenario(scenarioId); + logConsoleLogStatement(log, executedDebugSteps, artifact, inputIndex); + } } const lastExecutedDebugStep = executedDebugSteps[executedDebugSteps.length - 1]; @@ -85,11 +84,18 @@ const debugSingleScenario = ( const isNullFail = lastExecutedDebugStep.error.includes(AuthenticationErrorCommon.nonNullSignatureFailure); const requireStatementIp = failingIp + (isNullFail && isSignatureCheckWithoutVerify(failingInstruction) ? 1 : 0); + const { program: { inputIndex }, error } = lastExecutedDebugStep; + + // If there is no artifact, this is a P2PKH debug error, error can occur when final CHECKSIG fails with NULLFAIL or when + // public key does not match pkh in EQUALVERIFY + // Note: due to P2PKHUnlocker implementation, the CHECKSIG cannot fail in practice, only the EQUALVERIFY can fail + if (!artifact) { + throw new FailedTransactionError(error, getBitauthUri(template)); + } + const requireStatement = (artifact.debug?.requires ?? []) .find((statement) => statement.ip === requireStatementIp); - const { program: { inputIndex }, error } = lastExecutedDebugStep; - if (requireStatement) { // Note that we use failingIp here rather than requireStatementIp, see comment above throw new FailedRequireError( @@ -119,11 +125,17 @@ const debugSingleScenario = ( // console.warn('message', finalExecutedVerifyIp); // console.warn(artifact.debug?.requires); + const { program: { inputIndex } } = lastExecutedDebugStep; + + // If there is no artifact, this is a P2PKH debug error, final verify can only occur when final CHECKSIG failed + // Note: due to P2PKHUnlocker implementation, this cannot happen in practice + if (!artifact) { + throw new FailedTransactionError(evaluationResult, getBitauthUri(template)); + } + const requireStatement = (artifact.debug?.requires ?? []) .find((message) => message.ip === finalExecutedVerifyIp); - const { program: { inputIndex } } = lastExecutedDebugStep; - if (requireStatement) { throw new FailedRequireError( artifact, sourcemapInstructionPointer, requireStatement, inputIndex, getBitauthUri(template), From 7d1c472ade08883cbe66d10bc03ca4c06b2697de Mon Sep 17 00:00:00 2001 From: Rosco Kalis Date: Tue, 16 Sep 2025 11:30:37 +0200 Subject: [PATCH 5/8] Skip newly added test that is hard to fix + small cleanup --- packages/cashscript/src/LibauthTemplate.ts | 6 ++---- packages/cashscript/test/debugging.test.ts | 4 +++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/cashscript/src/LibauthTemplate.ts b/packages/cashscript/src/LibauthTemplate.ts index 44dfc0a9..a0f6d812 100644 --- a/packages/cashscript/src/LibauthTemplate.ts +++ b/packages/cashscript/src/LibauthTemplate.ts @@ -48,7 +48,6 @@ import { addressToLockScript, extendedStringify, getSignatureAndPubkeyFromP2PKHI import { TransactionBuilder } from './TransactionBuilder.js'; import { deflate } from 'pako'; - /** * Generates template entities for P2PKH (Pay to Public Key Hash) placeholder scripts. * @@ -62,6 +61,7 @@ export const generateTemplateEntitiesP2PKH = ( const lockScriptName = `p2pkh_placeholder_lock_${inputIndex}`; const unlockScriptName = `p2pkh_placeholder_unlock_${inputIndex}`; + // TODO: Add descriptions return { [`signer_${inputIndex}`]: { scripts: [lockScriptName, unlockScriptName], @@ -319,8 +319,6 @@ export const generateTemplateScenariosP2PKH = ( csTransaction: TransactionType, inputIndex: number, ): WalletTemplate['scenarios'] => { - // const artifact = contract.artifact; - // const encodedConstructorArgs = contract.encodedConstructorArgs; const scenarioIdentifier = `P2PKH_spend_input${inputIndex}_evaluate`; const { signature, publicKey } = getSignatureAndPubkeyFromP2PKHInput(libauthTransaction.inputs[inputIndex]); @@ -585,7 +583,7 @@ export const getLibauthTemplates = ( export const debugLibauthTemplate = (template: WalletTemplate, transaction: TransactionBuilder): DebugResults => { const allArtifacts = transaction.inputs - .map(input => 'contract' in input.unlocker ? input.unlocker.contract : undefined) + .map(input => isContractUnlocker(input.unlocker) ? input.unlocker.contract : undefined) .filter((contract): contract is Contract => Boolean(contract)) .map(contract => contract.artifact); diff --git a/packages/cashscript/test/debugging.test.ts b/packages/cashscript/test/debugging.test.ts index 6144d018..8e5942d7 100644 --- a/packages/cashscript/test/debugging.test.ts +++ b/packages/cashscript/test/debugging.test.ts @@ -633,7 +633,9 @@ describe('Debugging tests', () => { expect(Object.keys(result).length).toBeGreaterThan(0); }); - it('should fail when spending from P2PKH inputs with an unlocker for a different public key', async () => { + // We currently don't have a way to properly handle non-matching UTXOs and unlockers + // Note: that also goes for Contract UTXOs where a user uses an unlocker of a different contract + it.skip('should fail when spending from P2PKH inputs with an unlocker for a different public key', async () => { const provider = new MockNetworkProvider(); const transactionBuilder = new TransactionBuilder({ provider }) From b2ccdb07bbc81ecc8a2de2352bdd5e31114e9fe0 Mon Sep 17 00:00:00 2001 From: Rosco Kalis Date: Tue, 16 Sep 2025 11:45:41 +0200 Subject: [PATCH 6/8] Add TODOs for removing private keys from P2SH debugging as well --- packages/cashscript/src/LibauthTemplate.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/cashscript/src/LibauthTemplate.ts b/packages/cashscript/src/LibauthTemplate.ts index a0f6d812..12f07f0b 100644 --- a/packages/cashscript/src/LibauthTemplate.ts +++ b/packages/cashscript/src/LibauthTemplate.ts @@ -294,6 +294,7 @@ export const generateTemplateScenarios = ( }, currentBlockHeight: 2, currentBlockTime: Math.round(+new Date() / 1000), + // TODO: remove usage of private keys in P2SH scenarios as well keys: { privateKeys: generateTemplateScenarioKeys(abiFunction.inputs, encodedFunctionArgs), }, @@ -652,6 +653,7 @@ export const generateUnlockingScriptParams = ( ...generateTemplateScenarioParametersValues(abiFunction.inputs, encodedFunctionArgs), ...generateTemplateScenarioParametersValues(contract.artifact.constructorInputs, contract.encodedConstructorArgs), }, + // TODO: remove usage of private keys in P2SH scenarios as well keys: { privateKeys: generateTemplateScenarioKeys(abiFunction.inputs, encodedFunctionArgs), }, From 8c517f895e94bcfdc7d86686f7e6d17bd337e047 Mon Sep 17 00:00:00 2001 From: Rosco Kalis Date: Thu, 18 Sep 2025 10:28:30 +0200 Subject: [PATCH 7/8] Update fixture --- .../test/fixture/libauth-template/fixtures.ts | 274 ++++++++++++++++-- 1 file changed, 246 insertions(+), 28 deletions(-) diff --git a/packages/cashscript/test/fixture/libauth-template/fixtures.ts b/packages/cashscript/test/fixture/libauth-template/fixtures.ts index de27e2b3..55e0fbbf 100644 --- a/packages/cashscript/test/fixture/libauth-template/fixtures.ts +++ b/packages/cashscript/test/fixture/libauth-template/fixtures.ts @@ -1368,13 +1368,18 @@ export const fixtures: Fixture[] = [ 'p2pkh_placeholder_lock_0', 'p2pkh_placeholder_unlock_0', ], - 'description': 'placeholder_key_0', + 'description': 'P2PKH data for input 0', 'name': 'P2PKH Signer (input #0)', 'variables': { - 'placeholder_key_0': { + 'signature_0': { 'description': '', - 'name': 'P2PKH Placeholder Key (input #0)', - 'type': 'Key', + 'name': 'P2PKH Signature (input #0)', + 'type': 'WalletData', + }, + 'public_key_0': { + 'description': '', + 'name': 'P2PKH public key (input #0)', + 'type': 'WalletData', }, }, }, @@ -1383,13 +1388,18 @@ export const fixtures: Fixture[] = [ 'p2pkh_placeholder_lock_2', 'p2pkh_placeholder_unlock_2', ], - 'description': 'placeholder_key_2', + 'description': 'P2PKH data for input 2', 'name': 'P2PKH Signer (input #2)', 'variables': { - 'placeholder_key_2': { + 'signature_2': { 'description': '', - 'name': 'P2PKH Placeholder Key (input #2)', - 'type': 'Key', + 'name': 'P2PKH Signature (input #2)', + 'type': 'WalletData', + }, + 'public_key_2': { + 'description': '', + 'name': 'P2PKH public key (input #2)', + 'type': 'WalletData', }, }, }, @@ -1409,27 +1419,136 @@ export const fixtures: Fixture[] = [ 'script': '// "P2PKH" contract constructor parameters\n // bytes20 = <0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970>\n\n// bytecode\n /* contract P2PKH(bytes20 pkh) { */\n /* // Require pk to match stored pkh and signature to match */\n /* function spend(pubkey pk, sig s) { */\nOP_OVER OP_HASH160 OP_EQUALVERIFY /* require(hash160(pk) == pkh); */\nOP_CHECKSIG /* require(checkSig(s, pk)); */\n /* } */\n /* } */\n /* */', }, 'p2pkh_placeholder_unlock_0': { + 'passes': [ + 'P2PKH_spend_input0_evaluate', + ], 'name': 'P2PKH Unlock (input #0)', - 'script': '\n', + 'script': '\n', 'unlocks': 'p2pkh_placeholder_lock_0', }, 'p2pkh_placeholder_lock_0': { 'lockingType': 'standard', 'name': 'P2PKH Lock (input #0)', - 'script': 'OP_DUP\nOP_HASH160 <$( OP_HASH160\n)> OP_EQUALVERIFY\nOP_CHECKSIG', + 'script': 'OP_DUP\nOP_HASH160 <$( OP_HASH160\n)> OP_EQUALVERIFY\nOP_CHECKSIG', }, 'p2pkh_placeholder_unlock_2': { + 'passes': [ + 'P2PKH_spend_input2_evaluate', + ], 'name': 'P2PKH Unlock (input #2)', - 'script': '\n', + 'script': '\n', 'unlocks': 'p2pkh_placeholder_lock_2', }, 'p2pkh_placeholder_lock_2': { 'lockingType': 'standard', 'name': 'P2PKH Lock (input #2)', - 'script': 'OP_DUP\nOP_HASH160 <$( OP_HASH160\n)> OP_EQUALVERIFY\nOP_CHECKSIG', + 'script': 'OP_DUP\nOP_HASH160 <$( OP_HASH160\n)> OP_EQUALVERIFY\nOP_CHECKSIG', }, }, 'scenarios': { + 'P2PKH_spend_input0_evaluate': { + 'name': 'Evaluate P2PKH spend (input #0)', + 'description': 'An example evaluation where this script execution passes.', + 'data': { + 'bytecode': { + 'signature_0': expect.stringMatching(/^0x[0-9a-f]{130}$/), + 'public_key_0': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + }, + 'currentBlockHeight': expect.any(Number), + 'currentBlockTime': expect.any(Number), + }, + 'transaction': { + 'inputs': [ + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.stringMatching(/^[0-9a-f]{64}$/), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': [ + 'slot', + ], + }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.stringMatching(/^[0-9a-f]{64}$/), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'P2PKH_spend_input1_unlock', + 'overrides': { + 'bytecode': { + 'pk': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + 'pkh': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + 'keys': { + 'privateKeys': { + 's': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', + }, + }, + }, + }, + }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.stringMatching(/^[0-9a-f]{64}$/), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'p2pkh_placeholder_unlock_2', + 'overrides': { + 'bytecode': { + 'signature_2': expect.stringMatching(/^0x[0-9a-f]{142,146}$/), + 'public_key_2': '0x028f1219c918234d6bb06b4782354ff0759bd73036f3c849b88020c79fe013cd38', + }, + }, + }, + }, + ], + 'locktime': expect.any(Number), + 'outputs': [ + { + 'lockingBytecode': { + 'script': 'P2PKH_eae136efb95be487872bfe03984fc1eb80b23361_lock', + 'overrides': { + 'bytecode': { + 'pkh': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + }, + }, + 'valueSatoshis': 1000, + }, + ], + 'version': 2, + }, + 'sourceOutputs': [ + { + 'lockingBytecode': [ + 'slot', + ], + 'valueSatoshis': expect.any(Number), + }, + { + 'lockingBytecode': { + 'script': 'P2PKH_eae136efb95be487872bfe03984fc1eb80b23361_lock', + 'overrides': { + 'bytecode': { + 'pkh': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + }, + }, + 'valueSatoshis': expect.any(Number), + }, + { + 'lockingBytecode': { + 'script': 'p2pkh_placeholder_lock_2', + 'overrides': { + 'bytecode': { + 'signature_2': expect.stringMatching(/^0x[0-9a-f]{142,146}$/), + 'public_key_2': '0x028f1219c918234d6bb06b4782354ff0759bd73036f3c849b88020c79fe013cd38', + }, + }, + }, + 'valueSatoshis': expect.any(Number), + }, + ], + }, 'P2PKH_spend_input1_evaluate': { 'name': 'Evaluate P2PKH spend (input #1)', 'description': 'An example evaluation where this script execution passes.', @@ -1455,10 +1574,9 @@ export const fixtures: Fixture[] = [ 'unlockingBytecode': { 'script': 'p2pkh_placeholder_unlock_0', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_0': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_0': expect.stringMatching(/^0x[0-9a-f]{130}$/), + 'public_key_0': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -1478,16 +1596,15 @@ export const fixtures: Fixture[] = [ 'unlockingBytecode': { 'script': 'p2pkh_placeholder_unlock_2', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_2': '71080d8b52ec7b12adaec909ed54cd989b682ce2c35647eec219a16f5f90c528', - }, + 'bytecode': { + 'signature_2': expect.stringMatching(/^0x[0-9a-f]{142,146}$/), + 'public_key_2': '0x028f1219c918234d6bb06b4782354ff0759bd73036f3c849b88020c79fe013cd38', }, }, }, }, ], - 'locktime': 0, + 'locktime': expect.any(Number), 'outputs': [ { 'lockingBytecode': { @@ -1508,10 +1625,9 @@ export const fixtures: Fixture[] = [ 'lockingBytecode': { 'script': 'p2pkh_placeholder_lock_0', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_0': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', - }, + 'bytecode': { + 'signature_0': expect.stringMatching(/^0x[0-9a-f]{130}$/), + 'public_key_0': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', }, }, }, @@ -1527,13 +1643,115 @@ export const fixtures: Fixture[] = [ 'lockingBytecode': { 'script': 'p2pkh_placeholder_lock_2', 'overrides': { - 'keys': { - 'privateKeys': { - 'placeholder_key_2': '71080d8b52ec7b12adaec909ed54cd989b682ce2c35647eec219a16f5f90c528', + 'bytecode': { + 'signature_2': expect.stringMatching(/^0x[0-9a-f]{142,146}$/), + 'public_key_2': '0x028f1219c918234d6bb06b4782354ff0759bd73036f3c849b88020c79fe013cd38', + }, + }, + }, + 'valueSatoshis': expect.any(Number), + }, + ], + }, + 'P2PKH_spend_input2_evaluate': { + 'name': 'Evaluate P2PKH spend (input #2)', + 'description': 'An example evaluation where this script execution passes.', + 'data': { + 'bytecode': { + 'signature_2': expect.stringMatching(/^0x[0-9a-f]{142,146}$/), + 'public_key_2': '0x028f1219c918234d6bb06b4782354ff0759bd73036f3c849b88020c79fe013cd38', + }, + 'currentBlockHeight': expect.any(Number), + 'currentBlockTime': expect.any(Number), + }, + 'transaction': { + 'inputs': [ + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.stringMatching(/^[0-9a-f]{64}$/), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'p2pkh_placeholder_unlock_0', + 'overrides': { + 'bytecode': { + 'signature_0': expect.stringMatching(/^0x[0-9a-f]{130}$/), + 'public_key_0': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + }, + }, + }, + }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.stringMatching(/^[0-9a-f]{64}$/), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': { + 'script': 'P2PKH_spend_input1_unlock', + 'overrides': { + 'bytecode': { + 'pk': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + 'pkh': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + 'keys': { + 'privateKeys': { + 's': '36f8155c559f3a670586bbbf9fd52beef6f96124f5a3a39c167fc24b052d24d7', + }, }, }, }, }, + { + 'outpointIndex': expect.any(Number), + 'outpointTransactionHash': expect.stringMatching(/^[0-9a-f]{64}$/), + 'sequenceNumber': 4294967294, + 'unlockingBytecode': [ + 'slot', + ], + }, + ], + 'locktime': expect.any(Number), + 'outputs': [ + { + 'lockingBytecode': { + 'script': 'P2PKH_eae136efb95be487872bfe03984fc1eb80b23361_lock', + 'overrides': { + 'bytecode': { + 'pkh': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + }, + }, + 'valueSatoshis': 1000, + }, + ], + 'version': 2, + }, + 'sourceOutputs': [ + { + 'lockingBytecode': { + 'script': 'p2pkh_placeholder_lock_0', + 'overrides': { + 'bytecode': { + 'signature_0': expect.stringMatching(/^0x[0-9a-f]{130}$/), + 'public_key_0': '0x0373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c088', + }, + }, + }, + 'valueSatoshis': expect.any(Number), + }, + { + 'lockingBytecode': { + 'script': 'P2PKH_eae136efb95be487872bfe03984fc1eb80b23361_lock', + 'overrides': { + 'bytecode': { + 'pkh': '0x512dbb2c8c02efbac8d92431aa0ac33f6b0bf970', + }, + }, + }, + 'valueSatoshis': expect.any(Number), + }, + { + 'lockingBytecode': [ + 'slot', + ], 'valueSatoshis': expect.any(Number), }, ], From 26c27fdf7e0e0bb39059f174bb8ff2b271fd0085 Mon Sep 17 00:00:00 2001 From: Rosco Kalis Date: Thu, 18 Sep 2025 10:28:56 +0200 Subject: [PATCH 8/8] Add libauth template refactor to release notes --- website/docs/releases/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/website/docs/releases/release-notes.md b/website/docs/releases/release-notes.md index 4ab9c2ba..38797b94 100644 --- a/website/docs/releases/release-notes.md +++ b/website/docs/releases/release-notes.md @@ -8,6 +8,7 @@ title: Release Notes - :boom: **BREAKING**: Set `updateUtxoSet` to `true` by default for `MockNetworkProvider`. - :boom: **BREAKING**: Make `provider` a required option in `Contract` constructor. - :boom: **BREAKING**: Remove deprecated "old" transaction builder (`contract.functions`). +- :hammer_and_wrench: Improve libauth template generation. ## v0.11.5