cashscript 0.10.0-next.6 → 0.10.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.
- package/README.md +1 -1
- package/dist/Argument.d.ts +8 -7
- package/dist/Argument.js +3 -10
- package/dist/Contract.d.ts +4 -4
- package/dist/Contract.js +2 -2
- package/dist/Errors.d.ts +17 -53
- package/dist/Errors.js +54 -58
- package/dist/LibauthTemplate.js +66 -46
- package/dist/SignatureTemplate.d.ts +1 -0
- package/dist/SignatureTemplate.js +3 -0
- package/dist/Transaction.d.ts +3 -3
- package/dist/Transaction.js +9 -25
- package/dist/TransactionBuilder.d.ts +3 -3
- package/dist/debugging.d.ts +1 -1
- package/dist/debugging.js +19 -12
- package/dist/index.d.ts +5 -6
- package/dist/index.js +2 -3
- package/dist/interfaces.d.ts +2 -2
- package/dist/network/index.d.ts +1 -1
- package/dist/test/JestExtensions.d.ts +1 -0
- package/dist/test/JestExtensions.js +13 -1
- package/dist/utils.d.ts +4 -4
- package/dist/utils.js +6 -4
- package/jest/package.json +5 -0
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# CashScript
|
|
2
2
|
|
|
3
3
|

|
|
4
|
-
[](https://codecov.io/gh/CashScript/cashscript/)
|
|
5
5
|
[](https://www.npmjs.com/package/cashscript)
|
|
6
6
|
[](https://www.npmjs.com/package/cashscript)
|
|
7
7
|
[](https://www.npmjs.com/package/cashscript)
|
package/dist/Argument.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { AbiFunction, Artifact } from '@cashscript/utils';
|
|
2
2
|
import SignatureTemplate from './SignatureTemplate.js';
|
|
3
|
-
export
|
|
4
|
-
export
|
|
5
|
-
export
|
|
6
|
-
export
|
|
7
|
-
export
|
|
8
|
-
export declare
|
|
9
|
-
export declare
|
|
3
|
+
export type ConstructorArgument = bigint | boolean | string | Uint8Array;
|
|
4
|
+
export type FunctionArgument = ConstructorArgument | SignatureTemplate;
|
|
5
|
+
export type EncodedConstructorArgument = Uint8Array;
|
|
6
|
+
export type EncodedFunctionArgument = Uint8Array | SignatureTemplate;
|
|
7
|
+
export type EncodeFunction = (arg: FunctionArgument, typeStr: string) => EncodedFunctionArgument;
|
|
8
|
+
export declare function encodeFunctionArgument(argument: FunctionArgument, typeStr: string): EncodedFunctionArgument;
|
|
9
|
+
export declare const encodeConstructorArguments: (artifact: Artifact, constructorArgs: ConstructorArgument[], encodeFunction?: EncodeFunction) => Uint8Array[];
|
|
10
|
+
export declare const encodeFunctionArguments: (abiFunction: AbiFunction, functionArgs: FunctionArgument[], encodeFunction?: EncodeFunction) => EncodedFunctionArgument[];
|
package/dist/Argument.js
CHANGED
|
@@ -2,7 +2,7 @@ import { hexToBin } from '@bitauth/libauth';
|
|
|
2
2
|
import { BytesType, encodeBool, encodeInt, encodeString, parseType, PrimitiveType, } from '@cashscript/utils';
|
|
3
3
|
import { TypeError } from './Errors.js';
|
|
4
4
|
import SignatureTemplate from './SignatureTemplate.js';
|
|
5
|
-
export function
|
|
5
|
+
export function encodeFunctionArgument(argument, typeStr) {
|
|
6
6
|
let type = parseType(typeStr);
|
|
7
7
|
if (type === PrimitiveType.BOOL) {
|
|
8
8
|
if (typeof argument !== 'boolean') {
|
|
@@ -50,7 +50,7 @@ export function encodeArgument(argument, typeStr) {
|
|
|
50
50
|
}
|
|
51
51
|
return argument;
|
|
52
52
|
}
|
|
53
|
-
export const encodeConstructorArguments = (artifact, constructorArgs, encodeFunction =
|
|
53
|
+
export const encodeConstructorArguments = (artifact, constructorArgs, encodeFunction = encodeFunctionArgument) => {
|
|
54
54
|
// Check there's no signature templates in the constructor
|
|
55
55
|
if (constructorArgs.some((arg) => arg instanceof SignatureTemplate)) {
|
|
56
56
|
throw new Error('Cannot use signatures in constructor');
|
|
@@ -59,15 +59,8 @@ export const encodeConstructorArguments = (artifact, constructorArgs, encodeFunc
|
|
|
59
59
|
.map((arg, i) => encodeFunction(arg, artifact.constructorInputs[i].type));
|
|
60
60
|
return encodedArgs;
|
|
61
61
|
};
|
|
62
|
-
export const encodeFunctionArguments = (abiFunction, functionArgs, encodeFunction =
|
|
62
|
+
export const encodeFunctionArguments = (abiFunction, functionArgs, encodeFunction = encodeFunctionArgument) => {
|
|
63
63
|
const encodedArgs = functionArgs.map((arg, i) => encodeFunction(arg, abiFunction.inputs[i].type));
|
|
64
64
|
return encodedArgs;
|
|
65
65
|
};
|
|
66
|
-
// Note: BitAuth IDE requires 0 to be encoded as a single byte (rather than the default empty byte array)
|
|
67
|
-
// TODO: Double check this with Pat
|
|
68
|
-
export function encodeArgumentForLibauthTemplate(argument, typeStr) {
|
|
69
|
-
if (typeStr === PrimitiveType.INT && argument === 0n)
|
|
70
|
-
return Uint8Array.from([0]);
|
|
71
|
-
return encodeArgument(argument, typeStr);
|
|
72
|
-
}
|
|
73
66
|
//# sourceMappingURL=Argument.js.map
|
package/dist/Contract.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Artifact, Script } from '@cashscript/utils';
|
|
2
2
|
import { Transaction } from './Transaction.js';
|
|
3
|
-
import {
|
|
3
|
+
import { ConstructorArgument, FunctionArgument } from './Argument.js';
|
|
4
4
|
import { Unlocker, ContractOptions, Utxo, AddressType } from './interfaces.js';
|
|
5
5
|
import NetworkProvider from './network/NetworkProvider.js';
|
|
6
6
|
export declare class Contract {
|
|
@@ -18,11 +18,11 @@ export declare class Contract {
|
|
|
18
18
|
provider: NetworkProvider;
|
|
19
19
|
addressType: AddressType;
|
|
20
20
|
encodedConstructorArgs: Uint8Array[];
|
|
21
|
-
constructor(artifact: Artifact, constructorArgs:
|
|
21
|
+
constructor(artifact: Artifact, constructorArgs: ConstructorArgument[], options?: ContractOptions | undefined);
|
|
22
22
|
getBalance(): Promise<bigint>;
|
|
23
23
|
getUtxos(): Promise<Utxo[]>;
|
|
24
24
|
private createFunction;
|
|
25
25
|
private createUnlocker;
|
|
26
26
|
}
|
|
27
|
-
export
|
|
28
|
-
export
|
|
27
|
+
export type ContractFunction = (...args: FunctionArgument[]) => Transaction;
|
|
28
|
+
export type ContractUnlocker = (...args: FunctionArgument[]) => Unlocker;
|
package/dist/Contract.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { binToHex } from '@bitauth/libauth';
|
|
2
2
|
import { asmToScript, calculateBytesize, countOpcodes, generateRedeemScript, hash256, scriptToBytecode, } from '@cashscript/utils';
|
|
3
3
|
import { Transaction } from './Transaction.js';
|
|
4
|
-
import {
|
|
4
|
+
import { encodeFunctionArgument, encodeConstructorArguments, encodeFunctionArguments } from './Argument.js';
|
|
5
5
|
import { addressToLockScript, createInputScript, createSighashPreimage, scriptToAddress, } from './utils.js';
|
|
6
6
|
import SignatureTemplate from './SignatureTemplate.js';
|
|
7
7
|
import { ElectrumNetworkProvider } from './network/index.js';
|
|
@@ -77,7 +77,7 @@ export class Contract {
|
|
|
77
77
|
}
|
|
78
78
|
const bytecode = scriptToBytecode(this.redeemScript);
|
|
79
79
|
const encodedArgs = args
|
|
80
|
-
.map((arg, i) =>
|
|
80
|
+
.map((arg, i) => encodeFunctionArgument(arg, abiFunction.inputs[i].type));
|
|
81
81
|
const generateUnlockingBytecode = ({ transaction, sourceOutputs, inputIndex }) => {
|
|
82
82
|
// TODO: Remove old-style covenant code for v1.0 release
|
|
83
83
|
let covenantHashType = -1;
|
package/dist/Errors.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RequireStatement, Type } from '@cashscript/utils';
|
|
1
|
+
import { Artifact, RequireStatement, Type } from '@cashscript/utils';
|
|
2
2
|
export declare class TypeError extends Error {
|
|
3
3
|
constructor(actual: string, expected: Type);
|
|
4
4
|
}
|
|
@@ -8,64 +8,28 @@ export declare class OutputSatoshisTooSmallError extends Error {
|
|
|
8
8
|
export declare class TokensToNonTokenAddressError extends Error {
|
|
9
9
|
constructor(address: string);
|
|
10
10
|
}
|
|
11
|
+
export declare class NoDebugInformationInArtifactError extends Error {
|
|
12
|
+
constructor();
|
|
13
|
+
}
|
|
11
14
|
export declare class FailedTransactionError extends Error {
|
|
12
15
|
reason: string;
|
|
13
16
|
bitauthUri?: string | undefined;
|
|
14
17
|
constructor(reason: string, bitauthUri?: string | undefined);
|
|
15
18
|
}
|
|
16
|
-
export declare class
|
|
17
|
-
|
|
19
|
+
export declare class FailedTransactionEvaluationError extends FailedTransactionError {
|
|
20
|
+
artifact: Artifact;
|
|
21
|
+
failingInstructionPointer: number;
|
|
22
|
+
inputIndex: number;
|
|
23
|
+
bitauthUri: string;
|
|
24
|
+
libauthErrorMessage: string;
|
|
25
|
+
constructor(artifact: Artifact, failingInstructionPointer: number, inputIndex: number, bitauthUri: string, libauthErrorMessage: string);
|
|
26
|
+
}
|
|
27
|
+
export declare class FailedRequireError extends FailedTransactionError {
|
|
28
|
+
artifact: Artifact;
|
|
29
|
+
failingInstructionPointer: number;
|
|
18
30
|
requireStatement: RequireStatement;
|
|
19
31
|
inputIndex: number;
|
|
32
|
+
bitauthUri: string;
|
|
20
33
|
libauthErrorMessage?: string | undefined;
|
|
21
|
-
bitauthUri?: string;
|
|
22
|
-
constructor(contractName: string, requireStatement: RequireStatement, inputIndex: number, libauthErrorMessage?: string | undefined);
|
|
23
|
-
}
|
|
24
|
-
export declare enum NodeErrorReason {
|
|
25
|
-
EVAL_FALSE = "Script evaluated without error but finished with a false/empty top stack element",
|
|
26
|
-
VERIFY = "Script failed an OP_VERIFY operation",
|
|
27
|
-
EQUALVERIFY = "Script failed an OP_EQUALVERIFY operation",
|
|
28
|
-
CHECKMULTISIGVERIFY = "Script failed an OP_CHECKMULTISIGVERIFY operation",
|
|
29
|
-
CHECKSIGVERIFY = "Script failed an OP_CHECKSIGVERIFY operation",
|
|
30
|
-
CHECKDATASIGVERIFY = "Script failed an OP_CHECKDATASIGVERIFY operation",
|
|
31
|
-
NUMEQUALVERIFY = "Script failed an OP_NUMEQUALVERIFY operation",
|
|
32
|
-
SCRIPT_SIZE = "Script is too big",
|
|
33
|
-
PUSH_SIZE = "Push value size limit exceeded",
|
|
34
|
-
OP_COUNT = "Operation limit exceeded",
|
|
35
|
-
STACK_SIZE = "Stack size limit exceeded",
|
|
36
|
-
SIG_COUNT = "Signature count negative or greater than pubkey count",
|
|
37
|
-
PUBKEY_COUNT = "Pubkey count negative or limit exceeded",
|
|
38
|
-
INVALID_OPERAND_SIZE = "Invalid operand size",
|
|
39
|
-
INVALID_NUMBER_RANGE = "Given operand is not a number within the valid range",
|
|
40
|
-
IMPOSSIBLE_ENCODING = "The requested encoding is impossible to satisfy",
|
|
41
|
-
INVALID_SPLIT_RANGE = "Invalid OP_SPLIT range",
|
|
42
|
-
INVALID_BIT_COUNT = "Invalid number of bit set in OP_CHECKMULTISIG",
|
|
43
|
-
BAD_OPCODE = "Opcode missing or not understood",
|
|
44
|
-
DISABLED_OPCODE = "Attempted to use a disabled opcode",
|
|
45
|
-
INVALID_STACK_OPERATION = "Operation not valid with the current stack size",
|
|
46
|
-
INVALID_ALTSTACK_OPERATION = "Operation not valid with the current altstack size",
|
|
47
|
-
OP_RETURN = "OP_RETURN was encountered",
|
|
48
|
-
UNBALANCED_CONDITIONAL = "Invalid OP_IF construction",
|
|
49
|
-
DIV_BY_ZERO = "Division by zero error",
|
|
50
|
-
MOD_BY_ZERO = "Modulo by zero error",
|
|
51
|
-
INVALID_BITFIELD_SIZE = "Bitfield of unexpected size error",
|
|
52
|
-
INVALID_BIT_RANGE = "Bitfield's bit out of the expected range",
|
|
53
|
-
NEGATIVE_LOCKTIME = "Negative locktime",
|
|
54
|
-
UNSATISFIED_LOCKTIME = "Locktime requirement not satisfied",
|
|
55
|
-
SIG_HASHTYPE = "Signature hash type missing or not understood",
|
|
56
|
-
SIG_DER = "Non-canonical DER signature",
|
|
57
|
-
MINIMALDATA = "Data push larger than necessary",
|
|
58
|
-
SIG_PUSHONLY = "Only push operators allowed in signature scripts",
|
|
59
|
-
SIG_HIGH_S = "Non-canonical signature: S value is unnecessarily high",
|
|
60
|
-
MINIMALIF = "OP_IF/NOTIF argument must be minimal",
|
|
61
|
-
SIG_NULLFAIL = "Signature must be zero for failed CHECK(MULTI)SIG operation",
|
|
62
|
-
SIG_BADLENGTH = "Signature cannot be 65 bytes in CHECKMULTISIG",
|
|
63
|
-
SIG_NONSCHNORR = "Only Schnorr signatures allowed in this operation",
|
|
64
|
-
DISCOURAGE_UPGRADABLE_NOPS = "NOPx reserved for soft-fork upgrades",
|
|
65
|
-
PUBKEYTYPE = "Public key is neither compressed or uncompressed",
|
|
66
|
-
CLEANSTACK = "Script did not clean its stack",
|
|
67
|
-
NONCOMPRESSED_PUBKEY = "Using non-compressed public key",
|
|
68
|
-
ILLEGAL_FORKID = "Illegal use of SIGHASH_FORKID",
|
|
69
|
-
MUST_USE_FORKID = "Signature must use SIGHASH_FORKID",
|
|
70
|
-
UNKNOWN = "unknown error"
|
|
34
|
+
constructor(artifact: Artifact, failingInstructionPointer: number, requireStatement: RequireStatement, inputIndex: number, bitauthUri: string, libauthErrorMessage?: string | undefined);
|
|
71
35
|
}
|
package/dist/Errors.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sourceMapToLocationData } from '@cashscript/utils';
|
|
1
2
|
export class TypeError extends Error {
|
|
2
3
|
constructor(actual, expected) {
|
|
3
4
|
super(`Found type '${actual}' where type '${expected.toString()}' was expected`);
|
|
@@ -13,73 +14,68 @@ export class TokensToNonTokenAddressError extends Error {
|
|
|
13
14
|
super(`Tried to send tokens to an address without token support, ${address}.`);
|
|
14
15
|
}
|
|
15
16
|
}
|
|
17
|
+
export class NoDebugInformationInArtifactError extends Error {
|
|
18
|
+
constructor() {
|
|
19
|
+
super('No debug information found in artifact, please recompile with cashc version 0.10.0 or newer.');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
16
22
|
export class FailedTransactionError extends Error {
|
|
17
23
|
constructor(reason, bitauthUri) {
|
|
18
|
-
super(`${reason}
|
|
24
|
+
super(`${reason}${bitauthUri ? `\n\nBitauth URI: ${bitauthUri}` : ''}`);
|
|
19
25
|
this.reason = reason;
|
|
20
26
|
this.bitauthUri = bitauthUri;
|
|
21
27
|
}
|
|
22
28
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
export class FailedTransactionEvaluationError extends FailedTransactionError {
|
|
30
|
+
constructor(artifact, failingInstructionPointer, inputIndex, bitauthUri, libauthErrorMessage) {
|
|
31
|
+
let message = `${artifact.contractName}.cash Error in transaction at input ${inputIndex} in contract ${artifact.contractName}.cash.\nReason: ${libauthErrorMessage}`;
|
|
32
|
+
if (artifact.debug) {
|
|
33
|
+
const { statement, lineNumber } = getLocationDataForInstructionPointer(artifact, failingInstructionPointer);
|
|
34
|
+
message = `${artifact.contractName}.cash:${lineNumber} Error in transaction at input ${inputIndex} in contract ${artifact.contractName}.cash at line ${lineNumber}.\nReason: ${libauthErrorMessage}\nFailing statement: ${statement}`;
|
|
35
|
+
}
|
|
36
|
+
super(message, bitauthUri);
|
|
37
|
+
this.artifact = artifact;
|
|
38
|
+
this.failingInstructionPointer = failingInstructionPointer;
|
|
39
|
+
this.inputIndex = inputIndex;
|
|
40
|
+
this.bitauthUri = bitauthUri;
|
|
41
|
+
this.libauthErrorMessage = libauthErrorMessage;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export class FailedRequireError extends FailedTransactionError {
|
|
45
|
+
constructor(artifact, failingInstructionPointer, requireStatement, inputIndex, bitauthUri, libauthErrorMessage) {
|
|
46
|
+
let { statement, lineNumber } = getLocationDataForInstructionPointer(artifact, failingInstructionPointer);
|
|
47
|
+
if (!statement.includes('require')) {
|
|
48
|
+
statement = requireStatement.message
|
|
49
|
+
? `require(${statement}, "${requireStatement.message}")`
|
|
50
|
+
: `require(${statement})`;
|
|
51
|
+
// Sometimes in reconstructed multiline require statements, we get double commas
|
|
52
|
+
statement = statement.replace(/,,/g, ',');
|
|
53
|
+
}
|
|
54
|
+
const baseMessage = `${artifact.contractName}.cash:${lineNumber} Require statement failed at input ${inputIndex} in contract ${artifact.contractName}.cash at line ${lineNumber}`;
|
|
55
|
+
const baseMessageWithRequireMessage = `${baseMessage} with the following message: ${requireStatement.message}`;
|
|
56
|
+
const fullMessage = `${requireStatement.message ? baseMessageWithRequireMessage : baseMessage}.\nFailing statement: ${statement}`;
|
|
57
|
+
super(fullMessage, bitauthUri);
|
|
58
|
+
this.artifact = artifact;
|
|
59
|
+
this.failingInstructionPointer = failingInstructionPointer;
|
|
30
60
|
this.requireStatement = requireStatement;
|
|
31
61
|
this.inputIndex = inputIndex;
|
|
62
|
+
this.bitauthUri = bitauthUri;
|
|
32
63
|
this.libauthErrorMessage = libauthErrorMessage;
|
|
33
64
|
}
|
|
34
65
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
NodeErrorReason["PUBKEY_COUNT"] = "Pubkey count negative or limit exceeded";
|
|
51
|
-
NodeErrorReason["INVALID_OPERAND_SIZE"] = "Invalid operand size";
|
|
52
|
-
NodeErrorReason["INVALID_NUMBER_RANGE"] = "Given operand is not a number within the valid range";
|
|
53
|
-
NodeErrorReason["IMPOSSIBLE_ENCODING"] = "The requested encoding is impossible to satisfy";
|
|
54
|
-
NodeErrorReason["INVALID_SPLIT_RANGE"] = "Invalid OP_SPLIT range";
|
|
55
|
-
NodeErrorReason["INVALID_BIT_COUNT"] = "Invalid number of bit set in OP_CHECKMULTISIG";
|
|
56
|
-
NodeErrorReason["BAD_OPCODE"] = "Opcode missing or not understood";
|
|
57
|
-
NodeErrorReason["DISABLED_OPCODE"] = "Attempted to use a disabled opcode";
|
|
58
|
-
NodeErrorReason["INVALID_STACK_OPERATION"] = "Operation not valid with the current stack size";
|
|
59
|
-
NodeErrorReason["INVALID_ALTSTACK_OPERATION"] = "Operation not valid with the current altstack size";
|
|
60
|
-
NodeErrorReason["OP_RETURN"] = "OP_RETURN was encountered";
|
|
61
|
-
NodeErrorReason["UNBALANCED_CONDITIONAL"] = "Invalid OP_IF construction";
|
|
62
|
-
NodeErrorReason["DIV_BY_ZERO"] = "Division by zero error";
|
|
63
|
-
NodeErrorReason["MOD_BY_ZERO"] = "Modulo by zero error";
|
|
64
|
-
NodeErrorReason["INVALID_BITFIELD_SIZE"] = "Bitfield of unexpected size error";
|
|
65
|
-
NodeErrorReason["INVALID_BIT_RANGE"] = "Bitfield's bit out of the expected range";
|
|
66
|
-
NodeErrorReason["NEGATIVE_LOCKTIME"] = "Negative locktime";
|
|
67
|
-
NodeErrorReason["UNSATISFIED_LOCKTIME"] = "Locktime requirement not satisfied";
|
|
68
|
-
NodeErrorReason["SIG_HASHTYPE"] = "Signature hash type missing or not understood";
|
|
69
|
-
NodeErrorReason["SIG_DER"] = "Non-canonical DER signature";
|
|
70
|
-
NodeErrorReason["MINIMALDATA"] = "Data push larger than necessary";
|
|
71
|
-
NodeErrorReason["SIG_PUSHONLY"] = "Only push operators allowed in signature scripts";
|
|
72
|
-
NodeErrorReason["SIG_HIGH_S"] = "Non-canonical signature: S value is unnecessarily high";
|
|
73
|
-
NodeErrorReason["MINIMALIF"] = "OP_IF/NOTIF argument must be minimal";
|
|
74
|
-
NodeErrorReason["SIG_NULLFAIL"] = "Signature must be zero for failed CHECK(MULTI)SIG operation";
|
|
75
|
-
NodeErrorReason["SIG_BADLENGTH"] = "Signature cannot be 65 bytes in CHECKMULTISIG";
|
|
76
|
-
NodeErrorReason["SIG_NONSCHNORR"] = "Only Schnorr signatures allowed in this operation";
|
|
77
|
-
NodeErrorReason["DISCOURAGE_UPGRADABLE_NOPS"] = "NOPx reserved for soft-fork upgrades";
|
|
78
|
-
NodeErrorReason["PUBKEYTYPE"] = "Public key is neither compressed or uncompressed";
|
|
79
|
-
NodeErrorReason["CLEANSTACK"] = "Script did not clean its stack";
|
|
80
|
-
NodeErrorReason["NONCOMPRESSED_PUBKEY"] = "Using non-compressed public key";
|
|
81
|
-
NodeErrorReason["ILLEGAL_FORKID"] = "Illegal use of SIGHASH_FORKID";
|
|
82
|
-
NodeErrorReason["MUST_USE_FORKID"] = "Signature must use SIGHASH_FORKID";
|
|
83
|
-
NodeErrorReason["UNKNOWN"] = "unknown error";
|
|
84
|
-
})(NodeErrorReason || (NodeErrorReason = {}));
|
|
66
|
+
const getLocationDataForInstructionPointer = (artifact, instructionPointer) => {
|
|
67
|
+
const locationData = sourceMapToLocationData(artifact.debug.sourceMap);
|
|
68
|
+
// We subtract the constructor inputs because these are present in the evaluation (and thus the instruction pointer)
|
|
69
|
+
// but they are not present in the source code (and thus the location data)
|
|
70
|
+
const modifiedInstructionPointer = instructionPointer - artifact.constructorInputs.length;
|
|
71
|
+
const { location } = locationData[modifiedInstructionPointer];
|
|
72
|
+
const failingLines = artifact.source.split('\n').slice(location.start.line - 1, location.end.line);
|
|
73
|
+
// Slice off the start and end of the statement's start and end lines to only return the failing part
|
|
74
|
+
// Note that we first slice off the end, to avoid shifting the end column index
|
|
75
|
+
failingLines[failingLines.length - 1] = failingLines[failingLines.length - 1].slice(0, location.end.column);
|
|
76
|
+
failingLines[0] = failingLines[0].slice(location.start.column);
|
|
77
|
+
const statement = failingLines.join('\n');
|
|
78
|
+
const lineNumber = location.start.line;
|
|
79
|
+
return { statement, lineNumber };
|
|
80
|
+
};
|
|
85
81
|
//# sourceMappingURL=Errors.js.map
|
package/dist/LibauthTemplate.js
CHANGED
|
@@ -1,49 +1,55 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { bytecodeToScript, formatBitAuthScript, } from '@cashscript/utils';
|
|
2
2
|
import { hexToBin, decodeTransaction, binToHex, binToBase64, utf8ToBin, isHex, } from '@bitauth/libauth';
|
|
3
3
|
import { deflate } from 'pako';
|
|
4
|
-
import { isUtxoP2PKH, } from './interfaces.js';
|
|
4
|
+
import { isUtxoP2PKH, SignatureAlgorithm, HashType, } from './interfaces.js';
|
|
5
5
|
import SignatureTemplate from './SignatureTemplate.js';
|
|
6
6
|
import { addressToLockScript, extendedStringify, snakeCase, zip } from './utils.js';
|
|
7
|
-
// TODO: Can we change this so we don't need to pass in both the transaction and the transactionHex?
|
|
8
7
|
export const buildTemplate = async ({ transaction, transactionHex = undefined, // set this argument to prevent unnecessary call `transaction.build()`
|
|
9
8
|
}) => {
|
|
10
9
|
const contract = transaction.contract;
|
|
11
10
|
const txHex = transactionHex ?? await transaction.build();
|
|
12
|
-
const hasSignatureTemplates = transaction.inputs.filter((input) => isUtxoP2PKH(input)).length > 0;
|
|
13
11
|
const template = {
|
|
14
12
|
$schema: 'https://ide.bitauth.com/authentication-template-v0.schema.json',
|
|
15
13
|
description: 'Imported from cashscript',
|
|
16
14
|
name: contract.artifact.contractName,
|
|
17
15
|
supported: ['BCH_2023_05'],
|
|
18
16
|
version: 0,
|
|
19
|
-
entities: generateTemplateEntities(contract.artifact, transaction.abiFunction),
|
|
17
|
+
entities: generateTemplateEntities(contract.artifact, transaction.abiFunction, transaction.encodedFunctionArgs),
|
|
20
18
|
scripts: generateTemplateScripts(contract.artifact, contract.addressType, transaction.abiFunction, transaction.encodedFunctionArgs, contract.encodedConstructorArgs),
|
|
21
|
-
scenarios: generateTemplateScenarios(contract, transaction, txHex, contract.artifact, transaction.abiFunction, transaction.encodedFunctionArgs, contract.encodedConstructorArgs
|
|
19
|
+
scenarios: generateTemplateScenarios(contract, transaction, txHex, contract.artifact, transaction.abiFunction, transaction.encodedFunctionArgs, contract.encodedConstructorArgs),
|
|
22
20
|
};
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
transaction.inputs
|
|
22
|
+
.forEach((input, index) => {
|
|
23
|
+
if (!isUtxoP2PKH(input))
|
|
24
|
+
return;
|
|
25
|
+
const lockScriptName = `p2pkh_placeholder_lock_${index}`;
|
|
26
|
+
const unlockScriptName = `p2pkh_placeholder_unlock_${index}`;
|
|
27
|
+
const placeholderKeyName = `placeholder_key_${index}`;
|
|
28
|
+
const signatureAlgorithmName = getSignatureAlgorithmName(input.template.getSignatureAlgorithm());
|
|
29
|
+
const hashtypeName = getHashTypeName(input.template.getHashType(false));
|
|
30
|
+
const signatureString = `${placeholderKeyName}.${signatureAlgorithmName}.${hashtypeName}`;
|
|
31
|
+
template.entities.parameters.scripts.push(lockScriptName, unlockScriptName);
|
|
26
32
|
template.entities.parameters.variables = {
|
|
27
33
|
...template.entities.parameters.variables,
|
|
28
|
-
|
|
29
|
-
description:
|
|
30
|
-
name:
|
|
34
|
+
[placeholderKeyName]: {
|
|
35
|
+
description: placeholderKeyName,
|
|
36
|
+
name: placeholderKeyName,
|
|
31
37
|
type: 'Key',
|
|
32
38
|
},
|
|
33
39
|
};
|
|
34
40
|
// add extra unlocking and locking script for P2PKH inputs spent alongside our contract
|
|
35
41
|
// this is needed for correct cross-referrences in the template
|
|
36
|
-
template.scripts
|
|
37
|
-
name:
|
|
38
|
-
script:
|
|
39
|
-
unlocks:
|
|
42
|
+
template.scripts[unlockScriptName] = {
|
|
43
|
+
name: unlockScriptName,
|
|
44
|
+
script: `<${signatureString}>\n<${placeholderKeyName}.public_key>`,
|
|
45
|
+
unlocks: lockScriptName,
|
|
40
46
|
};
|
|
41
|
-
template.scripts
|
|
47
|
+
template.scripts[lockScriptName] = {
|
|
42
48
|
lockingType: 'standard',
|
|
43
|
-
name:
|
|
44
|
-
script:
|
|
49
|
+
name: lockScriptName,
|
|
50
|
+
script: `OP_DUP\nOP_HASH160 <$(<${placeholderKeyName}.public_key> OP_HASH160\n)> OP_EQUALVERIFY\nOP_CHECKSIG`,
|
|
45
51
|
};
|
|
46
|
-
}
|
|
52
|
+
});
|
|
47
53
|
return template;
|
|
48
54
|
};
|
|
49
55
|
export const getBitauthUri = (template) => {
|
|
@@ -51,13 +57,13 @@ export const getBitauthUri = (template) => {
|
|
|
51
57
|
const payload = base64toBase64Url(binToBase64(deflate(utf8ToBin(extendedStringify(template)))));
|
|
52
58
|
return `https://ide.bitauth.com/import-template/${payload}`;
|
|
53
59
|
};
|
|
54
|
-
const generateTemplateEntities = (artifact, abiFunction) => {
|
|
55
|
-
const functionParameters = Object.fromEntries(abiFunction.inputs.map((input) => ([
|
|
60
|
+
const generateTemplateEntities = (artifact, abiFunction, encodedFunctionArgs) => {
|
|
61
|
+
const functionParameters = Object.fromEntries(abiFunction.inputs.map((input, index) => ([
|
|
56
62
|
snakeCase(input.name),
|
|
57
63
|
{
|
|
58
64
|
description: `"${input.name}" parameter of function "${abiFunction.name}"`,
|
|
59
65
|
name: input.name,
|
|
60
|
-
type:
|
|
66
|
+
type: encodedFunctionArgs[index] instanceof SignatureTemplate ? 'Key' : 'WalletData',
|
|
61
67
|
},
|
|
62
68
|
])));
|
|
63
69
|
const constructorParameters = Object.fromEntries(artifact.constructorInputs.map((input) => ([
|
|
@@ -130,7 +136,7 @@ const generateTemplateUnlockScript = (artifact, abiFunction, encodedFunctionArgs
|
|
|
130
136
|
unlocks: 'lock',
|
|
131
137
|
};
|
|
132
138
|
};
|
|
133
|
-
const generateTemplateScenarios = (contract, transaction, transactionHex, artifact, abiFunction, encodedFunctionArgs, encodedConstructorArgs
|
|
139
|
+
const generateTemplateScenarios = (contract, transaction, transactionHex, artifact, abiFunction, encodedFunctionArgs, encodedConstructorArgs) => {
|
|
134
140
|
const libauthTransaction = decodeTransaction(hexToBin(transactionHex));
|
|
135
141
|
if (typeof libauthTransaction === 'string')
|
|
136
142
|
throw Error(libauthTransaction);
|
|
@@ -145,11 +151,10 @@ const generateTemplateScenarios = (contract, transaction, transactionHex, artifa
|
|
|
145
151
|
...generateTemplateScenarioParametersValues(abiFunction.inputs, encodedFunctionArgs),
|
|
146
152
|
...generateTemplateScenarioParametersValues(artifact.constructorInputs, encodedConstructorArgs),
|
|
147
153
|
},
|
|
148
|
-
// TODO: Don't hardcode these values
|
|
149
154
|
currentBlockHeight: 2,
|
|
150
155
|
currentBlockTime: Math.round(+new Date() / 1000),
|
|
151
156
|
keys: {
|
|
152
|
-
privateKeys: generateTemplateScenarioKeys(abiFunction.inputs, encodedFunctionArgs
|
|
157
|
+
privateKeys: generateTemplateScenarioKeys(abiFunction.inputs, encodedFunctionArgs),
|
|
153
158
|
},
|
|
154
159
|
},
|
|
155
160
|
transaction: generateTemplateScenarioTransaction(contract, libauthTransaction, transaction),
|
|
@@ -165,21 +170,17 @@ const generateTemplateScenarios = (contract, transaction, transactionHex, artifa
|
|
|
165
170
|
const generateTemplateScenarioTransaction = (contract, libauthTransaction, csTransaction) => {
|
|
166
171
|
const slotIndex = csTransaction.inputs.findIndex((input) => !isUtxoP2PKH(input));
|
|
167
172
|
const inputs = libauthTransaction.inputs.map((input, index) => {
|
|
168
|
-
const csInput = csTransaction.inputs[index];
|
|
173
|
+
const csInput = csTransaction.inputs[index];
|
|
169
174
|
return {
|
|
170
175
|
outpointIndex: input.outpointIndex,
|
|
171
|
-
outpointTransactionHash:
|
|
172
|
-
// TODO: This should always be Uint8Array according to the libauth transaction types
|
|
173
|
-
input.outpointTransactionHash instanceof Uint8Array
|
|
174
|
-
? binToHex(input.outpointTransactionHash)
|
|
175
|
-
: input.outpointTransactionHash,
|
|
176
|
+
outpointTransactionHash: binToHex(input.outpointTransactionHash),
|
|
176
177
|
sequenceNumber: input.sequenceNumber,
|
|
177
|
-
unlockingBytecode: generateTemplateScenarioBytecode(csInput,
|
|
178
|
+
unlockingBytecode: generateTemplateScenarioBytecode(csInput, `p2pkh_placeholder_unlock_${index}`, index === slotIndex),
|
|
178
179
|
};
|
|
179
180
|
});
|
|
180
181
|
const locktime = libauthTransaction.locktime;
|
|
181
182
|
const outputs = libauthTransaction.outputs.map((output, index) => {
|
|
182
|
-
const csOutput = csTransaction.outputs[index];
|
|
183
|
+
const csOutput = csTransaction.outputs[index];
|
|
183
184
|
return {
|
|
184
185
|
lockingBytecode: generateTemplateScenarioTransactionOutputLockingBytecode(csOutput, contract),
|
|
185
186
|
token: serialiseTokenDetails(output.token),
|
|
@@ -200,7 +201,7 @@ const generateTemplateScenarioSourceOutputs = (csTransaction) => {
|
|
|
200
201
|
const slotIndex = csTransaction.inputs.findIndex((input) => !isUtxoP2PKH(input));
|
|
201
202
|
return csTransaction.inputs.map((input, index) => {
|
|
202
203
|
return {
|
|
203
|
-
lockingBytecode: generateTemplateScenarioBytecode(input,
|
|
204
|
+
lockingBytecode: generateTemplateScenarioBytecode(input, `p2pkh_placeholder_lock_${index}`, index === slotIndex),
|
|
204
205
|
valueSatoshis: Number(input.satoshis),
|
|
205
206
|
token: serialiseTokenDetails(input.token),
|
|
206
207
|
};
|
|
@@ -229,21 +230,17 @@ const generateTemplateScenarioParametersValues = (types, encodedArgs) => {
|
|
|
229
230
|
.filter(([, arg]) => !(arg instanceof SignatureTemplate))
|
|
230
231
|
.map(([input, arg]) => {
|
|
231
232
|
const encodedArgumentHex = binToHex(arg);
|
|
232
|
-
|
|
233
|
-
const prefixedEncodedArgument = encodedArgumentHex.length ? `0x${encodedArgumentHex}` : encodedArgumentHex;
|
|
233
|
+
const prefixedEncodedArgument = encodedArgumentHex.length > 0 ? `0x${encodedArgumentHex}` : '';
|
|
234
234
|
return [snakeCase(input.name), prefixedEncodedArgument];
|
|
235
235
|
});
|
|
236
236
|
return Object.fromEntries(entries);
|
|
237
237
|
};
|
|
238
|
-
const generateTemplateScenarioKeys = (types, encodedArgs
|
|
238
|
+
const generateTemplateScenarioKeys = (types, encodedArgs) => {
|
|
239
239
|
const typesAndArguments = zip(types, encodedArgs);
|
|
240
240
|
const entries = typesAndArguments
|
|
241
241
|
.filter(([, arg]) => arg instanceof SignatureTemplate)
|
|
242
242
|
.map(([input, arg]) => [snakeCase(input.name), binToHex(arg.privateKey)]);
|
|
243
|
-
|
|
244
|
-
? [['placeholder_key', '0x0000000000000000000000000000000000000000000000000000000000000000']]
|
|
245
|
-
: [];
|
|
246
|
-
return Object.fromEntries([...entries, ...placeholderKey]);
|
|
243
|
+
return Object.fromEntries(entries);
|
|
247
244
|
};
|
|
248
245
|
const formatParametersForDebugging = (types, args) => {
|
|
249
246
|
if (types.length === 0)
|
|
@@ -252,8 +249,9 @@ const formatParametersForDebugging = (types, args) => {
|
|
|
252
249
|
const typesAndArguments = zip(types, args).reverse();
|
|
253
250
|
return typesAndArguments.map(([input, arg]) => {
|
|
254
251
|
if (arg instanceof SignatureTemplate) {
|
|
255
|
-
|
|
256
|
-
|
|
252
|
+
const signatureAlgorithmName = getSignatureAlgorithmName(arg.getSignatureAlgorithm());
|
|
253
|
+
const hashtypeName = getHashTypeName(arg.getHashType(false));
|
|
254
|
+
return `<${snakeCase(input.name)}.${signatureAlgorithmName}.${hashtypeName}> // ${input.type}`;
|
|
257
255
|
}
|
|
258
256
|
const typeStr = input.type === 'bytes' ? `bytes${arg.length}` : input.type;
|
|
259
257
|
// we output these values as pushdata, comment will contain the type and the value of the variable
|
|
@@ -261,9 +259,32 @@ const formatParametersForDebugging = (types, args) => {
|
|
|
261
259
|
return `<${snakeCase(input.name)}> // ${typeStr} = <${`0x${binToHex(arg)}`}>`;
|
|
262
260
|
}).join('\n');
|
|
263
261
|
};
|
|
262
|
+
const getSignatureAlgorithmName = (signatureAlgorithm) => {
|
|
263
|
+
const signatureAlgorithmNames = {
|
|
264
|
+
[SignatureAlgorithm.SCHNORR]: 'schnorr_signature',
|
|
265
|
+
[SignatureAlgorithm.ECDSA]: 'ecdsa_signature',
|
|
266
|
+
};
|
|
267
|
+
return signatureAlgorithmNames[signatureAlgorithm];
|
|
268
|
+
};
|
|
269
|
+
const getHashTypeName = (hashType) => {
|
|
270
|
+
const hashtypeNames = {
|
|
271
|
+
[HashType.SIGHASH_ALL]: 'all_outputs',
|
|
272
|
+
[HashType.SIGHASH_ALL | HashType.SIGHASH_ANYONECANPAY]: 'all_outputs_single_input',
|
|
273
|
+
[HashType.SIGHASH_ALL | HashType.SIGHASH_UTXOS]: 'all_outputs_all_utxos',
|
|
274
|
+
[HashType.SIGHASH_ALL | HashType.SIGHASH_ANYONECANPAY | HashType.SIGHASH_UTXOS]: 'all_outputs_single_input_INVALID_all_utxos',
|
|
275
|
+
[HashType.SIGHASH_SINGLE]: 'corresponding_output',
|
|
276
|
+
[HashType.SIGHASH_SINGLE | HashType.SIGHASH_ANYONECANPAY]: 'corresponding_output_single_input',
|
|
277
|
+
[HashType.SIGHASH_SINGLE | HashType.SIGHASH_UTXOS]: 'corresponding_output_all_utxos',
|
|
278
|
+
[HashType.SIGHASH_SINGLE | HashType.SIGHASH_ANYONECANPAY | HashType.SIGHASH_UTXOS]: 'corresponding_output_single_input_INVALID_all_utxos',
|
|
279
|
+
[HashType.SIGHASH_NONE]: 'no_outputs',
|
|
280
|
+
[HashType.SIGHASH_NONE | HashType.SIGHASH_ANYONECANPAY]: 'no_outputs_single_input',
|
|
281
|
+
[HashType.SIGHASH_NONE | HashType.SIGHASH_UTXOS]: 'no_outputs_all_utxos',
|
|
282
|
+
[HashType.SIGHASH_NONE | HashType.SIGHASH_ANYONECANPAY | HashType.SIGHASH_UTXOS]: 'no_outputs_single_input_INVALID_all_utxos',
|
|
283
|
+
};
|
|
284
|
+
return hashtypeNames[hashType];
|
|
285
|
+
};
|
|
264
286
|
const formatBytecodeForDebugging = (artifact) => {
|
|
265
287
|
if (!artifact.debug) {
|
|
266
|
-
// TODO: See if we can merge this with code from @cashscript/utils -> script.ts
|
|
267
288
|
return artifact.bytecode
|
|
268
289
|
.split(' ')
|
|
269
290
|
.map((asmElement) => (isHex(asmElement) ? `<0x${asmElement}>` : asmElement))
|
|
@@ -271,7 +292,6 @@ const formatBytecodeForDebugging = (artifact) => {
|
|
|
271
292
|
}
|
|
272
293
|
return formatBitAuthScript(bytecodeToScript(hexToBin(artifact.debug.bytecode)), artifact.debug.sourceMap, artifact.source);
|
|
273
294
|
};
|
|
274
|
-
// TODO: Maybe move / refactor
|
|
275
295
|
const serialiseTokenDetails = (token) => {
|
|
276
296
|
if (!token)
|
|
277
297
|
return undefined;
|
|
@@ -6,6 +6,7 @@ export default class SignatureTemplate {
|
|
|
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;
|
|
9
|
+
getSignatureAlgorithm(): SignatureAlgorithm;
|
|
9
10
|
getPublicKey(): Uint8Array;
|
|
10
11
|
unlockP2PKH(): Unlocker;
|
|
11
12
|
}
|
|
@@ -27,6 +27,9 @@ export default class SignatureTemplate {
|
|
|
27
27
|
getHashType(bchForkId = true) {
|
|
28
28
|
return bchForkId ? (this.hashtype | SigningSerializationFlag.forkId) : this.hashtype;
|
|
29
29
|
}
|
|
30
|
+
getSignatureAlgorithm() {
|
|
31
|
+
return this.signatureAlgorithm;
|
|
32
|
+
}
|
|
30
33
|
getPublicKey() {
|
|
31
34
|
return secp256k1.derivePublicKeyCompressed(this.privateKey);
|
|
32
35
|
}
|
package/dist/Transaction.d.ts
CHANGED
|
@@ -4,12 +4,12 @@ import { Utxo, Output, Recipient, TokenDetails, TransactionDetails, Unlocker } f
|
|
|
4
4
|
import SignatureTemplate from './SignatureTemplate.js';
|
|
5
5
|
import { Contract } from './Contract.js';
|
|
6
6
|
import { DebugResult } from './debugging.js';
|
|
7
|
-
import {
|
|
7
|
+
import { EncodedFunctionArgument } from './Argument.js';
|
|
8
8
|
export declare class Transaction {
|
|
9
9
|
contract: Contract;
|
|
10
10
|
private unlocker;
|
|
11
11
|
abiFunction: AbiFunction;
|
|
12
|
-
encodedFunctionArgs:
|
|
12
|
+
encodedFunctionArgs: EncodedFunctionArgument[];
|
|
13
13
|
private selector?;
|
|
14
14
|
inputs: Utxo[];
|
|
15
15
|
outputs: Output[];
|
|
@@ -19,7 +19,7 @@ export declare class Transaction {
|
|
|
19
19
|
private hardcodedFee;
|
|
20
20
|
private minChange;
|
|
21
21
|
private tokenChange;
|
|
22
|
-
constructor(contract: Contract, unlocker: Unlocker, abiFunction: AbiFunction, encodedFunctionArgs:
|
|
22
|
+
constructor(contract: Contract, unlocker: Unlocker, abiFunction: AbiFunction, encodedFunctionArgs: EncodedFunctionArgument[], selector?: number | undefined);
|
|
23
23
|
from(input: Utxo): this;
|
|
24
24
|
from(inputs: Utxo[]): this;
|
|
25
25
|
fromP2PKH(input: Utxo, template: SignatureTemplate): this;
|
package/dist/Transaction.js
CHANGED
|
@@ -8,10 +8,9 @@ import { createInputScript, getInputSize, createOpReturnOutput, getTxSizeWithout
|
|
|
8
8
|
import SignatureTemplate from './SignatureTemplate.js';
|
|
9
9
|
import { P2PKH_INPUT_SIZE } from './constants.js';
|
|
10
10
|
import { TransactionBuilder } from './TransactionBuilder.js';
|
|
11
|
-
import MockNetworkProvider from './network/MockNetworkProvider.js';
|
|
12
11
|
import { buildTemplate, getBitauthUri } from './LibauthTemplate.js';
|
|
13
|
-
import { debugTemplate
|
|
14
|
-
import {
|
|
12
|
+
import { debugTemplate } from './debugging.js';
|
|
13
|
+
import { FailedTransactionError } from './Errors.js';
|
|
15
14
|
export class Transaction {
|
|
16
15
|
constructor(contract, unlocker, abiFunction, encodedFunctionArgs, selector) {
|
|
17
16
|
this.contract = contract;
|
|
@@ -102,30 +101,12 @@ export class Transaction {
|
|
|
102
101
|
}
|
|
103
102
|
async send(raw) {
|
|
104
103
|
const tx = await this.build();
|
|
105
|
-
let template;
|
|
106
104
|
// Debug the transaction locally before sending so any errors are caught early
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
await this.debug();
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
catch (error) {
|
|
114
|
-
if (error instanceof FailedRequireError) {
|
|
115
|
-
error.bitauthUri = await this.bitauthUri();
|
|
116
|
-
error.message += `\n\nBitauth URI: ${error.bitauthUri}`;
|
|
117
|
-
}
|
|
118
|
-
throw error;
|
|
105
|
+
// Libauth debugging does not work with old-style covenants
|
|
106
|
+
if (!this.abiFunction.covenant) {
|
|
107
|
+
await this.debug();
|
|
119
108
|
}
|
|
120
109
|
try {
|
|
121
|
-
// TODO: Can we move this to MockNetworkProvider?
|
|
122
|
-
if (this.contract.provider instanceof MockNetworkProvider) {
|
|
123
|
-
template = await buildTemplate({
|
|
124
|
-
transaction: this,
|
|
125
|
-
transactionHex: tx,
|
|
126
|
-
});
|
|
127
|
-
evaluateTemplate(template);
|
|
128
|
-
}
|
|
129
110
|
const txid = await this.contract.provider.sendRawTransaction(tx);
|
|
130
111
|
return raw ? await this.getTxDetails(txid, raw) : await this.getTxDetails(txid);
|
|
131
112
|
}
|
|
@@ -136,6 +117,9 @@ export class Transaction {
|
|
|
136
117
|
}
|
|
137
118
|
// method to debug the transaction with libauth VM, throws upon evaluation error
|
|
138
119
|
async debug() {
|
|
120
|
+
if (!this.contract.artifact.debug) {
|
|
121
|
+
console.warn('No debug information found in artifact. Recompile with cashc version 0.10.0 or newer to get better debugging information.');
|
|
122
|
+
}
|
|
139
123
|
const template = await this.getLibauthTemplate();
|
|
140
124
|
return debugTemplate(template, this.contract.artifact);
|
|
141
125
|
}
|
|
@@ -165,7 +149,7 @@ export class Transaction {
|
|
|
165
149
|
}
|
|
166
150
|
async setInputsAndOutputs() {
|
|
167
151
|
if (this.outputs.length === 0) {
|
|
168
|
-
throw Error('Attempted to build a transaction without outputs');
|
|
152
|
+
throw new Error('Attempted to build a transaction without outputs');
|
|
169
153
|
}
|
|
170
154
|
// Fetched utxos are only used when no inputs are available, so only fetch in that case.
|
|
171
155
|
const allUtxos = this.inputs.length === 0
|
|
@@ -4,9 +4,9 @@ export interface TransactionBuilderOptions {
|
|
|
4
4
|
provider: NetworkProvider;
|
|
5
5
|
}
|
|
6
6
|
export declare class TransactionBuilder {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
provider: NetworkProvider;
|
|
8
|
+
inputs: UnlockableUtxo[];
|
|
9
|
+
outputs: Output[];
|
|
10
10
|
private locktime;
|
|
11
11
|
private maxFee?;
|
|
12
12
|
constructor(options: TransactionBuilderOptions);
|
package/dist/debugging.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AuthenticationProgramStateCommon, WalletTemplate } from '@bitauth/libauth';
|
|
2
2
|
import { Artifact } from '@cashscript/utils';
|
|
3
3
|
export declare const evaluateTemplate: (template: WalletTemplate) => boolean;
|
|
4
|
-
export
|
|
4
|
+
export type DebugResult = AuthenticationProgramStateCommon[];
|
|
5
5
|
export declare const debugTemplate: (template: WalletTemplate, artifact: Artifact) => DebugResult;
|
package/dist/debugging.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { AuthenticationErrorCommon, binToHex, createCompiler, createVirtualMachineBCH2023, encodeAuthenticationInstruction, walletTemplateToCompilerConfiguration } from '@bitauth/libauth';
|
|
2
2
|
import { Op, PrimitiveType, bytecodeToAsm, decodeBool, decodeInt, decodeString } from '@cashscript/utils';
|
|
3
3
|
import { findLastIndex, toRegExp } from './utils.js';
|
|
4
|
-
import { FailedRequireError, FailedTransactionError } from './Errors.js';
|
|
4
|
+
import { FailedRequireError, FailedTransactionError, FailedTransactionEvaluationError } from './Errors.js';
|
|
5
|
+
import { getBitauthUri } from './LibauthTemplate.js';
|
|
5
6
|
// evaluates the fully defined template, throws upon error
|
|
6
7
|
export const evaluateTemplate = (template) => {
|
|
7
8
|
const { vm, program } = createProgram(template);
|
|
8
9
|
const verifyResult = vm.verify(program);
|
|
9
10
|
if (typeof verifyResult === 'string') {
|
|
10
|
-
throw new FailedTransactionError(verifyResult);
|
|
11
|
+
throw new FailedTransactionError(verifyResult, getBitauthUri(template));
|
|
11
12
|
}
|
|
12
13
|
return verifyResult;
|
|
13
14
|
};
|
|
@@ -36,31 +37,37 @@ export const debugTemplate = (template, artifact) => {
|
|
|
36
37
|
const failingIp = lastExecutedDebugStep.ip - 1;
|
|
37
38
|
// Generally speaking, an error is thrown by the OP_VERIFY opcode, but for NULLFAIL, the error is thrown in the
|
|
38
39
|
// preceding OP_CHECKSIG opcode. The error message is registered in the next instruction, so we need to increment
|
|
39
|
-
// the instruction pointer to get the correct error message.
|
|
40
|
+
// the instruction pointer to get the correct error message from the require messages in the artifact.
|
|
41
|
+
// Note that we do NOT use this adjusted IP when passing the failing IP into the FailedRequireError
|
|
40
42
|
const isNullFail = lastExecutedDebugStep.error === AuthenticationErrorCommon.nonNullSignatureFailure;
|
|
41
43
|
const requireStatementIp = failingIp + (isNullFail ? 1 : 0);
|
|
42
44
|
const requireStatement = (artifact.debug?.requires ?? [])
|
|
43
45
|
.find((statement) => statement.ip === requireStatementIp);
|
|
44
|
-
|
|
46
|
+
const { program: { inputIndex }, error } = lastExecutedDebugStep;
|
|
45
47
|
if (requireStatement) {
|
|
46
|
-
|
|
47
|
-
throw new FailedRequireError(artifact
|
|
48
|
+
// Note that we use failingIp here rather than requireStatementIp, see comment above
|
|
49
|
+
throw new FailedRequireError(artifact, failingIp, requireStatement, inputIndex, getBitauthUri(template), error);
|
|
48
50
|
}
|
|
49
|
-
|
|
51
|
+
// Note that we use failingIp here rather than requireStatementIp, see comment above
|
|
52
|
+
throw new FailedTransactionEvaluationError(artifact, failingIp, inputIndex, getBitauthUri(template), error);
|
|
50
53
|
}
|
|
51
54
|
const evaluationResult = vm.verify(program);
|
|
52
55
|
if (failedFinalVerify(evaluationResult)) {
|
|
53
56
|
const finalExecutedVerifyIp = getFinalExecutedVerifyIp(executedDebugSteps);
|
|
57
|
+
// The final executed verify instruction points to the "implicit" VERIFY that is added at the end of the script.
|
|
58
|
+
// This instruction does not exist in the sourcemap, so we need to decrement the instruction pointer to get the
|
|
59
|
+
// actual final executed statement (which is *not* the require statement, but the evaluation within)
|
|
60
|
+
const sourcemapInstructionPointer = finalExecutedVerifyIp - 1;
|
|
54
61
|
// logDebugSteps(executedDebugSteps, lastExecutedDebugStep.instructions);
|
|
55
62
|
// console.warn('message', finalExecutedVerifyIp);
|
|
56
63
|
// console.warn(artifact.debug?.requires);
|
|
57
64
|
const requireStatement = (artifact.debug?.requires ?? [])
|
|
58
65
|
.find((message) => message.ip === finalExecutedVerifyIp);
|
|
66
|
+
const { program: { inputIndex } } = lastExecutedDebugStep;
|
|
59
67
|
if (requireStatement) {
|
|
60
|
-
|
|
61
|
-
throw new FailedRequireError(artifact.contractName, requireStatement, inputIndex, error);
|
|
68
|
+
throw new FailedRequireError(artifact, sourcemapInstructionPointer, requireStatement, inputIndex, getBitauthUri(template));
|
|
62
69
|
}
|
|
63
|
-
throw new
|
|
70
|
+
throw new FailedTransactionEvaluationError(artifact, sourcemapInstructionPointer, inputIndex, getBitauthUri(template), evaluationResult);
|
|
64
71
|
}
|
|
65
72
|
return fullDebugSteps;
|
|
66
73
|
};
|
|
@@ -76,10 +83,10 @@ const createProgram = (template) => {
|
|
|
76
83
|
scenarioId: 'evaluate_function',
|
|
77
84
|
});
|
|
78
85
|
if (typeof scenarioGeneration === 'string') {
|
|
79
|
-
throw new FailedTransactionError(scenarioGeneration);
|
|
86
|
+
throw new FailedTransactionError(scenarioGeneration, getBitauthUri(template));
|
|
80
87
|
}
|
|
81
88
|
if (typeof scenarioGeneration.scenario === 'string') {
|
|
82
|
-
throw new FailedTransactionError(scenarioGeneration.scenario);
|
|
89
|
+
throw new FailedTransactionError(scenarioGeneration.scenario, getBitauthUri(template));
|
|
83
90
|
}
|
|
84
91
|
return { vm, program: scenarioGeneration.scenario.program };
|
|
85
92
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
export {
|
|
3
|
-
export { Contract, ContractFunction } from './Contract.js';
|
|
1
|
+
export { default as SignatureTemplate } from './SignatureTemplate.js';
|
|
2
|
+
export { Contract, type ContractFunction } from './Contract.js';
|
|
4
3
|
export { Transaction } from './Transaction.js';
|
|
5
4
|
export { TransactionBuilder } from './TransactionBuilder.js';
|
|
6
|
-
export {
|
|
7
|
-
export { Artifact, AbiFunction, AbiInput } from '@cashscript/utils';
|
|
5
|
+
export { type ConstructorArgument, type FunctionArgument, type EncodedConstructorArgument, type EncodedFunctionArgument, encodeFunctionArgument, } from './Argument.js';
|
|
6
|
+
export type { Artifact, AbiFunction, AbiInput } from '@cashscript/utils';
|
|
8
7
|
export * as utils from '@cashscript/utils';
|
|
9
8
|
export * from './interfaces.js';
|
|
10
9
|
export * from './Errors.js';
|
|
11
|
-
export { NetworkProvider, BitcoinRpcNetworkProvider, ElectrumNetworkProvider, FullStackNetworkProvider, MockNetworkProvider, } from './network/index.js';
|
|
10
|
+
export { type NetworkProvider, BitcoinRpcNetworkProvider, ElectrumNetworkProvider, FullStackNetworkProvider, MockNetworkProvider, } from './network/index.js';
|
|
12
11
|
export { randomUtxo, randomToken, randomNFT } from './utils.js';
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
export { SignatureTemplate };
|
|
1
|
+
export { default as SignatureTemplate } from './SignatureTemplate.js';
|
|
3
2
|
export { Contract } from './Contract.js';
|
|
4
3
|
export { Transaction } from './Transaction.js';
|
|
5
4
|
export { TransactionBuilder } from './TransactionBuilder.js';
|
|
6
|
-
export {
|
|
5
|
+
export { encodeFunctionArgument, } from './Argument.js';
|
|
7
6
|
export * as utils from '@cashscript/utils';
|
|
8
7
|
export * from './interfaces.js';
|
|
9
8
|
export * from './Errors.js';
|
package/dist/interfaces.d.ts
CHANGED
|
@@ -82,7 +82,7 @@ export declare const Network: {
|
|
|
82
82
|
CHIPNET: "chipnet";
|
|
83
83
|
REGTEST: "regtest";
|
|
84
84
|
};
|
|
85
|
-
export
|
|
85
|
+
export type Network = (typeof Network)[keyof typeof Network];
|
|
86
86
|
export interface TransactionDetails extends Transaction {
|
|
87
87
|
txid: string;
|
|
88
88
|
hex: string;
|
|
@@ -91,4 +91,4 @@ export interface ContractOptions {
|
|
|
91
91
|
provider?: NetworkProvider;
|
|
92
92
|
addressType?: AddressType;
|
|
93
93
|
}
|
|
94
|
-
export
|
|
94
|
+
export type AddressType = 'p2sh20' | 'p2sh32';
|
package/dist/network/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { default as NetworkProvider } from './NetworkProvider.js';
|
|
1
|
+
export type { default as NetworkProvider } from './NetworkProvider.js';
|
|
2
2
|
export { default as BitcoinRpcNetworkProvider } from './BitcoinRpcNetworkProvider.js';
|
|
3
3
|
export { default as ElectrumNetworkProvider } from './ElectrumNetworkProvider.js';
|
|
4
4
|
export { default as FullStackNetworkProvider } from './FullStackNetworkProvider.js';
|
|
@@ -29,7 +29,7 @@ expect.extend({
|
|
|
29
29
|
try {
|
|
30
30
|
await transaction.debug();
|
|
31
31
|
const matcherHint = this.utils.matcherHint('.toFailRequireWith', undefined, match.toString(), { isNot: this.isNot });
|
|
32
|
-
const message = () => `${matcherHint}\n\nContract function did not fail a require statement
|
|
32
|
+
const message = () => `${matcherHint}\n\nContract function did not fail a require statement.`;
|
|
33
33
|
return { message, pass: false };
|
|
34
34
|
}
|
|
35
35
|
catch (transactionError) {
|
|
@@ -46,6 +46,18 @@ expect.extend({
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
},
|
|
49
|
+
async toFailRequire(transaction) {
|
|
50
|
+
try {
|
|
51
|
+
await transaction.debug();
|
|
52
|
+
const message = () => 'Contract function did not fail a require statement.';
|
|
53
|
+
return { message, pass: false };
|
|
54
|
+
}
|
|
55
|
+
catch (transactionError) {
|
|
56
|
+
const receivedText = `Received string: ${this.utils.printReceived(transactionError?.message ?? '')}`;
|
|
57
|
+
const message = () => `Contract function failed a require statement.\n${receivedText}`;
|
|
58
|
+
return { message, pass: true };
|
|
59
|
+
}
|
|
60
|
+
},
|
|
49
61
|
});
|
|
50
62
|
export {};
|
|
51
63
|
//# sourceMappingURL=JestExtensions.js.map
|
package/dist/utils.d.ts
CHANGED
|
@@ -28,10 +28,10 @@ export declare function utxoTokenComparator(a: Utxo, b: Utxo): number;
|
|
|
28
28
|
*/
|
|
29
29
|
export declare function addressToLockScript(address: string): Uint8Array;
|
|
30
30
|
export declare function getNetworkPrefix(network: string): 'bitcoincash' | 'bchtest' | 'bchreg';
|
|
31
|
-
export declare const randomUtxo: (defaults?: Partial<Utxo>
|
|
32
|
-
export declare const randomToken: (defaults?: Partial<TokenDetails>
|
|
33
|
-
export declare const randomNFT: (defaults?: Partial<TokenDetails>
|
|
31
|
+
export declare const randomUtxo: (defaults?: Partial<Utxo>) => Utxo;
|
|
32
|
+
export declare const randomToken: (defaults?: Partial<TokenDetails>) => TokenDetails;
|
|
33
|
+
export declare const randomNFT: (defaults?: Partial<TokenDetails>) => TokenDetails;
|
|
34
34
|
export declare function findLastIndex<T>(array: Array<T>, predicate: (value: T, index: number, obj: T[]) => boolean): number;
|
|
35
35
|
export declare const snakeCase: (str: string) => string;
|
|
36
|
-
export declare const extendedStringify: (obj: any, spaces?: number
|
|
36
|
+
export declare const extendedStringify: (obj: any, spaces?: number) => string;
|
|
37
37
|
export declare const zip: <T, U>(a: T[], b: U[]) => [T, U][];
|
package/dist/utils.js
CHANGED
|
@@ -139,10 +139,12 @@ export function toRegExp(reasons) {
|
|
|
139
139
|
return new RegExp(reasons.join('|').replace(/\(/g, '\\(').replace(/\)/g, '\\)'));
|
|
140
140
|
}
|
|
141
141
|
export function scriptToAddress(script, network, addressType, tokenSupport) {
|
|
142
|
-
const
|
|
142
|
+
const bytecode = scriptToLockingBytecode(script, addressType);
|
|
143
143
|
const prefix = getNetworkPrefix(network);
|
|
144
|
-
const
|
|
145
|
-
|
|
144
|
+
const result = lockingBytecodeToCashAddress({ bytecode, prefix, tokenSupport });
|
|
145
|
+
if (typeof result === 'string')
|
|
146
|
+
throw new Error(result);
|
|
147
|
+
return result.address;
|
|
146
148
|
}
|
|
147
149
|
export function scriptToLockingBytecode(script, addressType) {
|
|
148
150
|
const scriptBytecode = scriptToBytecode(script);
|
|
@@ -221,7 +223,7 @@ function getPushDataOpcode(data) {
|
|
|
221
223
|
return Uint8Array.from([byteLength]);
|
|
222
224
|
if (byteLength < 256)
|
|
223
225
|
return Uint8Array.from([0x4c, byteLength]);
|
|
224
|
-
throw Error('Pushdata too large');
|
|
226
|
+
throw new Error('Pushdata too large');
|
|
225
227
|
}
|
|
226
228
|
const randomInt = () => BigInt(Math.floor(Math.random() * 10000));
|
|
227
229
|
export const randomUtxo = (defaults) => ({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cashscript",
|
|
3
|
-
"version": "0.10.0
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Easily write and interact with Bitcoin Cash contracts",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bitcoin cash",
|
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
],
|
|
11
11
|
"homepage": "https://cashscript.org",
|
|
12
12
|
"bugs": {
|
|
13
|
-
"url": "https://github.com/
|
|
13
|
+
"url": "https://github.com/CashScript/cashscript/issues"
|
|
14
14
|
},
|
|
15
15
|
"repository": {
|
|
16
16
|
"type": "git",
|
|
17
|
-
"url": "git+https://github.com/
|
|
17
|
+
"url": "git+https://github.com/CashScript/cashscript.git"
|
|
18
18
|
},
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"author": "Rosco Kalis <roscokalis@gmail.com>",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"test": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@bitauth/libauth": "^
|
|
47
|
-
"@cashscript/utils": "^0.10.0
|
|
46
|
+
"@bitauth/libauth": "^3.0.0",
|
|
47
|
+
"@cashscript/utils": "^0.10.0",
|
|
48
48
|
"bip68": "^1.0.4",
|
|
49
49
|
"bitcoin-rpc-promise-retry": "^1.3.0",
|
|
50
50
|
"delay": "^5.0.0",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"bip39": "^3.0.4",
|
|
60
60
|
"eslint": "^8.54.0",
|
|
61
61
|
"jest": "^29.4.1",
|
|
62
|
-
"typescript": "^
|
|
62
|
+
"typescript": "^5.5.4"
|
|
63
63
|
},
|
|
64
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "cd3031b60dc4ca2d2351608dfd838a50bcc7bbb9"
|
|
65
65
|
}
|