cashscript 0.11.4 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ import { WalletTemplate } from '@bitauth/libauth';
2
+ import { DebugResults } from '../debugging.js';
3
+ import { TransactionBuilder } from '../TransactionBuilder.js';
4
+ export declare const getLibauthTemplate: (transactionBuilder: TransactionBuilder) => WalletTemplate;
5
+ export declare const debugLibauthTemplate: (template: WalletTemplate, transaction: TransactionBuilder) => DebugResults;
6
+ export declare const getBitauthUri: (template: WalletTemplate) => string;
@@ -0,0 +1,445 @@
1
+ import { binToBase64, binToHex, utf8ToBin, } from '@bitauth/libauth';
2
+ import { encodeFunctionArguments } from '../Argument.js';
3
+ import { debugTemplate } from '../debugging.js';
4
+ import { isContractUnlocker, isP2PKHUnlocker, isStandardUnlockableUtxo, isUnlockableUtxo, VmTarget, } from '../interfaces.js';
5
+ import SignatureTemplate from '../SignatureTemplate.js';
6
+ import { addressToLockScript, extendedStringify, zip } from '../utils.js';
7
+ import { deflate } from 'pako';
8
+ import MockNetworkProvider from '../network/MockNetworkProvider.js';
9
+ import { addHexPrefixExceptEmpty, formatBytecodeForDebugging, formatParametersForDebugging, getLockScriptName, getSignatureAndPubkeyFromP2PKHInput, getUnlockScriptName, lockingBytecodeIsSetToSlot, serialiseTokenDetails } from './utils.js';
10
+ // TODO: Add / improve descriptions throughout the template generation
11
+ export const getLibauthTemplate = (transactionBuilder) => {
12
+ if (transactionBuilder.inputs.some((input) => !isStandardUnlockableUtxo(input))) {
13
+ throw new Error('Cannot use debugging functionality with a transaction that contains custom unlockers');
14
+ }
15
+ const libauthTransaction = transactionBuilder.buildLibauthTransaction();
16
+ const vmTarget = transactionBuilder.provider instanceof MockNetworkProvider
17
+ ? transactionBuilder.provider.vmTarget
18
+ : VmTarget.BCH_2025_05;
19
+ const template = {
20
+ $schema: 'https://ide.bitauth.com/authentication-template-v0.schema.json',
21
+ description: 'Imported from cashscript',
22
+ name: 'CashScript Generated Debugging Template',
23
+ supported: [vmTarget],
24
+ version: 0,
25
+ entities: generateAllTemplateEntities(transactionBuilder),
26
+ scripts: generateAllTemplateScripts(transactionBuilder),
27
+ scenarios: generateAllTemplateScenarios(libauthTransaction, transactionBuilder),
28
+ };
29
+ // TODO: Refactor the below code to not have deep reassignment of scenario.sourceOutputs and scenario.transaction.outputs
30
+ // Initialize bytecode mappings, these will be used to map the locking and unlocking scripts and naming the scripts
31
+ const unlockingBytecodeToLockingBytecodeParams = {};
32
+ const lockingBytecodeToLockingBytecodeParams = {};
33
+ // We can typecast this because we check that all inputs are standard unlockable at the top of this function
34
+ for (const [inputIndex, input] of transactionBuilder.inputs.entries()) {
35
+ if (isContractUnlocker(input.unlocker)) {
36
+ const lockScriptName = getLockScriptName(input.unlocker.contract);
37
+ if (!lockScriptName)
38
+ continue;
39
+ const lockingScriptParams = generateLockingScriptParams(input.unlocker.contract, input, lockScriptName);
40
+ const unlockingBytecode = binToHex(libauthTransaction.inputs[inputIndex].unlockingBytecode);
41
+ unlockingBytecodeToLockingBytecodeParams[unlockingBytecode] = lockingScriptParams;
42
+ const lockingBytecode = binToHex(addressToLockScript(input.unlocker.contract.address));
43
+ lockingBytecodeToLockingBytecodeParams[lockingBytecode] = lockingScriptParams;
44
+ }
45
+ }
46
+ for (const scenario of Object.values(template.scenarios)) {
47
+ // For Inputs
48
+ for (const [idx, input] of libauthTransaction.inputs.entries()) {
49
+ const unlockingBytecode = binToHex(input.unlockingBytecode);
50
+ const lockingBytecodeParams = unlockingBytecodeToLockingBytecodeParams[unlockingBytecode];
51
+ // If lockingBytecodeParams is unknown, then it stays at default: {}
52
+ if (!lockingBytecodeParams)
53
+ continue;
54
+ // If locking bytecode is set to ['slot'] then this is being evaluated by the scenario, so we don't replace bytecode
55
+ if (lockingBytecodeIsSetToSlot(scenario?.sourceOutputs?.[idx]?.lockingBytecode))
56
+ continue;
57
+ // If lockingBytecodeParams is known, and this input is not ['slot'] then assign a locking bytecode as source output
58
+ if (scenario.sourceOutputs?.[idx]) {
59
+ scenario.sourceOutputs[idx] = {
60
+ ...scenario.sourceOutputs[idx],
61
+ lockingBytecode: lockingBytecodeParams,
62
+ };
63
+ }
64
+ }
65
+ // For Outputs
66
+ for (const [idx, output] of libauthTransaction.outputs.entries()) {
67
+ const lockingBytecode = binToHex(output.lockingBytecode);
68
+ const lockingBytecodeParams = lockingBytecodeToLockingBytecodeParams[lockingBytecode];
69
+ // If lockingBytecodeParams is unknown, then it stays at default: {}
70
+ if (!lockingBytecodeParams)
71
+ continue;
72
+ // If locking bytecode is set to ['slot'] then this is being evaluated by the scenario, so we don't replace bytecode
73
+ if (lockingBytecodeIsSetToSlot(scenario?.transaction?.outputs?.[idx]?.lockingBytecode))
74
+ continue;
75
+ // If lockingBytecodeParams is known, and this input is not ['slot'] then assign a locking bytecode as source output
76
+ if (scenario?.transaction?.outputs?.[idx]) {
77
+ scenario.transaction.outputs[idx] = {
78
+ ...scenario.transaction.outputs[idx],
79
+ lockingBytecode: lockingBytecodeParams,
80
+ };
81
+ }
82
+ }
83
+ }
84
+ return template;
85
+ };
86
+ export const debugLibauthTemplate = (template, transaction) => {
87
+ const allArtifacts = transaction.inputs
88
+ .map(input => isContractUnlocker(input.unlocker) ? input.unlocker.contract : undefined)
89
+ .filter((contract) => Boolean(contract))
90
+ .map(contract => contract.artifact);
91
+ return debugTemplate(template, allArtifacts);
92
+ };
93
+ export const getBitauthUri = (template) => {
94
+ const base64toBase64Url = (base64) => base64.replace(/\+/g, '-').replace(/\//g, '_');
95
+ const payload = base64toBase64Url(binToBase64(deflate(utf8ToBin(extendedStringify(template)))));
96
+ return `https://ide.bitauth.com/import-template/${payload}`;
97
+ };
98
+ const generateAllTemplateEntities = (transactionBuilder) => {
99
+ const entities = transactionBuilder.inputs.map((input, inputIndex) => {
100
+ if (isP2PKHUnlocker(input.unlocker)) {
101
+ return generateTemplateEntitiesP2PKH(inputIndex);
102
+ }
103
+ if (isContractUnlocker(input.unlocker)) {
104
+ const encodedArgs = encodeFunctionArguments(input.unlocker.abiFunction, input.unlocker.params ?? []);
105
+ return generateTemplateEntitiesP2SH(input.unlocker.contract, input.unlocker.abiFunction, encodedArgs, inputIndex);
106
+ }
107
+ throw new Error('Unknown unlocker type');
108
+ });
109
+ return entities.reduce((acc, entity) => ({ ...acc, ...entity }), {});
110
+ };
111
+ const generateAllTemplateScripts = (transactionBuilder) => {
112
+ const scripts = transactionBuilder.inputs.map((input, inputIndex) => {
113
+ if (isP2PKHUnlocker(input.unlocker)) {
114
+ return generateTemplateScriptsP2PKH(inputIndex);
115
+ }
116
+ if (isContractUnlocker(input.unlocker)) {
117
+ const encodedArgs = encodeFunctionArguments(input.unlocker.abiFunction, input.unlocker.params ?? []);
118
+ return generateTemplateScriptsP2SH(input.unlocker.contract, input.unlocker.abiFunction, encodedArgs, input.unlocker.contract.encodedConstructorArgs, inputIndex);
119
+ }
120
+ throw new Error('Unknown unlocker type');
121
+ });
122
+ return scripts.reduce((acc, script) => ({ ...acc, ...script }), {});
123
+ };
124
+ const generateAllTemplateScenarios = (libauthTransaction, transactionBuilder) => {
125
+ const scenarios = transactionBuilder.inputs.map((input, inputIndex) => {
126
+ if (isP2PKHUnlocker(input.unlocker)) {
127
+ return generateTemplateScenariosP2PKH(libauthTransaction, transactionBuilder, inputIndex);
128
+ }
129
+ if (isContractUnlocker(input.unlocker)) {
130
+ const encodedArgs = encodeFunctionArguments(input.unlocker.abiFunction, input.unlocker.params ?? []);
131
+ return generateTemplateScenarios(input.unlocker.contract, libauthTransaction, transactionBuilder, input.unlocker.abiFunction, encodedArgs, inputIndex);
132
+ }
133
+ throw new Error('Unknown unlocker type');
134
+ });
135
+ return scenarios.reduce((acc, scenario) => ({ ...acc, ...scenario }), {});
136
+ };
137
+ const generateTemplateEntitiesP2PKH = (inputIndex) => {
138
+ const lockScriptName = `p2pkh_placeholder_lock_${inputIndex}`;
139
+ const unlockScriptName = `p2pkh_placeholder_unlock_${inputIndex}`;
140
+ return {
141
+ [`signer_${inputIndex}`]: {
142
+ scripts: [lockScriptName, unlockScriptName],
143
+ description: `P2PKH data for input ${inputIndex}`,
144
+ name: `P2PKH Signer (input #${inputIndex})`,
145
+ variables: {
146
+ [`signature_${inputIndex}`]: {
147
+ description: '',
148
+ name: `P2PKH Signature (input #${inputIndex})`,
149
+ type: 'WalletData',
150
+ },
151
+ [`public_key_${inputIndex}`]: {
152
+ description: '',
153
+ name: `P2PKH public key (input #${inputIndex})`,
154
+ type: 'WalletData',
155
+ },
156
+ },
157
+ },
158
+ };
159
+ };
160
+ const generateTemplateEntitiesP2SH = (contract, abiFunction, encodedFunctionArgs, inputIndex) => {
161
+ const entities = {
162
+ [contract.artifact.contractName + '_input' + inputIndex + '_parameters']: {
163
+ description: 'Contract creation and function parameters',
164
+ name: `${contract.artifact.contractName} (input #${inputIndex})`,
165
+ scripts: [
166
+ getLockScriptName(contract),
167
+ getUnlockScriptName(contract, abiFunction, inputIndex),
168
+ ],
169
+ variables: {
170
+ ...createWalletTemplateVariables(contract.artifact, abiFunction, encodedFunctionArgs),
171
+ ...generateFunctionIndexTemplateVariable(contract.artifact.abi),
172
+ },
173
+ },
174
+ };
175
+ return entities;
176
+ };
177
+ const createWalletTemplateVariables = (artifact, abiFunction, encodedFunctionArgs) => {
178
+ const functionParameters = Object.fromEntries(abiFunction.inputs.map((input, index) => ([
179
+ input.name,
180
+ {
181
+ description: `"${input.name}" parameter of function "${abiFunction.name}"`,
182
+ name: input.name,
183
+ type: encodedFunctionArgs[index] instanceof SignatureTemplate ? 'Key' : 'WalletData',
184
+ },
185
+ ])));
186
+ const constructorParameters = Object.fromEntries(artifact.constructorInputs.map((input) => ([
187
+ input.name,
188
+ {
189
+ description: `"${input.name}" parameter of this contract`,
190
+ name: input.name,
191
+ type: 'WalletData',
192
+ },
193
+ ])));
194
+ return { ...functionParameters, ...constructorParameters };
195
+ };
196
+ const generateTemplateScriptsP2PKH = (inputIndex) => {
197
+ const lockScriptName = `p2pkh_placeholder_lock_${inputIndex}`;
198
+ const unlockScriptName = `p2pkh_placeholder_unlock_${inputIndex}`;
199
+ const scripts = {
200
+ [unlockScriptName]: {
201
+ passes: [`P2PKH_spend_input${inputIndex}_evaluate`],
202
+ name: `P2PKH Unlock (input #${inputIndex})`,
203
+ script: `<signature_${inputIndex}>\n<public_key_${inputIndex}>`,
204
+ unlocks: lockScriptName,
205
+ },
206
+ [lockScriptName]: {
207
+ lockingType: 'standard',
208
+ name: `P2PKH Lock (input #${inputIndex})`,
209
+ script: `OP_DUP\nOP_HASH160 <$(<public_key_${inputIndex}> OP_HASH160\n)> OP_EQUALVERIFY\nOP_CHECKSIG`,
210
+ },
211
+ };
212
+ return scripts;
213
+ };
214
+ const generateTemplateScriptsP2SH = (contract, abiFunction, encodedFunctionArgs, encodedConstructorArgs, inputIndex) => {
215
+ const unlockingScriptName = getUnlockScriptName(contract, abiFunction, inputIndex);
216
+ const lockingScriptName = getLockScriptName(contract);
217
+ return {
218
+ [unlockingScriptName]: generateTemplateUnlockScript(contract, abiFunction, encodedFunctionArgs, inputIndex),
219
+ [lockingScriptName]: generateTemplateLockScript(contract, encodedConstructorArgs),
220
+ };
221
+ };
222
+ const generateTemplateLockScript = (contract, constructorArguments) => {
223
+ return {
224
+ lockingType: contract.addressType,
225
+ name: contract.artifact.contractName,
226
+ script: [
227
+ `// "${contract.artifact.contractName}" contract constructor parameters`,
228
+ formatParametersForDebugging(contract.artifact.constructorInputs, constructorArguments),
229
+ '',
230
+ '// bytecode',
231
+ formatBytecodeForDebugging(contract.artifact),
232
+ ].join('\n'),
233
+ };
234
+ };
235
+ const generateTemplateUnlockScript = (contract, abiFunction, encodedFunctionArgs, inputIndex) => {
236
+ const scenarioIdentifier = `${contract.artifact.contractName}_${abiFunction.name}_input${inputIndex}_evaluate`;
237
+ const functionIndex = contract.artifact.abi.findIndex((func) => func.name === abiFunction.name);
238
+ const functionIndexString = contract.artifact.abi.length > 1
239
+ ? ['// function index in contract', `<function_index> // int = <${functionIndex}>`, '']
240
+ : [];
241
+ return {
242
+ // this unlocking script must pass our only scenario
243
+ passes: [scenarioIdentifier],
244
+ name: `${abiFunction.name} (input #${inputIndex})`,
245
+ script: [
246
+ `// "${abiFunction.name}" function parameters`,
247
+ formatParametersForDebugging(abiFunction.inputs, encodedFunctionArgs),
248
+ '',
249
+ ...functionIndexString,
250
+ ].join('\n'),
251
+ unlocks: getLockScriptName(contract),
252
+ };
253
+ };
254
+ const generateTemplateScenarios = (contract, libauthTransaction, transactionBuilder, abiFunction, encodedFunctionArgs, inputIndex) => {
255
+ const artifact = contract.artifact;
256
+ const encodedConstructorArgs = contract.encodedConstructorArgs;
257
+ const scenarioIdentifier = `${artifact.contractName}_${abiFunction.name}_input${inputIndex}_evaluate`;
258
+ const scenarios = {
259
+ // single scenario to spend out transaction under test given the CashScript parameters provided
260
+ [scenarioIdentifier]: {
261
+ name: `Evaluate ${artifact.contractName} ${abiFunction.name} (input #${inputIndex})`,
262
+ description: 'An example evaluation where this script execution passes.',
263
+ data: {
264
+ // encode values for the variables defined above in `entities` property
265
+ bytecode: {
266
+ ...generateTemplateScenarioParametersFunctionIndex(abiFunction, contract.artifact.abi),
267
+ ...generateTemplateScenarioParametersValues(abiFunction.inputs, encodedFunctionArgs),
268
+ ...generateTemplateScenarioParametersValues(artifact.constructorInputs, encodedConstructorArgs),
269
+ },
270
+ keys: {
271
+ privateKeys: generateTemplateScenarioKeys(abiFunction.inputs, encodedFunctionArgs),
272
+ },
273
+ },
274
+ transaction: generateTemplateScenarioTransaction(contract, libauthTransaction, transactionBuilder, inputIndex),
275
+ sourceOutputs: generateTemplateScenarioSourceOutputs(transactionBuilder, libauthTransaction, inputIndex),
276
+ },
277
+ };
278
+ return scenarios;
279
+ };
280
+ const generateTemplateScenariosP2PKH = (libauthTransaction, transactionBuilder, inputIndex) => {
281
+ const scenarioIdentifier = `P2PKH_spend_input${inputIndex}_evaluate`;
282
+ const { signature, publicKey } = getSignatureAndPubkeyFromP2PKHInput(libauthTransaction.inputs[inputIndex]);
283
+ const scenarios = {
284
+ // single scenario to spend out transaction under test given the CashScript parameters provided
285
+ [scenarioIdentifier]: {
286
+ name: `Evaluate P2PKH spend (input #${inputIndex})`,
287
+ description: 'An example evaluation where this script execution passes.',
288
+ data: {
289
+ // encode values for the variables defined above in `entities` property
290
+ bytecode: {
291
+ [`signature_${inputIndex}`]: `0x${binToHex(signature)}`,
292
+ [`public_key_${inputIndex}`]: `0x${binToHex(publicKey)}`,
293
+ },
294
+ },
295
+ transaction: generateTemplateScenarioTransaction(undefined, libauthTransaction, transactionBuilder, inputIndex),
296
+ sourceOutputs: generateTemplateScenarioSourceOutputs(transactionBuilder, libauthTransaction, inputIndex),
297
+ },
298
+ };
299
+ return scenarios;
300
+ };
301
+ const generateTemplateScenarioTransaction = (contract, libauthTransaction, transactionBuilder, slotIndex) => {
302
+ const zippedInputs = zip(transactionBuilder.inputs, libauthTransaction.inputs);
303
+ const inputs = zippedInputs.map(([csInput, libauthInput], inputIndex) => {
304
+ return {
305
+ outpointIndex: libauthInput.outpointIndex,
306
+ outpointTransactionHash: binToHex(libauthInput.outpointTransactionHash),
307
+ sequenceNumber: libauthInput.sequenceNumber,
308
+ unlockingBytecode: generateTemplateScenarioBytecode(csInput, libauthInput, inputIndex, 'p2pkh_placeholder_unlock', slotIndex === inputIndex),
309
+ };
310
+ });
311
+ const locktime = libauthTransaction.locktime;
312
+ const zippedOutputs = zip(transactionBuilder.outputs, libauthTransaction.outputs);
313
+ const outputs = zippedOutputs.map(([csOutput, libauthOutput]) => {
314
+ if (csOutput && contract) {
315
+ return {
316
+ lockingBytecode: generateTemplateScenarioTransactionOutputLockingBytecode(csOutput, contract),
317
+ token: serialiseTokenDetails(libauthOutput.token),
318
+ valueSatoshis: Number(libauthOutput.valueSatoshis),
319
+ };
320
+ }
321
+ return {
322
+ lockingBytecode: `${binToHex(libauthOutput.lockingBytecode)}`,
323
+ token: serialiseTokenDetails(libauthOutput.token),
324
+ valueSatoshis: Number(libauthOutput.valueSatoshis),
325
+ };
326
+ });
327
+ const version = libauthTransaction.version;
328
+ return { inputs, locktime, outputs, version };
329
+ };
330
+ const generateTemplateScenarioSourceOutputs = (transactionBuilder, libauthTransaction, slotIndex) => {
331
+ const zippedInputs = zip(transactionBuilder.inputs, libauthTransaction.inputs);
332
+ return zippedInputs.map(([csInput, libauthInput], inputIndex) => {
333
+ return {
334
+ lockingBytecode: generateTemplateScenarioBytecode(csInput, libauthInput, inputIndex, 'p2pkh_placeholder_lock', inputIndex === slotIndex),
335
+ valueSatoshis: Number(csInput.satoshis),
336
+ token: serialiseTokenDetails(csInput.token),
337
+ };
338
+ });
339
+ };
340
+ const generateLockingScriptParams = (contract, { unlocker }, lockScriptName) => {
341
+ if (isP2PKHUnlocker(unlocker)) {
342
+ return {
343
+ script: lockScriptName,
344
+ };
345
+ }
346
+ const constructorParamsEntries = contract.artifact.constructorInputs
347
+ .map(({ name }, index) => [
348
+ name,
349
+ addHexPrefixExceptEmpty(binToHex(unlocker.contract.encodedConstructorArgs[index])),
350
+ ]);
351
+ const constructorParams = Object.fromEntries(constructorParamsEntries);
352
+ return {
353
+ script: lockScriptName,
354
+ overrides: {
355
+ bytecode: { ...constructorParams },
356
+ },
357
+ };
358
+ };
359
+ const generateUnlockingScriptParams = (csInput, libauthInput, p2pkhScriptNameTemplate, inputIndex) => {
360
+ if (isP2PKHUnlocker(csInput.unlocker)) {
361
+ const { signature, publicKey } = getSignatureAndPubkeyFromP2PKHInput(libauthInput);
362
+ return {
363
+ script: `${p2pkhScriptNameTemplate}_${inputIndex}`,
364
+ overrides: {
365
+ bytecode: {
366
+ [`signature_${inputIndex}`]: `0x${binToHex(signature)}`,
367
+ [`public_key_${inputIndex}`]: `0x${binToHex(publicKey)}`,
368
+ },
369
+ },
370
+ };
371
+ }
372
+ const abiFunction = csInput.unlocker.abiFunction;
373
+ const contract = csInput.unlocker.contract;
374
+ const encodedFunctionArgs = encodeFunctionArguments(abiFunction, csInput.unlocker.params);
375
+ return {
376
+ script: getUnlockScriptName(contract, abiFunction, inputIndex),
377
+ overrides: {
378
+ // encode values for the variables defined above in `entities` property
379
+ bytecode: {
380
+ ...generateTemplateScenarioParametersFunctionIndex(abiFunction, contract.artifact.abi),
381
+ ...generateTemplateScenarioParametersValues(abiFunction.inputs, encodedFunctionArgs),
382
+ ...generateTemplateScenarioParametersValues(contract.artifact.constructorInputs, contract.encodedConstructorArgs),
383
+ },
384
+ keys: {
385
+ privateKeys: generateTemplateScenarioKeys(abiFunction.inputs, encodedFunctionArgs),
386
+ },
387
+ },
388
+ };
389
+ };
390
+ const generateTemplateScenarioParametersValues = (types, encodedArgs) => {
391
+ const typesAndArguments = zip(types, encodedArgs);
392
+ const entries = typesAndArguments
393
+ // SignatureTemplates are handled by the 'keys' object in the scenario
394
+ .filter(([, arg]) => !(arg instanceof SignatureTemplate))
395
+ .map(([input, arg]) => {
396
+ const encodedArgumentHex = binToHex(arg);
397
+ const prefixedEncodedArgument = addHexPrefixExceptEmpty(encodedArgumentHex);
398
+ return [input.name, prefixedEncodedArgument];
399
+ });
400
+ return Object.fromEntries(entries);
401
+ };
402
+ const generateTemplateScenarioKeys = (types, encodedArgs) => {
403
+ const typesAndArguments = zip(types, encodedArgs);
404
+ const entries = typesAndArguments
405
+ .filter(([, arg]) => arg instanceof SignatureTemplate)
406
+ .map(([input, arg]) => [input.name, binToHex(arg.privateKey)]);
407
+ return Object.fromEntries(entries);
408
+ };
409
+ // Used for generating the locking / unlocking bytecode for source outputs and inputs
410
+ const generateTemplateScenarioBytecode = (input, libauthInput, inputIndex, p2pkhScriptNameTemplate, insertSlot) => {
411
+ if (insertSlot)
412
+ return ['slot'];
413
+ if (isUnlockableUtxo(input) && isStandardUnlockableUtxo(input)) {
414
+ return generateUnlockingScriptParams(input, libauthInput, p2pkhScriptNameTemplate, inputIndex);
415
+ }
416
+ // 'slot' means that we are currently evaluating this specific input,
417
+ // {} means that it is the same script type, but not being evaluated
418
+ return {};
419
+ };
420
+ const generateTemplateScenarioTransactionOutputLockingBytecode = (csOutput, contract) => {
421
+ if (csOutput.to instanceof Uint8Array)
422
+ return binToHex(csOutput.to);
423
+ if ([contract.address, contract.tokenAddress].includes(csOutput.to))
424
+ return {};
425
+ return binToHex(addressToLockScript(csOutput.to));
426
+ };
427
+ const generateTemplateScenarioParametersFunctionIndex = (abiFunction, abi) => {
428
+ const functionIndex = abi.length > 1
429
+ ? abi.findIndex((func) => func.name === abiFunction.name)
430
+ : undefined;
431
+ return functionIndex !== undefined ? { function_index: functionIndex.toString() } : {};
432
+ };
433
+ const generateFunctionIndexTemplateVariable = (abi) => {
434
+ if (abi.length > 1) {
435
+ return {
436
+ function_index: {
437
+ description: 'Script function index to execute',
438
+ name: 'function_index',
439
+ type: 'WalletData',
440
+ },
441
+ };
442
+ }
443
+ return {};
444
+ };
445
+ //# sourceMappingURL=LibauthTemplate.js.map
@@ -0,0 +1,27 @@
1
+ import { AbiFunction, AbiInput, Artifact } from '@cashscript/utils';
2
+ import { HashType, LibauthTokenDetails, SignatureAlgorithm, TokenDetails } from '../interfaces.js';
3
+ import { type WalletTemplateScenarioBytecode, Input } from '@bitauth/libauth';
4
+ import { EncodedFunctionArgument } from '../Argument.js';
5
+ import { Contract } from '../Contract.js';
6
+ export declare const getLockScriptName: (contract: Contract) => string;
7
+ export declare const getUnlockScriptName: (contract: Contract, abiFunction: AbiFunction, inputIndex: number) => string;
8
+ export declare const getSignatureAlgorithmName: (signatureAlgorithm: SignatureAlgorithm) => string;
9
+ export declare const getHashTypeName: (hashType: HashType) => string;
10
+ export declare const addHexPrefixExceptEmpty: (value: string) => string;
11
+ export declare const formatParametersForDebugging: (types: readonly AbiInput[], args: EncodedFunctionArgument[]) => string;
12
+ export declare const formatBytecodeForDebugging: (artifact: Artifact) => string;
13
+ export declare const serialiseTokenDetails: (token?: TokenDetails | LibauthTokenDetails) => LibauthTemplateTokenDetails | undefined;
14
+ interface LibauthTemplateTokenDetails {
15
+ amount: string;
16
+ category: string;
17
+ nft?: {
18
+ capability: 'none' | 'mutable' | 'minting';
19
+ commitment: string;
20
+ };
21
+ }
22
+ export declare const lockingBytecodeIsSetToSlot: (lockingBytecode?: WalletTemplateScenarioBytecode | ["slot"]) => boolean;
23
+ export declare const getSignatureAndPubkeyFromP2PKHInput: (libauthInput: Input) => {
24
+ signature: Uint8Array;
25
+ publicKey: Uint8Array;
26
+ };
27
+ export {};
@@ -0,0 +1,89 @@
1
+ import { bytecodeToScript, formatBitAuthScript } from '@cashscript/utils';
2
+ import { HashType, SignatureAlgorithm } from '../interfaces.js';
3
+ import { hexToBin, binToHex, isHex, decodeCashAddress, assertSuccess, decodeAuthenticationInstructions } from '@bitauth/libauth';
4
+ import { zip } from '../utils.js';
5
+ import SignatureTemplate from '../SignatureTemplate.js';
6
+ export const getLockScriptName = (contract) => {
7
+ const result = decodeCashAddress(contract.address);
8
+ if (typeof result === 'string')
9
+ throw new Error(result);
10
+ return `${contract.artifact.contractName}_${binToHex(result.payload)}_lock`;
11
+ };
12
+ export const getUnlockScriptName = (contract, abiFunction, inputIndex) => {
13
+ return `${contract.artifact.contractName}_${abiFunction.name}_input${inputIndex}_unlock`;
14
+ };
15
+ export const getSignatureAlgorithmName = (signatureAlgorithm) => {
16
+ const signatureAlgorithmNames = {
17
+ [SignatureAlgorithm.SCHNORR]: 'schnorr_signature',
18
+ [SignatureAlgorithm.ECDSA]: 'ecdsa_signature',
19
+ };
20
+ return signatureAlgorithmNames[signatureAlgorithm];
21
+ };
22
+ export const getHashTypeName = (hashType) => {
23
+ const hashtypeNames = {
24
+ [HashType.SIGHASH_ALL]: 'all_outputs',
25
+ [HashType.SIGHASH_ALL | HashType.SIGHASH_ANYONECANPAY]: 'all_outputs_single_input',
26
+ [HashType.SIGHASH_ALL | HashType.SIGHASH_UTXOS]: 'all_outputs_all_utxos',
27
+ [HashType.SIGHASH_ALL | HashType.SIGHASH_ANYONECANPAY | HashType.SIGHASH_UTXOS]: 'all_outputs_single_input_INVALID_all_utxos',
28
+ [HashType.SIGHASH_SINGLE]: 'corresponding_output',
29
+ [HashType.SIGHASH_SINGLE | HashType.SIGHASH_ANYONECANPAY]: 'corresponding_output_single_input',
30
+ [HashType.SIGHASH_SINGLE | HashType.SIGHASH_UTXOS]: 'corresponding_output_all_utxos',
31
+ [HashType.SIGHASH_SINGLE | HashType.SIGHASH_ANYONECANPAY | HashType.SIGHASH_UTXOS]: 'corresponding_output_single_input_INVALID_all_utxos',
32
+ [HashType.SIGHASH_NONE]: 'no_outputs',
33
+ [HashType.SIGHASH_NONE | HashType.SIGHASH_ANYONECANPAY]: 'no_outputs_single_input',
34
+ [HashType.SIGHASH_NONE | HashType.SIGHASH_UTXOS]: 'no_outputs_all_utxos',
35
+ [HashType.SIGHASH_NONE | HashType.SIGHASH_ANYONECANPAY | HashType.SIGHASH_UTXOS]: 'no_outputs_single_input_INVALID_all_utxos',
36
+ };
37
+ return hashtypeNames[hashType];
38
+ };
39
+ export const addHexPrefixExceptEmpty = (value) => {
40
+ return value.length > 0 ? `0x${value}` : '';
41
+ };
42
+ export const formatParametersForDebugging = (types, args) => {
43
+ if (types.length === 0)
44
+ return '// none';
45
+ // We reverse the arguments because the order of the arguments in the bytecode is reversed
46
+ const typesAndArguments = zip(types, args).reverse();
47
+ return typesAndArguments.map(([input, arg]) => {
48
+ if (arg instanceof SignatureTemplate) {
49
+ const signatureAlgorithmName = getSignatureAlgorithmName(arg.getSignatureAlgorithm());
50
+ const hashtypeName = getHashTypeName(arg.getHashType(false));
51
+ return `<${input.name}.${signatureAlgorithmName}.${hashtypeName}> // ${input.type}`;
52
+ }
53
+ const typeStr = input.type === 'bytes' ? `bytes${arg.length}` : input.type;
54
+ // we output these values as pushdata, comment will contain the type and the value of the variable
55
+ // e.g. <timeout> // int = <0xa08601>
56
+ return `<${input.name}> // ${typeStr} = <${`0x${binToHex(arg)}`}>`;
57
+ }).join('\n');
58
+ };
59
+ export const formatBytecodeForDebugging = (artifact) => {
60
+ if (!artifact.debug) {
61
+ return artifact.bytecode
62
+ .split(' ')
63
+ .map((asmElement) => (isHex(asmElement) ? `<0x${asmElement}>` : asmElement))
64
+ .join('\n');
65
+ }
66
+ return formatBitAuthScript(bytecodeToScript(hexToBin(artifact.debug.bytecode)), artifact.debug.sourceMap, artifact.source);
67
+ };
68
+ export const serialiseTokenDetails = (token) => {
69
+ if (!token)
70
+ return undefined;
71
+ return {
72
+ amount: token.amount.toString(),
73
+ category: token.category instanceof Uint8Array ? binToHex(token.category) : token.category,
74
+ nft: token.nft ? {
75
+ capability: token.nft.capability,
76
+ commitment: token.nft.commitment instanceof Uint8Array ? binToHex(token.nft.commitment) : token.nft.commitment,
77
+ } : undefined,
78
+ };
79
+ };
80
+ export const lockingBytecodeIsSetToSlot = (lockingBytecode) => {
81
+ return Array.isArray(lockingBytecode) && lockingBytecode.length === 1 && lockingBytecode[0] === 'slot';
82
+ };
83
+ export const getSignatureAndPubkeyFromP2PKHInput = (libauthInput) => {
84
+ const inputData = (assertSuccess(decodeAuthenticationInstructions(libauthInput.unlockingBytecode)));
85
+ const signature = inputData[0].data;
86
+ const publicKey = inputData[1].data;
87
+ return { signature, publicKey };
88
+ };
89
+ //# sourceMappingURL=utils.js.map
@@ -1,7 +1,8 @@
1
- import { Utxo, Network } from '../interfaces.js';
1
+ import { Utxo, Network, VmTarget } from '../interfaces.js';
2
2
  import NetworkProvider from './NetworkProvider.js';
3
- interface MockNetworkProviderOptions {
3
+ export interface MockNetworkProviderOptions {
4
4
  updateUtxoSet: boolean;
5
+ vmTarget?: VmTarget;
5
6
  }
6
7
  export default class MockNetworkProvider implements NetworkProvider {
7
8
  private utxoSet;
@@ -9,6 +10,7 @@ export default class MockNetworkProvider implements NetworkProvider {
9
10
  network: Network;
10
11
  blockHeight: number;
11
12
  options: MockNetworkProviderOptions;
13
+ vmTarget: VmTarget;
12
14
  constructor(options?: Partial<MockNetworkProviderOptions>);
13
15
  getUtxos(address: string): Promise<Utxo[]>;
14
16
  setBlockHeight(newBlockHeight: number): void;
@@ -18,4 +20,3 @@ export default class MockNetworkProvider implements NetworkProvider {
18
20
  addUtxo(addressOrLockingBytecode: string, utxo: Utxo): void;
19
21
  reset(): void;
20
22
  }
21
- export {};
@@ -1,13 +1,7 @@
1
1
  import { binToHex, decodeTransactionUnsafe, hexToBin, isHex } from '@bitauth/libauth';
2
2
  import { sha256 } from '@cashscript/utils';
3
- import { Network } from '../interfaces.js';
4
- import { addressToLockScript, libauthTokenDetailsToCashScriptTokenDetails, randomUtxo } from '../utils.js';
5
- // redeclare the addresses from vars.ts instead of importing them
6
- const aliceAddress = 'bchtest:qpgjmwev3spwlwkgmyjrr2s2cvlkkzlewq62mzgjnp';
7
- const bobAddress = 'bchtest:qz6q5gqnxdldkr07xpls5474mmzmlesd6qnux4skuc';
8
- const carolAddress = 'bchtest:qqsr7nqwe6rq5crj63gy5gdqchpnwmguusmr7tfmsj';
9
- // We are setting the default updateUtxoSet to 'false' so that it doesn't break the current behaviour
10
- // TODO: in a future breaking release we want to set this to 'true' by default
3
+ import { Network, VmTarget } from '../interfaces.js';
4
+ import { addressToLockScript, libauthTokenDetailsToCashScriptTokenDetails } from '../utils.js';
11
5
  export default class MockNetworkProvider {
12
6
  constructor(options) {
13
7
  // we use lockingBytecode hex as the key for utxoMap to make cash addresses and token addresses interchangeable
@@ -15,12 +9,8 @@ export default class MockNetworkProvider {
15
9
  this.transactionMap = {};
16
10
  this.network = Network.MOCKNET;
17
11
  this.blockHeight = 133700;
18
- this.options = { updateUtxoSet: false, ...options };
19
- for (let i = 0; i < 3; i += 1) {
20
- this.addUtxo(aliceAddress, randomUtxo());
21
- this.addUtxo(bobAddress, randomUtxo());
22
- this.addUtxo(carolAddress, randomUtxo());
23
- }
12
+ this.options = { updateUtxoSet: true, ...options };
13
+ this.vmTarget = this.options.vmTarget ?? VmTarget.BCH_2025_05;
24
14
  }
25
15
  async getUtxos(address) {
26
16
  const addressLockingBytecode = binToHex(addressToLockScript(address));