cashscript 0.10.0-next.5 → 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 +2 -2
- package/dist/Argument.d.ts +8 -7
- package/dist/Argument.js +3 -10
- package/dist/Contract.d.ts +4 -4
- package/dist/Contract.js +7 -4
- package/dist/Errors.d.ts +21 -55
- package/dist/Errors.js +59 -57
- 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 +15 -35
- package/dist/TransactionBuilder.d.ts +3 -3
- package/dist/TransactionBuilder.js +3 -2
- package/dist/debugging.d.ts +1 -1
- package/dist/debugging.js +69 -29
- 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 -6
- package/dist/utils.js +8 -32
- package/jest/package.json +5 -0
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
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)
|
|
8
8
|
|
|
9
9
|
CashScript is a high-level programming language for smart contracts on Bitcoin Cash. It offers a strong abstraction layer over Bitcoin Cash' native virtual machine, Bitcoin Script. Its syntax is based on Ethereum's smart contract language Solidity, but its functionality is very different since smart contracts on Bitcoin Cash differ greatly from smart contracts on Ethereum. For a detailed comparison of them, refer to the blog post [*Smart Contracts on Ethereum, Bitcoin and Bitcoin Cash*](https://kalis.me/smart-contracts-eth-btc-bch/).
|
|
10
10
|
|
|
11
|
-
See the [GitHub repository](https://github.com/
|
|
11
|
+
See the [GitHub repository](https://github.com/CashScript/cashscript) and the [CashScript website](https://cashscript.org) for full documentation and usage examples.
|
|
12
12
|
|
|
13
13
|
## The CashScript Language
|
|
14
14
|
CashScript is a high-level language that allows you to write Bitcoin Cash smart contracts in a straightforward and familiar way. Its syntax is inspired by Ethereum's Solidity language, but its functionality is different since the underlying systems have very different fundamentals. See the [language documentation](https://cashscript.org/docs/language/) for a full reference of the language.
|
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';
|
|
@@ -16,7 +16,7 @@ export class Contract {
|
|
|
16
16
|
throw new Error('Invalid or incomplete artifact provided');
|
|
17
17
|
}
|
|
18
18
|
if (artifact.constructorInputs.length !== constructorArgs.length) {
|
|
19
|
-
throw new Error(`Incorrect number of arguments passed to ${artifact.contractName} constructor`);
|
|
19
|
+
throw new Error(`Incorrect number of arguments passed to ${artifact.contractName} constructor. Expected ${artifact.constructorInputs.length} arguments (${artifact.constructorInputs.map(input => input.type)}) but got ${constructorArgs.length}`);
|
|
20
20
|
}
|
|
21
21
|
// Encode arguments (this also performs type checking)
|
|
22
22
|
this.encodedConstructorArgs = encodeConstructorArguments(artifact, constructorArgs);
|
|
@@ -62,7 +62,7 @@ export class Contract {
|
|
|
62
62
|
createFunction(abiFunction, selector) {
|
|
63
63
|
return (...args) => {
|
|
64
64
|
if (abiFunction.inputs.length !== args.length) {
|
|
65
|
-
throw new Error(`Incorrect number of arguments passed to function ${abiFunction.name}`);
|
|
65
|
+
throw new Error(`Incorrect number of arguments passed to function ${abiFunction.name}. Expected ${abiFunction.inputs.length} arguments (${abiFunction.inputs.map(input => input.type)}) but got ${args.length}`);
|
|
66
66
|
}
|
|
67
67
|
// Encode passed args (this also performs type checking)
|
|
68
68
|
const encodedArgs = encodeFunctionArguments(abiFunction, args);
|
|
@@ -72,9 +72,12 @@ export class Contract {
|
|
|
72
72
|
}
|
|
73
73
|
createUnlocker(abiFunction, selector) {
|
|
74
74
|
return (...args) => {
|
|
75
|
+
if (abiFunction.inputs.length !== args.length) {
|
|
76
|
+
throw new Error(`Incorrect number of arguments passed to function ${abiFunction.name}. Expected ${abiFunction.inputs.length} arguments (${abiFunction.inputs.map(input => input.type)}) but got ${args.length}`);
|
|
77
|
+
}
|
|
75
78
|
const bytecode = scriptToBytecode(this.redeemScript);
|
|
76
79
|
const encodedArgs = args
|
|
77
|
-
.map((arg, i) =>
|
|
80
|
+
.map((arg, i) => encodeFunctionArgument(arg, abiFunction.inputs[i].type));
|
|
78
81
|
const generateUnlockingBytecode = ({ transaction, sourceOutputs, inputIndex }) => {
|
|
79
82
|
// TODO: Remove old-style covenant code for v1.0 release
|
|
80
83
|
let covenantHashType = -1;
|
package/dist/Errors.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { 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,62 +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
|
-
|
|
14
|
-
constructor(reason: string,
|
|
15
|
-
}
|
|
16
|
-
export declare class FailedRequireError extends FailedTransactionError {
|
|
16
|
+
bitauthUri?: string | undefined;
|
|
17
|
+
constructor(reason: string, bitauthUri?: string | undefined);
|
|
17
18
|
}
|
|
18
|
-
export declare class
|
|
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);
|
|
19
26
|
}
|
|
20
|
-
export declare class
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
CHECKDATASIGVERIFY = "Script failed an OP_CHECKDATASIGVERIFY operation",
|
|
29
|
-
NUMEQUALVERIFY = "Script failed an OP_NUMEQUALVERIFY operation",
|
|
30
|
-
SCRIPT_SIZE = "Script is too big",
|
|
31
|
-
PUSH_SIZE = "Push value size limit exceeded",
|
|
32
|
-
OP_COUNT = "Operation limit exceeded",
|
|
33
|
-
STACK_SIZE = "Stack size limit exceeded",
|
|
34
|
-
SIG_COUNT = "Signature count negative or greater than pubkey count",
|
|
35
|
-
PUBKEY_COUNT = "Pubkey count negative or limit exceeded",
|
|
36
|
-
INVALID_OPERAND_SIZE = "Invalid operand size",
|
|
37
|
-
INVALID_NUMBER_RANGE = "Given operand is not a number within the valid range",
|
|
38
|
-
IMPOSSIBLE_ENCODING = "The requested encoding is impossible to satisfy",
|
|
39
|
-
INVALID_SPLIT_RANGE = "Invalid OP_SPLIT range",
|
|
40
|
-
INVALID_BIT_COUNT = "Invalid number of bit set in OP_CHECKMULTISIG",
|
|
41
|
-
BAD_OPCODE = "Opcode missing or not understood",
|
|
42
|
-
DISABLED_OPCODE = "Attempted to use a disabled opcode",
|
|
43
|
-
INVALID_STACK_OPERATION = "Operation not valid with the current stack size",
|
|
44
|
-
INVALID_ALTSTACK_OPERATION = "Operation not valid with the current altstack size",
|
|
45
|
-
OP_RETURN = "OP_RETURN was encountered",
|
|
46
|
-
UNBALANCED_CONDITIONAL = "Invalid OP_IF construction",
|
|
47
|
-
DIV_BY_ZERO = "Division by zero error",
|
|
48
|
-
MOD_BY_ZERO = "Modulo by zero error",
|
|
49
|
-
INVALID_BITFIELD_SIZE = "Bitfield of unexpected size error",
|
|
50
|
-
INVALID_BIT_RANGE = "Bitfield's bit out of the expected range",
|
|
51
|
-
NEGATIVE_LOCKTIME = "Negative locktime",
|
|
52
|
-
UNSATISFIED_LOCKTIME = "Locktime requirement not satisfied",
|
|
53
|
-
SIG_HASHTYPE = "Signature hash type missing or not understood",
|
|
54
|
-
SIG_DER = "Non-canonical DER signature",
|
|
55
|
-
MINIMALDATA = "Data push larger than necessary",
|
|
56
|
-
SIG_PUSHONLY = "Only push operators allowed in signature scripts",
|
|
57
|
-
SIG_HIGH_S = "Non-canonical signature: S value is unnecessarily high",
|
|
58
|
-
MINIMALIF = "OP_IF/NOTIF argument must be minimal",
|
|
59
|
-
SIG_NULLFAIL = "Signature must be zero for failed CHECK(MULTI)SIG operation",
|
|
60
|
-
SIG_BADLENGTH = "Signature cannot be 65 bytes in CHECKMULTISIG",
|
|
61
|
-
SIG_NONSCHNORR = "Only Schnorr signatures allowed in this operation",
|
|
62
|
-
DISCOURAGE_UPGRADABLE_NOPS = "NOPx reserved for soft-fork upgrades",
|
|
63
|
-
PUBKEYTYPE = "Public key is neither compressed or uncompressed",
|
|
64
|
-
CLEANSTACK = "Script did not clean its stack",
|
|
65
|
-
NONCOMPRESSED_PUBKEY = "Using non-compressed public key",
|
|
66
|
-
ILLEGAL_FORKID = "Illegal use of SIGHASH_FORKID",
|
|
67
|
-
MUST_USE_FORKID = "Signature must use SIGHASH_FORKID",
|
|
68
|
-
UNKNOWN = "unknown error"
|
|
27
|
+
export declare class FailedRequireError extends FailedTransactionError {
|
|
28
|
+
artifact: Artifact;
|
|
29
|
+
failingInstructionPointer: number;
|
|
30
|
+
requireStatement: RequireStatement;
|
|
31
|
+
inputIndex: number;
|
|
32
|
+
bitauthUri: string;
|
|
33
|
+
libauthErrorMessage?: string | undefined;
|
|
34
|
+
constructor(artifact: Artifact, failingInstructionPointer: number, requireStatement: RequireStatement, inputIndex: number, bitauthUri: string, libauthErrorMessage?: string | undefined);
|
|
69
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,67 +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
|
-
constructor(reason,
|
|
18
|
-
super(
|
|
23
|
+
constructor(reason, bitauthUri) {
|
|
24
|
+
super(`${reason}${bitauthUri ? `\n\nBitauth URI: ${bitauthUri}` : ''}`);
|
|
19
25
|
this.reason = reason;
|
|
20
|
-
this.
|
|
26
|
+
this.bitauthUri = bitauthUri;
|
|
21
27
|
}
|
|
22
28
|
}
|
|
23
|
-
export class
|
|
24
|
-
|
|
25
|
-
|
|
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
|
+
}
|
|
26
43
|
}
|
|
27
|
-
export class
|
|
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;
|
|
60
|
+
this.requireStatement = requireStatement;
|
|
61
|
+
this.inputIndex = inputIndex;
|
|
62
|
+
this.bitauthUri = bitauthUri;
|
|
63
|
+
this.libauthErrorMessage = libauthErrorMessage;
|
|
64
|
+
}
|
|
28
65
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
Reason["PUBKEY_COUNT"] = "Pubkey count negative or limit exceeded";
|
|
45
|
-
Reason["INVALID_OPERAND_SIZE"] = "Invalid operand size";
|
|
46
|
-
Reason["INVALID_NUMBER_RANGE"] = "Given operand is not a number within the valid range";
|
|
47
|
-
Reason["IMPOSSIBLE_ENCODING"] = "The requested encoding is impossible to satisfy";
|
|
48
|
-
Reason["INVALID_SPLIT_RANGE"] = "Invalid OP_SPLIT range";
|
|
49
|
-
Reason["INVALID_BIT_COUNT"] = "Invalid number of bit set in OP_CHECKMULTISIG";
|
|
50
|
-
Reason["BAD_OPCODE"] = "Opcode missing or not understood";
|
|
51
|
-
Reason["DISABLED_OPCODE"] = "Attempted to use a disabled opcode";
|
|
52
|
-
Reason["INVALID_STACK_OPERATION"] = "Operation not valid with the current stack size";
|
|
53
|
-
Reason["INVALID_ALTSTACK_OPERATION"] = "Operation not valid with the current altstack size";
|
|
54
|
-
Reason["OP_RETURN"] = "OP_RETURN was encountered";
|
|
55
|
-
Reason["UNBALANCED_CONDITIONAL"] = "Invalid OP_IF construction";
|
|
56
|
-
Reason["DIV_BY_ZERO"] = "Division by zero error";
|
|
57
|
-
Reason["MOD_BY_ZERO"] = "Modulo by zero error";
|
|
58
|
-
Reason["INVALID_BITFIELD_SIZE"] = "Bitfield of unexpected size error";
|
|
59
|
-
Reason["INVALID_BIT_RANGE"] = "Bitfield's bit out of the expected range";
|
|
60
|
-
Reason["NEGATIVE_LOCKTIME"] = "Negative locktime";
|
|
61
|
-
Reason["UNSATISFIED_LOCKTIME"] = "Locktime requirement not satisfied";
|
|
62
|
-
Reason["SIG_HASHTYPE"] = "Signature hash type missing or not understood";
|
|
63
|
-
Reason["SIG_DER"] = "Non-canonical DER signature";
|
|
64
|
-
Reason["MINIMALDATA"] = "Data push larger than necessary";
|
|
65
|
-
Reason["SIG_PUSHONLY"] = "Only push operators allowed in signature scripts";
|
|
66
|
-
Reason["SIG_HIGH_S"] = "Non-canonical signature: S value is unnecessarily high";
|
|
67
|
-
Reason["MINIMALIF"] = "OP_IF/NOTIF argument must be minimal";
|
|
68
|
-
Reason["SIG_NULLFAIL"] = "Signature must be zero for failed CHECK(MULTI)SIG operation";
|
|
69
|
-
Reason["SIG_BADLENGTH"] = "Signature cannot be 65 bytes in CHECKMULTISIG";
|
|
70
|
-
Reason["SIG_NONSCHNORR"] = "Only Schnorr signatures allowed in this operation";
|
|
71
|
-
Reason["DISCOURAGE_UPGRADABLE_NOPS"] = "NOPx reserved for soft-fork upgrades";
|
|
72
|
-
Reason["PUBKEYTYPE"] = "Public key is neither compressed or uncompressed";
|
|
73
|
-
Reason["CLEANSTACK"] = "Script did not clean its stack";
|
|
74
|
-
Reason["NONCOMPRESSED_PUBKEY"] = "Using non-compressed public key";
|
|
75
|
-
Reason["ILLEGAL_FORKID"] = "Illegal use of SIGHASH_FORKID";
|
|
76
|
-
Reason["MUST_USE_FORKID"] = "Signature must use SIGHASH_FORKID";
|
|
77
|
-
Reason["UNKNOWN"] = "unknown error";
|
|
78
|
-
})(Reason || (Reason = {}));
|
|
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
|
+
};
|
|
79
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
|
@@ -4,13 +4,13 @@ import delay from 'delay';
|
|
|
4
4
|
import { placeholder, scriptToBytecode, } from '@cashscript/utils';
|
|
5
5
|
import deepEqual from 'fast-deep-equal';
|
|
6
6
|
import { isUtxoP2PKH, } from './interfaces.js';
|
|
7
|
-
import { createInputScript, getInputSize, createOpReturnOutput, getTxSizeWithoutInputs, getPreimageSize,
|
|
7
|
+
import { createInputScript, getInputSize, createOpReturnOutput, getTxSizeWithoutInputs, getPreimageSize, validateOutput, utxoComparator, calculateDust, getOutputSize, utxoTokenComparator, } from './utils.js';
|
|
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
|
|
12
|
+
import { debugTemplate } from './debugging.js';
|
|
13
|
+
import { FailedTransactionError } from './Errors.js';
|
|
14
14
|
export class Transaction {
|
|
15
15
|
constructor(contract, unlocker, abiFunction, encodedFunctionArgs, selector) {
|
|
16
16
|
this.contract = contract;
|
|
@@ -101,45 +101,25 @@ export class Transaction {
|
|
|
101
101
|
}
|
|
102
102
|
async send(raw) {
|
|
103
103
|
const tx = await this.build();
|
|
104
|
-
|
|
104
|
+
// Debug the transaction locally before sending so any errors are caught early
|
|
105
|
+
// Libauth debugging does not work with old-style covenants
|
|
106
|
+
if (!this.abiFunction.covenant) {
|
|
107
|
+
await this.debug();
|
|
108
|
+
}
|
|
105
109
|
try {
|
|
106
|
-
if (this.contract.provider instanceof MockNetworkProvider) {
|
|
107
|
-
template = await buildTemplate({
|
|
108
|
-
transaction: this,
|
|
109
|
-
transactionHex: tx,
|
|
110
|
-
});
|
|
111
|
-
evaluateTemplate(template);
|
|
112
|
-
}
|
|
113
110
|
const txid = await this.contract.provider.sendRawTransaction(tx);
|
|
114
111
|
return raw ? await this.getTxDetails(txid, raw) : await this.getTxDetails(txid);
|
|
115
112
|
}
|
|
116
|
-
catch (
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
transaction: this,
|
|
120
|
-
transactionHex: tx,
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
const reason = maybeNodeError.error ?? maybeNodeError.message ?? maybeNodeError;
|
|
124
|
-
const bitauthUri = getBitauthUri(template);
|
|
125
|
-
try {
|
|
126
|
-
debugTemplate(template, this.contract.artifact);
|
|
127
|
-
}
|
|
128
|
-
catch (libauthError) {
|
|
129
|
-
if (this.contract.provider instanceof MockNetworkProvider) {
|
|
130
|
-
throw buildError(libauthError, bitauthUri);
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
const message = `${libauthError}\n\nUnderlying node error: ${reason}`;
|
|
134
|
-
throw buildError(message, bitauthUri);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
// this must be unreachable
|
|
138
|
-
throw buildError(reason, bitauthUri);
|
|
113
|
+
catch (error) {
|
|
114
|
+
const reason = error.error ?? error.message ?? error;
|
|
115
|
+
throw new FailedTransactionError(reason, await this.bitauthUri());
|
|
139
116
|
}
|
|
140
117
|
}
|
|
141
118
|
// method to debug the transaction with libauth VM, throws upon evaluation error
|
|
142
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
|
+
}
|
|
143
123
|
const template = await this.getLibauthTemplate();
|
|
144
124
|
return debugTemplate(template, this.contract.artifact);
|
|
145
125
|
}
|
|
@@ -169,7 +149,7 @@ export class Transaction {
|
|
|
169
149
|
}
|
|
170
150
|
async setInputsAndOutputs() {
|
|
171
151
|
if (this.outputs.length === 0) {
|
|
172
|
-
throw Error('Attempted to build a transaction without outputs');
|
|
152
|
+
throw new Error('Attempted to build a transaction without outputs');
|
|
173
153
|
}
|
|
174
154
|
// Fetched utxos are only used when no inputs are available, so only fetch in that case.
|
|
175
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);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { binToHex, decodeTransaction, encodeTransaction, hexToBin, } from '@bitauth/libauth';
|
|
2
2
|
import delay from 'delay';
|
|
3
3
|
import { isUnlockableUtxo, } from './interfaces.js';
|
|
4
|
-
import {
|
|
4
|
+
import { cashScriptOutputToLibauthOutput, createOpReturnOutput, validateOutput, } from './utils.js';
|
|
5
|
+
import { FailedTransactionError } from './Errors.js';
|
|
5
6
|
const DEFAULT_SEQUENCE = 0xfffffffe;
|
|
6
7
|
export class TransactionBuilder {
|
|
7
8
|
constructor(options) {
|
|
@@ -94,7 +95,7 @@ export class TransactionBuilder {
|
|
|
94
95
|
}
|
|
95
96
|
catch (e) {
|
|
96
97
|
const reason = e.error ?? e.message;
|
|
97
|
-
throw
|
|
98
|
+
throw new FailedTransactionError(reason);
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
101
|
async getTxDetails(txid, raw) {
|
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,12 +1,14 @@
|
|
|
1
|
-
import { AuthenticationErrorCommon, binToHex, createCompiler, createVirtualMachineBCH2023, walletTemplateToCompilerConfiguration } from '@bitauth/libauth';
|
|
2
|
-
import { Op, PrimitiveType, decodeBool, decodeInt, decodeString } from '@cashscript/utils';
|
|
1
|
+
import { AuthenticationErrorCommon, binToHex, createCompiler, createVirtualMachineBCH2023, encodeAuthenticationInstruction, walletTemplateToCompilerConfiguration } from '@bitauth/libauth';
|
|
2
|
+
import { Op, PrimitiveType, bytecodeToAsm, decodeBool, decodeInt, decodeString } from '@cashscript/utils';
|
|
3
3
|
import { findLastIndex, toRegExp } from './utils.js';
|
|
4
|
+
import { FailedRequireError, FailedTransactionError, FailedTransactionEvaluationError } from './Errors.js';
|
|
5
|
+
import { getBitauthUri } from './LibauthTemplate.js';
|
|
4
6
|
// evaluates the fully defined template, throws upon error
|
|
5
7
|
export const evaluateTemplate = (template) => {
|
|
6
8
|
const { vm, program } = createProgram(template);
|
|
7
9
|
const verifyResult = vm.verify(program);
|
|
8
10
|
if (typeof verifyResult === 'string') {
|
|
9
|
-
throw new
|
|
11
|
+
throw new FailedTransactionError(verifyResult, getBitauthUri(template));
|
|
10
12
|
}
|
|
11
13
|
return verifyResult;
|
|
12
14
|
};
|
|
@@ -24,34 +26,48 @@ export const debugTemplate = (template, artifact) => {
|
|
|
24
26
|
const executedLogs = (artifact.debug?.logs ?? [])
|
|
25
27
|
.filter((debugStep) => executedDebugSteps.some((log) => log.ip === debugStep.ip));
|
|
26
28
|
for (const log of executedLogs) {
|
|
27
|
-
|
|
28
|
-
logConsoleLogStatement(log, correspondingDebugStep, artifact);
|
|
29
|
+
logConsoleLogStatement(log, executedDebugSteps, artifact);
|
|
29
30
|
}
|
|
30
31
|
const lastExecutedDebugStep = executedDebugSteps[executedDebugSteps.length - 1];
|
|
31
32
|
// If an error is present in the last step, that means a require statement in the middle of the function failed
|
|
32
33
|
if (lastExecutedDebugStep.error) {
|
|
34
|
+
// In Libauth any thrown error gets registered in the instruction that happens right *after* the instruction that
|
|
35
|
+
// caused the error (in other words the OP_VERIFY). We need to decrement the instruction pointer to get the correct
|
|
36
|
+
// failing instruction.
|
|
37
|
+
const failingIp = lastExecutedDebugStep.ip - 1;
|
|
38
|
+
// Generally speaking, an error is thrown by the OP_VERIFY opcode, but for NULLFAIL, the error is thrown in the
|
|
39
|
+
// preceding OP_CHECKSIG opcode. The error message is registered in the next instruction, so we need to increment
|
|
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
|
|
33
42
|
const isNullFail = lastExecutedDebugStep.error === AuthenticationErrorCommon.nonNullSignatureFailure;
|
|
34
|
-
const
|
|
35
|
-
const requireStatement = (artifact.debug?.
|
|
36
|
-
.find((
|
|
43
|
+
const requireStatementIp = failingIp + (isNullFail ? 1 : 0);
|
|
44
|
+
const requireStatement = (artifact.debug?.requires ?? [])
|
|
45
|
+
.find((statement) => statement.ip === requireStatementIp);
|
|
46
|
+
const { program: { inputIndex }, error } = lastExecutedDebugStep;
|
|
37
47
|
if (requireStatement) {
|
|
38
|
-
|
|
48
|
+
// Note that we use failingIp here rather than requireStatementIp, see comment above
|
|
49
|
+
throw new FailedRequireError(artifact, failingIp, requireStatement, inputIndex, getBitauthUri(template), error);
|
|
39
50
|
}
|
|
40
|
-
|
|
51
|
+
// Note that we use failingIp here rather than requireStatementIp, see comment above
|
|
52
|
+
throw new FailedTransactionEvaluationError(artifact, failingIp, inputIndex, getBitauthUri(template), error);
|
|
41
53
|
}
|
|
42
54
|
const evaluationResult = vm.verify(program);
|
|
43
55
|
if (failedFinalVerify(evaluationResult)) {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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;
|
|
61
|
+
// logDebugSteps(executedDebugSteps, lastExecutedDebugStep.instructions);
|
|
62
|
+
// console.warn('message', finalExecutedVerifyIp);
|
|
63
|
+
// console.warn(artifact.debug?.requires);
|
|
64
|
+
const requireStatement = (artifact.debug?.requires ?? [])
|
|
65
|
+
.find((message) => message.ip === finalExecutedVerifyIp);
|
|
66
|
+
const { program: { inputIndex } } = lastExecutedDebugStep;
|
|
67
|
+
if (requireStatement) {
|
|
68
|
+
throw new FailedRequireError(artifact, sourcemapInstructionPointer, requireStatement, inputIndex, getBitauthUri(template));
|
|
53
69
|
}
|
|
54
|
-
throw new
|
|
70
|
+
throw new FailedTransactionEvaluationError(artifact, sourcemapInstructionPointer, inputIndex, getBitauthUri(template), evaluationResult);
|
|
55
71
|
}
|
|
56
72
|
return fullDebugSteps;
|
|
57
73
|
};
|
|
@@ -67,25 +83,28 @@ const createProgram = (template) => {
|
|
|
67
83
|
scenarioId: 'evaluate_function',
|
|
68
84
|
});
|
|
69
85
|
if (typeof scenarioGeneration === 'string') {
|
|
70
|
-
throw new
|
|
86
|
+
throw new FailedTransactionError(scenarioGeneration, getBitauthUri(template));
|
|
71
87
|
}
|
|
72
88
|
if (typeof scenarioGeneration.scenario === 'string') {
|
|
73
|
-
throw new
|
|
89
|
+
throw new FailedTransactionError(scenarioGeneration.scenario, getBitauthUri(template));
|
|
74
90
|
}
|
|
75
91
|
return { vm, program: scenarioGeneration.scenario.program };
|
|
76
92
|
};
|
|
77
|
-
const logConsoleLogStatement = (log,
|
|
93
|
+
const logConsoleLogStatement = (log, debugSteps, artifact) => {
|
|
78
94
|
let line = `${artifact.contractName}.cash:${log.line}`;
|
|
79
|
-
const decodedData = log.data.map((element) =>
|
|
95
|
+
const decodedData = log.data.map((element) => {
|
|
96
|
+
if (typeof element === 'string')
|
|
97
|
+
return element;
|
|
98
|
+
const debugStep = debugSteps.find((step) => step.ip === element.ip);
|
|
99
|
+
return decodeStackItem(element, debugStep.stack);
|
|
100
|
+
});
|
|
80
101
|
console.log(`${line} ${decodedData.join(' ')}`);
|
|
81
102
|
};
|
|
82
|
-
const
|
|
83
|
-
if (typeof element === 'string')
|
|
84
|
-
return element;
|
|
103
|
+
const decodeStackItem = (element, stack) => {
|
|
85
104
|
// Reversed since stack is in reverse order
|
|
86
105
|
const stackItem = [...stack].reverse()[element.stackIndex];
|
|
87
106
|
if (!stackItem) {
|
|
88
|
-
throw Error(`Stack item at index ${element.stackIndex} not found at instruction pointer ${ip}`);
|
|
107
|
+
throw Error(`Stack item at index ${element.stackIndex} not found at instruction pointer ${element.ip}`);
|
|
89
108
|
}
|
|
90
109
|
if (element.type === PrimitiveType.BOOL)
|
|
91
110
|
return decodeBool(stackItem);
|
|
@@ -113,7 +132,9 @@ const calculateCleanupSize = (instructions) => {
|
|
|
113
132
|
const cleanupOpcodes = [Op.OP_ENDIF, Op.OP_NIP, Op.OP_ELSE];
|
|
114
133
|
let cleanupSize = 0;
|
|
115
134
|
for (const instruction of [...instructions].reverse()) {
|
|
116
|
-
|
|
135
|
+
// The instructions array may contain undefined elements for the final executed debug steps that are not
|
|
136
|
+
// technically instructions (e.g. the final *implicit* verify). We still want to get rid of these in the cleanup
|
|
137
|
+
if (!instruction || cleanupOpcodes.includes(instruction.opcode)) {
|
|
117
138
|
cleanupSize++;
|
|
118
139
|
}
|
|
119
140
|
else {
|
|
@@ -122,4 +143,23 @@ const calculateCleanupSize = (instructions) => {
|
|
|
122
143
|
}
|
|
123
144
|
return cleanupSize;
|
|
124
145
|
};
|
|
146
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
147
|
+
const logDebugSteps = (debugSteps, instructions) => {
|
|
148
|
+
debugSteps.map((step) => {
|
|
149
|
+
const opcode = instructions[step.ip] ? bytecodeToAsm(encodeAuthenticationInstruction(instructions[step.ip])) : 'null';
|
|
150
|
+
console.warn(step.ip, opcode, step.stack, step.error);
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
const getFinalExecutedVerifyIp = (executedDebugSteps) => {
|
|
154
|
+
// Map every executed debug step to its corresponding instruction
|
|
155
|
+
// (note that the last executed step(s) may not have a corresponding instruction and will be `undefined`)
|
|
156
|
+
const executedInstructions = executedDebugSteps.map((step) => step.instructions[step.ip]);
|
|
157
|
+
const finalExecutedDebugStepIndex = executedDebugSteps.length - 1;
|
|
158
|
+
const cleanupSize = calculateCleanupSize(executedInstructions);
|
|
159
|
+
const finalExecutedNonCleanupStep = executedDebugSteps[finalExecutedDebugStepIndex - cleanupSize];
|
|
160
|
+
// The final verify in a function is dropped, so after cleanup, we get the final OP that was executed *before* the
|
|
161
|
+
// final verify, so we need to add one to get the actual final verify instruction
|
|
162
|
+
const finalExecutedVerifyIp = finalExecutedNonCleanupStep.ip + 1;
|
|
163
|
+
return finalExecutedVerifyIp;
|
|
164
|
+
};
|
|
125
165
|
//# sourceMappingURL=debugging.js.map
|
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
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Transaction } from '@bitauth/libauth';
|
|
2
2
|
import { Script } from '@cashscript/utils';
|
|
3
3
|
import { Utxo, Output, LibauthOutput, TokenDetails, AddressType } from './interfaces.js';
|
|
4
|
-
import { FailedTransactionError } from './Errors.js';
|
|
5
4
|
export declare function validateOutput(output: Output): void;
|
|
6
5
|
export declare function calculateDust(output: Output): number;
|
|
7
6
|
export declare function getOutputSize(output: Output): number;
|
|
@@ -14,7 +13,6 @@ export declare function getTxSizeWithoutInputs(outputs: Output[]): number;
|
|
|
14
13
|
export declare function createInputScript(redeemScript: Script, encodedArgs: Uint8Array[], selector?: number, preimage?: Uint8Array): Uint8Array;
|
|
15
14
|
export declare function createOpReturnOutput(opReturnData: string[]): Output;
|
|
16
15
|
export declare function createSighashPreimage(transaction: Transaction, sourceOutputs: LibauthOutput[], inputIndex: number, coveredBytecode: Uint8Array, hashtype: number): Uint8Array;
|
|
17
|
-
export declare function buildError(reason: string, debugStr?: string): FailedTransactionError;
|
|
18
16
|
export declare function toRegExp(reasons: string[]): RegExp;
|
|
19
17
|
export declare function scriptToAddress(script: Script, network: string, addressType: AddressType, tokenSupport: boolean): string;
|
|
20
18
|
export declare function scriptToLockingBytecode(script: Script, addressType: AddressType): Uint8Array;
|
|
@@ -30,10 +28,10 @@ export declare function utxoTokenComparator(a: Utxo, b: Utxo): number;
|
|
|
30
28
|
*/
|
|
31
29
|
export declare function addressToLockScript(address: string): Uint8Array;
|
|
32
30
|
export declare function getNetworkPrefix(network: string): 'bitcoincash' | 'bchtest' | 'bchreg';
|
|
33
|
-
export declare const randomUtxo: (defaults?: Partial<Utxo>
|
|
34
|
-
export declare const randomToken: (defaults?: Partial<TokenDetails>
|
|
35
|
-
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;
|
|
36
34
|
export declare function findLastIndex<T>(array: Array<T>, predicate: (value: T, index: number, obj: T[]) => boolean): number;
|
|
37
35
|
export declare const snakeCase: (str: string) => string;
|
|
38
|
-
export declare const extendedStringify: (obj: any, spaces?: number
|
|
36
|
+
export declare const extendedStringify: (obj: any, spaces?: number) => string;
|
|
39
37
|
export declare const zip: <T, U>(a: T[], b: U[]) => [T, U][];
|
package/dist/utils.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { cashAddressToLockingBytecode, decodeCashAddress, addressContentsToLockingBytecode, lockingBytecodeToCashAddress, binToHex, generateSigningSerializationBCH, utf8ToBin, hexToBin, flattenBinArray, LockingBytecodeType, encodeTransactionOutput, isHex, bigIntToCompactUint,
|
|
1
|
+
import { cashAddressToLockingBytecode, decodeCashAddress, addressContentsToLockingBytecode, lockingBytecodeToCashAddress, binToHex, generateSigningSerializationBCH, utf8ToBin, hexToBin, flattenBinArray, LockingBytecodeType, encodeTransactionOutput, isHex, bigIntToCompactUint, NonFungibleTokenCapability, bigIntToVmNumber, } from '@bitauth/libauth';
|
|
2
2
|
import { encodeInt, hash160, hash256, sha256, Op, scriptToBytecode, } from '@cashscript/utils';
|
|
3
3
|
import { Network, } from './interfaces.js';
|
|
4
4
|
import { VERSION_SIZE, LOCKTIME_SIZE } from './constants.js';
|
|
5
|
-
import { OutputSatoshisTooSmallError, TokensToNonTokenAddressError,
|
|
5
|
+
import { OutputSatoshisTooSmallError, TokensToNonTokenAddressError, } from './Errors.js';
|
|
6
6
|
// ////////// PARAMETER VALIDATION ////////////////////////////////////////////
|
|
7
7
|
export function validateOutput(output) {
|
|
8
8
|
if (typeof output.to !== 'string')
|
|
@@ -135,40 +135,16 @@ export function createSighashPreimage(transaction, sourceOutputs, inputIndex, co
|
|
|
135
135
|
const sighashPreimage = generateSigningSerializationBCH(context, { coveredBytecode, signingSerializationType });
|
|
136
136
|
return sighashPreimage;
|
|
137
137
|
}
|
|
138
|
-
export function buildError(reason, debugStr) {
|
|
139
|
-
const require = [
|
|
140
|
-
Reason.EVAL_FALSE, Reason.VERIFY, Reason.EQUALVERIFY, Reason.CHECKMULTISIGVERIFY,
|
|
141
|
-
Reason.CHECKSIGVERIFY, Reason.CHECKDATASIGVERIFY, Reason.NUMEQUALVERIFY,
|
|
142
|
-
AuthenticationErrorCommon.failedVerify,
|
|
143
|
-
];
|
|
144
|
-
const timeCheck = [
|
|
145
|
-
Reason.NEGATIVE_LOCKTIME, Reason.UNSATISFIED_LOCKTIME,
|
|
146
|
-
AuthenticationErrorCommon.negativeLocktime, AuthenticationErrorCommon.unsatisfiedLocktime,
|
|
147
|
-
];
|
|
148
|
-
const sigCheck = [
|
|
149
|
-
Reason.SIG_COUNT, Reason.PUBKEY_COUNT, Reason.SIG_HASHTYPE, Reason.SIG_DER,
|
|
150
|
-
Reason.SIG_HIGH_S, Reason.SIG_NULLFAIL, Reason.SIG_BADLENGTH, Reason.SIG_NONSCHNORR,
|
|
151
|
-
AuthenticationErrorCommon.nonNullSignatureFailure,
|
|
152
|
-
];
|
|
153
|
-
if (toRegExp(require).test(reason)) {
|
|
154
|
-
return new FailedRequireError(reason, debugStr);
|
|
155
|
-
}
|
|
156
|
-
if (toRegExp(timeCheck).test(reason)) {
|
|
157
|
-
return new FailedTimeCheckError(reason, debugStr);
|
|
158
|
-
}
|
|
159
|
-
if (toRegExp(sigCheck).test(reason)) {
|
|
160
|
-
return new FailedSigCheckError(reason, debugStr);
|
|
161
|
-
}
|
|
162
|
-
return new FailedTransactionError(reason, debugStr);
|
|
163
|
-
}
|
|
164
138
|
export function toRegExp(reasons) {
|
|
165
139
|
return new RegExp(reasons.join('|').replace(/\(/g, '\\(').replace(/\)/g, '\\)'));
|
|
166
140
|
}
|
|
167
141
|
export function scriptToAddress(script, network, addressType, tokenSupport) {
|
|
168
|
-
const
|
|
142
|
+
const bytecode = scriptToLockingBytecode(script, addressType);
|
|
169
143
|
const prefix = getNetworkPrefix(network);
|
|
170
|
-
const
|
|
171
|
-
|
|
144
|
+
const result = lockingBytecodeToCashAddress({ bytecode, prefix, tokenSupport });
|
|
145
|
+
if (typeof result === 'string')
|
|
146
|
+
throw new Error(result);
|
|
147
|
+
return result.address;
|
|
172
148
|
}
|
|
173
149
|
export function scriptToLockingBytecode(script, addressType) {
|
|
174
150
|
const scriptBytecode = scriptToBytecode(script);
|
|
@@ -247,7 +223,7 @@ function getPushDataOpcode(data) {
|
|
|
247
223
|
return Uint8Array.from([byteLength]);
|
|
248
224
|
if (byteLength < 256)
|
|
249
225
|
return Uint8Array.from([0x4c, byteLength]);
|
|
250
|
-
throw Error('Pushdata too large');
|
|
226
|
+
throw new Error('Pushdata too large');
|
|
251
227
|
}
|
|
252
228
|
const randomInt = () => BigInt(Math.floor(Math.random() * 10000));
|
|
253
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
|
}
|