cashscript 0.9.1 → 0.10.0-next.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.
Files changed (37) hide show
  1. package/dist/{Contract.d.ts → src/Contract.d.ts} +7 -5
  2. package/dist/{Contract.js → src/Contract.js} +12 -2
  3. package/dist/{Errors.d.ts → src/Errors.d.ts} +2 -2
  4. package/dist/{Errors.js → src/Errors.js} +3 -3
  5. package/dist/src/LibauthTemplate.d.ts +12 -0
  6. package/dist/src/LibauthTemplate.js +472 -0
  7. package/dist/{SignatureTemplate.d.ts → src/SignatureTemplate.d.ts} +1 -1
  8. package/dist/{Transaction.d.ts → src/Transaction.d.ts} +13 -12
  9. package/dist/{Transaction.js → src/Transaction.js} +76 -81
  10. package/dist/{TransactionBuilder.js → src/TransactionBuilder.js} +3 -3
  11. package/dist/{index.d.ts → src/index.d.ts} +3 -2
  12. package/dist/{index.js → src/index.js} +3 -2
  13. package/dist/{interfaces.d.ts → src/interfaces.d.ts} +1 -1
  14. package/dist/src/network/MockNetworkProvider.d.ts +14 -0
  15. package/dist/src/network/MockNetworkProvider.js +46 -0
  16. package/dist/{network → src/network}/index.d.ts +1 -0
  17. package/dist/{network → src/network}/index.js +1 -0
  18. package/dist/{utils.d.ts → src/utils.d.ts} +8 -5
  19. package/dist/{utils.js → src/utils.js} +61 -24
  20. package/dist/test/JestExtensions.d.ts +9 -0
  21. package/dist/test/JestExtensions.js +66 -0
  22. package/package.json +8 -6
  23. /package/dist/{Argument.d.ts → src/Argument.d.ts} +0 -0
  24. /package/dist/{Argument.js → src/Argument.js} +0 -0
  25. /package/dist/{SignatureTemplate.js → src/SignatureTemplate.js} +0 -0
  26. /package/dist/{TransactionBuilder.d.ts → src/TransactionBuilder.d.ts} +0 -0
  27. /package/dist/{constants.d.ts → src/constants.d.ts} +0 -0
  28. /package/dist/{constants.js → src/constants.js} +0 -0
  29. /package/dist/{interfaces.js → src/interfaces.js} +0 -0
  30. /package/dist/{network → src/network}/BitcoinRpcNetworkProvider.d.ts +0 -0
  31. /package/dist/{network → src/network}/BitcoinRpcNetworkProvider.js +0 -0
  32. /package/dist/{network → src/network}/ElectrumNetworkProvider.d.ts +0 -0
  33. /package/dist/{network → src/network}/ElectrumNetworkProvider.js +0 -0
  34. /package/dist/{network → src/network}/FullStackNetworkProvider.d.ts +0 -0
  35. /package/dist/{network → src/network}/FullStackNetworkProvider.js +0 -0
  36. /package/dist/{network → src/network}/NetworkProvider.d.ts +0 -0
  37. /package/dist/{network → src/network}/NetworkProvider.js +0 -0
@@ -1,9 +1,11 @@
1
- import { Artifact } from '@cashscript/utils';
1
+ import { Artifact, Script } from '@cashscript/utils';
2
2
  import { Transaction } from './Transaction.js';
3
3
  import { Argument } from './Argument.js';
4
4
  import { Unlocker, ContractOptions, Utxo } from './interfaces.js';
5
+ import NetworkProvider from './network/NetworkProvider.js';
5
6
  export declare class Contract {
6
- private artifact;
7
+ artifact: Artifact;
8
+ constructorArgs: Argument[];
7
9
  private options?;
8
10
  name: string;
9
11
  address: string;
@@ -13,9 +15,9 @@ export declare class Contract {
13
15
  opcount: number;
14
16
  functions: Record<string, ContractFunction>;
15
17
  unlock: Record<string, ContractUnlocker>;
16
- private redeemScript;
17
- private provider;
18
- private addressType;
18
+ redeemScript: Script;
19
+ provider: NetworkProvider;
20
+ addressType: 'p2sh20' | 'p2sh32';
19
21
  constructor(artifact: Artifact, constructorArgs: Argument[], options?: ContractOptions | undefined);
20
22
  getBalance(): Promise<bigint>;
21
23
  getUtxos(): Promise<Utxo[]>;
@@ -8,6 +8,7 @@ import { ElectrumNetworkProvider } from './network/index.js';
8
8
  export class Contract {
9
9
  constructor(artifact, constructorArgs, options) {
10
10
  this.artifact = artifact;
11
+ this.constructorArgs = constructorArgs;
11
12
  this.options = options;
12
13
  this.provider = this.options?.provider ?? new ElectrumNetworkProvider();
13
14
  this.addressType = this.options?.addressType ?? 'p2sh32';
@@ -73,7 +74,8 @@ export class Contract {
73
74
  // Encode passed args (this also performs type checking)
74
75
  const encodedArgs = args
75
76
  .map((arg, i) => encodeArgument(arg, abiFunction.inputs[i].type));
76
- return new Transaction(this.address, this.provider, this.redeemScript, abiFunction, encodedArgs, selector);
77
+ const unlocker = this.createUnlocker(abiFunction, selector)(...args);
78
+ return new Transaction(this, unlocker, abiFunction, encodedArgs, selector);
77
79
  };
78
80
  }
79
81
  createUnlocker(abiFunction, selector) {
@@ -82,14 +84,22 @@ export class Contract {
82
84
  const encodedArgs = args
83
85
  .map((arg, i) => encodeArgument(arg, abiFunction.inputs[i].type));
84
86
  const generateUnlockingBytecode = ({ transaction, sourceOutputs, inputIndex }) => {
87
+ // TODO: Remove old-style covenant code for v1.0 release
88
+ let covenantHashType = -1;
85
89
  const completeArgs = encodedArgs.map((arg) => {
86
90
  if (!(arg instanceof SignatureTemplate))
87
91
  return arg;
92
+ // First signature is used for sighash preimage (maybe not the best way)
93
+ if (covenantHashType < 0)
94
+ covenantHashType = arg.getHashType();
88
95
  const preimage = createSighashPreimage(transaction, sourceOutputs, inputIndex, bytecode, arg.getHashType());
89
96
  const sighash = hash256(preimage);
90
97
  return arg.generateSignature(sighash);
91
98
  });
92
- const unlockingBytecode = createInputScript(this.redeemScript, completeArgs, selector);
99
+ const preimage = abiFunction.covenant
100
+ ? createSighashPreimage(transaction, sourceOutputs, inputIndex, bytecode, covenantHashType)
101
+ : undefined;
102
+ const unlockingBytecode = createInputScript(this.redeemScript, completeArgs, selector, preimage);
93
103
  return unlockingBytecode;
94
104
  };
95
105
  const generateLockingBytecode = () => addressToLockScript(this.address);
@@ -10,8 +10,8 @@ export declare class TokensToNonTokenAddressError extends Error {
10
10
  }
11
11
  export declare class FailedTransactionError extends Error {
12
12
  reason: string;
13
- meep?: string | undefined;
14
- constructor(reason: string, meep?: string | undefined);
13
+ debugStr?: string | undefined;
14
+ constructor(reason: string, debugStr?: string | undefined);
15
15
  }
16
16
  export declare class FailedRequireError extends FailedTransactionError {
17
17
  }
@@ -14,10 +14,10 @@ export class TokensToNonTokenAddressError extends Error {
14
14
  }
15
15
  }
16
16
  export class FailedTransactionError extends Error {
17
- constructor(reason, meep) {
18
- super(`Transaction failed with reason: ${reason}${meep ? `\n${meep}` : ''}`);
17
+ constructor(reason, debugStr) {
18
+ super(`Transaction failed with reason: ${reason}${debugStr ? `\n\n${debugStr}` : ''}`);
19
19
  this.reason = reason;
20
- this.meep = meep;
20
+ this.debugStr = debugStr;
21
21
  }
22
22
  }
23
23
  export class FailedRequireError extends FailedTransactionError {
@@ -0,0 +1,12 @@
1
+ import { Artifact } from '@cashscript/utils';
2
+ import { AuthenticationTemplate, AuthenticationProgramStateBCHCHIPs } from '@bitauth/libauth';
3
+ import { Transaction } from './Transaction.js';
4
+ export declare const stringify: (any: any, spaces?: number | undefined) => string;
5
+ export declare const buildTemplate: ({ transaction, transactionHex, }: {
6
+ transaction: Transaction;
7
+ transactionHex?: string | undefined;
8
+ }) => Promise<AuthenticationTemplate>;
9
+ export declare const getBitauthUri: (template: AuthenticationTemplate) => string;
10
+ export declare const evaluateTemplate: (template: AuthenticationTemplate) => boolean;
11
+ export declare type DebugResult = AuthenticationProgramStateBCHCHIPs[];
12
+ export declare const debugTemplate: (template: AuthenticationTemplate, artifact: Artifact) => DebugResult;
@@ -0,0 +1,472 @@
1
+ import { Op, PrimitiveType, bytecodeToScript, decodeBool, decodeInt, decodeString, formatLibauthScript, } from '@cashscript/utils';
2
+ import { hash160, hexToBin, decodeTransaction, binToHex, authenticationTemplateToCompilerConfiguration, createCompiler, createVirtualMachineBCHCHIPs, binToBase64, utf8ToBin, isHex, AuthenticationErrorCommon, } from '@bitauth/libauth';
3
+ import { deflate } from 'pako';
4
+ import { isUtxoP2PKH, } from '../src/interfaces.js';
5
+ import { encodeArgument as csEncodeArgument } from './Argument.js';
6
+ import { toRegExp } from './utils.js';
7
+ // all bitauth variables must be in snake case
8
+ const snakeCase = (str) => (str
9
+ && str
10
+ .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g)
11
+ .map((s) => s.toLowerCase())
12
+ .join('_'));
13
+ const merge = (array) => array.reduce((prev, cur) => ({
14
+ ...prev,
15
+ ...{ [Object.keys(cur)[0]]: cur[Object.keys(cur)[0]] },
16
+ }), {});
17
+ const encodeArgument = (argument, typeStr) => {
18
+ if (typeStr === PrimitiveType.INT && argument === 0n) {
19
+ return Uint8Array.from([0]);
20
+ }
21
+ return csEncodeArgument(argument, typeStr);
22
+ };
23
+ // stringify version which can serialize otherwise unsupported types
24
+ export const stringify = (any, spaces) => JSON.stringify(any, (_, v) => {
25
+ if (typeof v === 'bigint') {
26
+ return `${v.toString()}`;
27
+ }
28
+ if (v instanceof Uint8Array) {
29
+ return `${binToHex(v)}`;
30
+ }
31
+ return v;
32
+ }, spaces);
33
+ const zip = (a, b) => Array.from(Array(Math.max(b.length, a.length)), (_, i) => [a[i], b[i]]);
34
+ const createScenarioTransaction = (libauthTransaction, csTransaction) => {
35
+ const contract = csTransaction.contract;
36
+ const result = {};
37
+ // only one 'slot' is allowed, otherwise {} must be used
38
+ let inputSlotInserted = false;
39
+ result.inputs = libauthTransaction.inputs.map((input, index) => {
40
+ const csInput = csTransaction.inputs[index];
41
+ const signable = isUtxoP2PKH(csInput);
42
+ let unlockingBytecode = {};
43
+ if (signable) {
44
+ unlockingBytecode = {
45
+ script: 'p2pkh_placeholder_unlock',
46
+ overrides: {
47
+ keys: {
48
+ privateKeys: {
49
+ placeholder_key: binToHex(csInput.template.privateKey),
50
+ },
51
+ },
52
+ },
53
+ };
54
+ }
55
+ else {
56
+ // assume it is our contract's input
57
+ // eslint-disable-next-line
58
+ if (!inputSlotInserted) {
59
+ unlockingBytecode = ['slot'];
60
+ inputSlotInserted = true;
61
+ }
62
+ }
63
+ return {
64
+ outpointIndex: input.outpointIndex,
65
+ outpointTransactionHash: input.outpointTransactionHash instanceof Uint8Array
66
+ ? binToHex(input.outpointTransactionHash)
67
+ : input.outpointTransactionHash,
68
+ sequenceNumber: input.sequenceNumber,
69
+ unlockingBytecode,
70
+ };
71
+ });
72
+ result.locktime = libauthTransaction.locktime;
73
+ result.outputs = libauthTransaction.outputs.map((output, index) => {
74
+ const csOutput = csTransaction.outputs[index];
75
+ let { lockingBytecode } = output;
76
+ if (typeof csOutput.to === 'string') {
77
+ if ([
78
+ contract.address,
79
+ contract.tokenAddress,
80
+ ].includes(csOutput.to)) {
81
+ lockingBytecode = {};
82
+ }
83
+ else {
84
+ for (const csInput of csTransaction.inputs) {
85
+ if (isUtxoP2PKH(csInput)) {
86
+ const inputPkh = hash160(csInput.template.getPublicKey());
87
+ if (binToHex(output.lockingBytecode).slice(6, 46)
88
+ === binToHex(inputPkh)) {
89
+ lockingBytecode = {
90
+ script: 'p2pkh_placeholder_lock',
91
+ overrides: {
92
+ keys: {
93
+ privateKeys: {
94
+ placeholder_key: binToHex(csInput.template.privateKey),
95
+ },
96
+ },
97
+ },
98
+ };
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }
104
+ return {
105
+ lockingBytecode: lockingBytecode instanceof Uint8Array
106
+ ? binToHex(lockingBytecode)
107
+ : lockingBytecode,
108
+ token: output.token,
109
+ valueSatoshis: Number(output.valueSatoshis),
110
+ };
111
+ });
112
+ result.version = libauthTransaction.version;
113
+ return result;
114
+ };
115
+ const createScenarioSourceOutputs = (csTransaction) => {
116
+ // only one 'slot' is allowed, otherwise {} must be used
117
+ let inputSlotInserted = false;
118
+ return csTransaction.inputs.map((csInput) => {
119
+ const signable = isUtxoP2PKH(csInput);
120
+ let lockingBytecode = {};
121
+ if (signable) {
122
+ lockingBytecode = {
123
+ script: 'p2pkh_placeholder_lock',
124
+ overrides: {
125
+ keys: {
126
+ privateKeys: {
127
+ placeholder_key: binToHex(csInput.template.privateKey),
128
+ },
129
+ },
130
+ },
131
+ };
132
+ }
133
+ else {
134
+ // assume it is our contract's input
135
+ // eslint-disable-next-line
136
+ if (!inputSlotInserted) {
137
+ lockingBytecode = ['slot'];
138
+ inputSlotInserted = true;
139
+ }
140
+ }
141
+ const result = {
142
+ lockingBytecode: lockingBytecode,
143
+ valueSatoshis: Number(csInput.satoshis),
144
+ };
145
+ if (csInput.token) {
146
+ result.token = {
147
+ amount: csInput.token.amount.toString(),
148
+ category: csInput.token.category,
149
+ };
150
+ if (csInput.token.nft) {
151
+ result.token.nft = {
152
+ capability: csInput.token.nft.capability,
153
+ commitment: csInput.token.nft.commitment
154
+ };
155
+ }
156
+ }
157
+ return result;
158
+ });
159
+ };
160
+ export const buildTemplate = async ({ transaction, transactionHex = undefined, // set this argument to prevent unnecessary call `transaction.build()`
161
+ }) => {
162
+ const contract = transaction.contract;
163
+ const txHex = transactionHex ?? await transaction.build();
164
+ const libauthTransaction = decodeTransaction(hexToBin(txHex));
165
+ if (typeof libauthTransaction === 'string') {
166
+ throw Error(libauthTransaction);
167
+ }
168
+ const constructorInputs = contract.artifact.constructorInputs
169
+ .slice()
170
+ .reverse();
171
+ const contractParameters = contract.constructorArgs.slice().reverse();
172
+ const abiFunction = transaction.abiFunction;
173
+ const funcName = abiFunction.name;
174
+ const functionIndex = contract.artifact.abi.findIndex((func) => func.name === funcName);
175
+ const func = contract.artifact.abi[functionIndex];
176
+ const functionInputs = func.inputs.slice().reverse();
177
+ const args = transaction.args.slice().reverse();
178
+ const hasSignatureTemplates = transaction.inputs.filter((input) => isUtxoP2PKH(input)).length;
179
+ const formattedBytecode = contract.artifact.debug
180
+ ? formatLibauthScript(bytecodeToScript(hexToBin(contract.artifact.debug.bytecode)), contract.artifact.debug.sourceMap, contract.artifact.source).split('\n')
181
+ : contract.artifact.bytecode.split(' ').map((asmElement) => {
182
+ if (isHex(asmElement)) {
183
+ return `<0x${asmElement}>`;
184
+ }
185
+ return asmElement;
186
+ });
187
+ const template = {
188
+ $schema: 'https://ide.bitauth.com/authentication-template-v0.schema.json',
189
+ description: `Imported from cashscript`,
190
+ name: contract.artifact.contractName,
191
+ supported: ['BCH_SPEC'],
192
+ version: 0,
193
+ };
194
+ // declaration of template variables and their types
195
+ template.entities = {
196
+ parameters: {
197
+ description: 'Contract creation and function parameters',
198
+ name: 'parameters',
199
+ scripts: [
200
+ 'lock',
201
+ 'unlock_lock',
202
+ ],
203
+ variables: merge([
204
+ ...functionInputs.map((input) => ({
205
+ [snakeCase(input.name)]: {
206
+ description: `"${input.name}" parameter of function "${func.name}"`,
207
+ name: input.name,
208
+ type: input.type === PrimitiveType.SIG ? 'Key' : 'WalletData',
209
+ },
210
+ })),
211
+ {
212
+ function_index: {
213
+ description: 'Script function index to execute',
214
+ name: 'function_index',
215
+ type: 'WalletData',
216
+ },
217
+ },
218
+ ...constructorInputs.map((input) => ({
219
+ [snakeCase(input.name)]: {
220
+ description: `"${input.name}" parameter of this contract`,
221
+ name: input.name,
222
+ type: 'WalletData',
223
+ },
224
+ })),
225
+ ]),
226
+ }
227
+ };
228
+ // add extra variables for the p2pkh utxos spent together with our contract
229
+ if (hasSignatureTemplates) {
230
+ template.entities.parameters.scripts.push('p2pkh_placeholder_lock', 'p2pkh_placeholder_unlock');
231
+ template.entities.parameters.variables = {
232
+ ...template.entities.parameters.variables,
233
+ placeholder_key: {
234
+ description: 'placeholder_key',
235
+ name: 'placeholder_key',
236
+ type: 'Key',
237
+ },
238
+ };
239
+ }
240
+ template.scenarios = {
241
+ // single scenario to spend out transaction under test given the CashScript parameters provided
242
+ evaluate_function: {
243
+ name: 'Evaluate',
244
+ description: 'An example evaluation where this script execution passes.',
245
+ data: {
246
+ // encode values for the variables defined above in `entities` property
247
+ bytecode: merge([
248
+ ...zip(functionInputs, args)
249
+ .filter(([input]) => input.type !== PrimitiveType.SIG)
250
+ .map(([input, arg]) => {
251
+ const hex = binToHex(arg);
252
+ const result = hex.length ? `0x${hex}` : hex;
253
+ return {
254
+ [snakeCase(input.name)]: result,
255
+ };
256
+ }),
257
+ { function_index: functionIndex.toString() },
258
+ ...constructorInputs.map((input, index) => {
259
+ const hex = binToHex(encodeArgument(contractParameters[index], constructorInputs[index].type));
260
+ const result = hex.length ? `0x${hex}` : hex;
261
+ return {
262
+ [snakeCase(input.name)]: result,
263
+ };
264
+ }),
265
+ ]),
266
+ currentBlockHeight: 2,
267
+ currentBlockTime: Math.round(+new Date() / 1000),
268
+ keys: {
269
+ privateKeys: merge([
270
+ ...zip(functionInputs, args)
271
+ .filter(([input]) => input.type === PrimitiveType.SIG)
272
+ .map(([input, arg]) => ({
273
+ [snakeCase(input.name)]: binToHex(arg.privateKey),
274
+ })),
275
+ ...(hasSignatureTemplates
276
+ ? [
277
+ {
278
+ // placeholder will be replaced by a key for each respective P2PKH input spent
279
+ placeholder_key: '<Uint8Array: 0x0000000000000000000000000000000000000000000000000000000000000000>',
280
+ },
281
+ ]
282
+ : []),
283
+ ]),
284
+ },
285
+ },
286
+ transaction: createScenarioTransaction(libauthTransaction, transaction),
287
+ sourceOutputs: createScenarioSourceOutputs(transaction),
288
+ },
289
+ };
290
+ // definition of locking scripts and unlocking scripts with their respective bytecode
291
+ template.scripts = {
292
+ unlock_lock: {
293
+ // this unlocking script must pass our only scenario
294
+ passes: ['evaluate_function'],
295
+ name: 'unlock',
296
+ // unlocking script contains the CashScript function parameters and function selector
297
+ // we output these values as pushdata, comment will contain the type and the value of the variable
298
+ // example: '<timeout> // int = <0xa08601>'
299
+ script: [
300
+ `// "${func.name}" function parameters`,
301
+ ...(functionInputs.length
302
+ ? zip(functionInputs, args).map(([input, arg]) => (input.type === PrimitiveType.SIG
303
+ ? `<${snakeCase(input.name)}.schnorr_signature.all_outputs> // ${input.type}`
304
+ : `<${snakeCase(input.name)}> // ${input.type} = <${`0x${binToHex(arg)}`}>`))
305
+ : ['// none']),
306
+ '',
307
+ ...(contract.artifact.abi.length > 1
308
+ ? [
309
+ '// function index in contract',
310
+ `<function_index> // int = <${functionIndex.toString()}>`,
311
+ '',
312
+ ]
313
+ : []),
314
+ ].join('\n'),
315
+ unlocks: 'lock',
316
+ },
317
+ lock: {
318
+ lockingType: "p2sh20",
319
+ name: 'lock',
320
+ // locking script contains the CashScript contract parameters followed by the contract opcodes
321
+ // we output these values as pushdata, comment will contain the type and the value of the variable
322
+ // example: '<timeout> // int = <0xa08601>'
323
+ script: [
324
+ `// "${contract.artifact.contractName}" contract constructor parameters`,
325
+ ...(constructorInputs.length
326
+ ? constructorInputs.map((input, index) => {
327
+ const encoded = encodeArgument(contractParameters[index], constructorInputs[index].type);
328
+ return `<${snakeCase(input.name)}> // ${input.type === 'bytes' ? `bytes${encoded.length}` : input.type} = <${`0x${binToHex(encoded)}`}>`;
329
+ })
330
+ : ['// none']),
331
+ '',
332
+ '// bytecode',
333
+ ...formattedBytecode,
334
+ ].join('\n'),
335
+ },
336
+ };
337
+ // add extra unlocking and locking script for P2PKH inputs spent alongside our contract
338
+ // this is needed for correct cross-referrences in the template
339
+ if (hasSignatureTemplates) {
340
+ template.scripts.p2pkh_placeholder_unlock = {
341
+ name: 'p2pkh_placeholder_unlock',
342
+ script: '<placeholder_key.schnorr_signature.all_outputs>\n<placeholder_key.public_key>',
343
+ unlocks: 'p2pkh_placeholder_lock',
344
+ };
345
+ template.scripts.p2pkh_placeholder_lock = {
346
+ lockingType: 'standard',
347
+ name: 'p2pkh_placeholder_lock',
348
+ script: 'OP_DUP\nOP_HASH160 <$(<placeholder_key.public_key> OP_HASH160\n)> OP_EQUALVERIFY\nOP_CHECKSIG',
349
+ };
350
+ }
351
+ return template;
352
+ };
353
+ export const getBitauthUri = (template) => {
354
+ const base64toBase64Url = (base64) => base64.replace(/\+/g, '-').replace(/\//g, '_');
355
+ const payload = base64toBase64Url(binToBase64(deflate(utf8ToBin(stringify(template)))));
356
+ return `https://ide.bitauth.com/import-template/${payload}`;
357
+ };
358
+ // internal util. instantiates the virtual machine and compiles the template into a program
359
+ const createProgram = (template) => {
360
+ const configuration = authenticationTemplateToCompilerConfiguration(template);
361
+ const vm = createVirtualMachineBCHCHIPs();
362
+ const compiler = createCompiler(configuration);
363
+ const scenarioGeneration = compiler.generateScenario({
364
+ debug: true,
365
+ lockingScriptId: undefined,
366
+ unlockingScriptId: 'unlock_lock',
367
+ scenarioId: 'evaluate_function',
368
+ });
369
+ if (typeof scenarioGeneration === 'string'
370
+ || typeof scenarioGeneration.scenario === 'string') {
371
+ // eslint-disable-next-line
372
+ throw scenarioGeneration;
373
+ }
374
+ return { vm, program: scenarioGeneration.scenario.program };
375
+ };
376
+ // evaluates the fully defined template, throws upon error
377
+ export const evaluateTemplate = (template) => {
378
+ const { vm, program } = createProgram(template);
379
+ const verifyResult = vm.verify(program);
380
+ if (typeof verifyResult === 'string') {
381
+ // eslint-disable-next-line
382
+ throw verifyResult;
383
+ }
384
+ return verifyResult;
385
+ };
386
+ // debugs the template, optionally logging the execution data
387
+ export const debugTemplate = (template, artifact) => {
388
+ const { vm, program } = createProgram(template);
389
+ const debugResult = vm.debug(program);
390
+ for (const log of artifact.debug?.logs ?? []) {
391
+ // there might be 2 elements with same instruction pointer, first from unllocking script, second from locking
392
+ const state = debugResult
393
+ .filter((debugState) => debugState.ip === log.ip)
394
+ .slice().reverse()[0];
395
+ if (!state) {
396
+ throw Error(`Instruction pointer ${log.ip} points to a non-existing state of the program`);
397
+ }
398
+ let line = `${artifact.contractName}.cash:${log.line}`;
399
+ log.data.forEach((element) => {
400
+ let value;
401
+ if (typeof element === 'string') {
402
+ value = element;
403
+ }
404
+ else {
405
+ const stackItem = state.stack.slice().reverse()[element.stackIndex];
406
+ if (!stackItem) {
407
+ throw Error(`Stack item at index ${element.stackIndex} not found at instruction pointer ${log.ip}`);
408
+ }
409
+ switch (element.type) {
410
+ case PrimitiveType.BOOL:
411
+ value = decodeBool(stackItem);
412
+ break;
413
+ case PrimitiveType.INT:
414
+ value = decodeInt(stackItem);
415
+ break;
416
+ case PrimitiveType.STRING:
417
+ value = decodeString(stackItem);
418
+ break;
419
+ default:
420
+ value = `0x${binToHex(stackItem)}`;
421
+ break;
422
+ }
423
+ }
424
+ line += ` ${value}`;
425
+ });
426
+ // actual log, do not delete :)
427
+ console.log(line);
428
+ }
429
+ const lastState = debugResult[debugResult.length - 1];
430
+ if (lastState.error) {
431
+ const requireMessage = (artifact.debug?.requireMessages ?? []).filter((message) => message.ip === lastState.ip)[0];
432
+ if (requireMessage) {
433
+ // eslint-disable-next-line
434
+ throw `${artifact.contractName}.cash:${requireMessage.line} Error in evaluating input index ${lastState.program.inputIndex} with the following message: ${requireMessage.message}.
435
+ ${lastState.error}`;
436
+ }
437
+ else {
438
+ // eslint-disable-next-line
439
+ throw `Error in evaluating input index ${lastState.program.inputIndex}.
440
+ ${lastState.error}`;
441
+ }
442
+ }
443
+ else {
444
+ // one last pass of verifications not covered by the above debugging
445
+ // checks for removed final verify
446
+ const evaluationResult = vm.verify(program);
447
+ if (typeof evaluationResult === 'string' && toRegExp([
448
+ AuthenticationErrorCommon.requiresCleanStack,
449
+ AuthenticationErrorCommon.nonEmptyControlStack,
450
+ AuthenticationErrorCommon.unsuccessfulEvaluation,
451
+ ]).test(evaluationResult)) {
452
+ const stackContents = lastState.stack.map(item => `0x${binToHex(item)}`).join(', ');
453
+ const stackContentsMessage = `\nStack contents after evaluation: ${lastState.stack.length ? stackContents : 'empty'}`;
454
+ const lastMessage = artifact.debug?.requireMessages.sort((a, b) => b.ip - a.ip)[0];
455
+ if (!lastMessage) {
456
+ // eslint-disable-next-line
457
+ throw evaluationResult + stackContentsMessage;
458
+ }
459
+ const instructionsLeft = lastState.instructions.slice(lastMessage.ip);
460
+ if (instructionsLeft.length === 0
461
+ || instructionsLeft.every(instruction => [Op.OP_NIP, Op.OP_ENDIF].includes(instruction.opcode))) {
462
+ // eslint-disable-next-line
463
+ throw `${artifact.contractName}.cash:${lastMessage.line} Error in evaluating input index ${lastState.program.inputIndex} with the following message: ${lastMessage.message}.
464
+ ${evaluationResult.replace(/Error in evaluating input index \d: /, '')}` + stackContentsMessage;
465
+ }
466
+ // eslint-disable-next-line
467
+ throw evaluationResult + stackContentsMessage;
468
+ }
469
+ }
470
+ return debugResult;
471
+ };
472
+ //# sourceMappingURL=LibauthTemplate.js.map
@@ -2,7 +2,7 @@ import { Unlocker, HashType, SignatureAlgorithm } from './interfaces.js';
2
2
  export default class SignatureTemplate {
3
3
  private hashtype;
4
4
  private signatureAlgorithm;
5
- private privateKey;
5
+ privateKey: Uint8Array;
6
6
  constructor(signer: Keypair | Uint8Array | string, hashtype?: HashType, signatureAlgorithm?: SignatureAlgorithm);
7
7
  generateSignature(payload: Uint8Array, bchForkId?: boolean): Uint8Array;
8
8
  getHashType(bchForkId?: boolean): number;
@@ -1,23 +1,23 @@
1
- import { AbiFunction, Script } from '@cashscript/utils';
2
- import { Utxo, Recipient, TokenDetails, TransactionDetails } from './interfaces.js';
3
- import NetworkProvider from './network/NetworkProvider.js';
1
+ import { AbiFunction } from '@cashscript/utils';
2
+ import { Utxo, Output, Recipient, TokenDetails, TransactionDetails, Unlocker } from './interfaces.js';
4
3
  import SignatureTemplate from './SignatureTemplate.js';
4
+ import { Contract } from './Contract.js';
5
+ import { DebugResult } from './LibauthTemplate.js';
5
6
  export declare class Transaction {
6
- private address;
7
- private provider;
8
- private redeemScript;
9
- private abiFunction;
10
- private args;
7
+ contract: Contract;
8
+ private unlocker;
9
+ abiFunction: AbiFunction;
10
+ args: (Uint8Array | SignatureTemplate)[];
11
11
  private selector?;
12
- private inputs;
13
- private outputs;
12
+ inputs: Utxo[];
13
+ outputs: Output[];
14
14
  private sequence;
15
15
  private locktime;
16
16
  private feePerByte;
17
17
  private hardcodedFee;
18
18
  private minChange;
19
19
  private tokenChange;
20
- constructor(address: string, provider: NetworkProvider, redeemScript: Script, abiFunction: AbiFunction, args: (Uint8Array | SignatureTemplate)[], selector?: number | undefined);
20
+ constructor(contract: Contract, unlocker: Unlocker, abiFunction: AbiFunction, args: (Uint8Array | SignatureTemplate)[], selector?: number | undefined);
21
21
  from(input: Utxo): this;
22
22
  from(inputs: Utxo[]): this;
23
23
  fromP2PKH(input: Utxo, template: SignatureTemplate): this;
@@ -35,7 +35,8 @@ export declare class Transaction {
35
35
  build(): Promise<string>;
36
36
  send(): Promise<TransactionDetails>;
37
37
  send(raw: true): Promise<string>;
38
+ debug(): Promise<DebugResult>;
39
+ bitauthUri(): Promise<string>;
38
40
  private getTxDetails;
39
- meep(): Promise<string>;
40
41
  private setInputsAndOutputs;
41
42
  }