diff --git a/.cspell.json b/.cspell.json index 948da88f2..a14829167 100644 --- a/.cspell.json +++ b/.cspell.json @@ -1,5 +1,5 @@ { - "version": "0.1", + "version": "0.2", "language": "en-GB", "words": [ "aave", @@ -11,6 +11,7 @@ "authchain", "anyhedge", "anyonecanpay", + "backpointer", "badlength", "bchjs", "bchreg", @@ -31,9 +32,10 @@ "boolor", "bytecode", "bytesize", + "callees", "cashaddress", "cashc", - "cashproof", + "cashlibs", "cashscript", "cashtokens", "castable", @@ -48,11 +50,16 @@ "chipnet", "cleanstack", "cleanup", + "cleanups", + "remappings", + "codegen", "cryptocurrency", "collateralized", "datasig", "defi", "deserialisation", + "destructures", + "divmod", "docblock", "electroncash", "electrum", @@ -79,7 +86,6 @@ "hashprevouts", "hashsequence", "hashtype", - "hashtypes", "hodl", "hodling", "htlc", @@ -108,6 +114,8 @@ "lshift", "LSHIFTNUM", "LSHIFTBIN", + "math", + "mathlib", "mecenas", "meep", "minimaldata", @@ -116,6 +124,7 @@ "n", "narrowings", "noncompressed", + "nonexistent", "nonfinal", "nonschnorr", "nops", @@ -130,6 +139,7 @@ "op", "opcode", "opcodes", + "opcost", "opcount", "OUTPOINTINDEX", "OUTPOINTTXHASH", diff --git a/.gitignore b/.gitignore index 05c8b47fe..339562340 100644 --- a/.gitignore +++ b/.gitignore @@ -108,3 +108,6 @@ typings/ manual-test.ts .claude/ + +# Test fixtures that emulate installed packages (re-included despite the node_modules ignore above) +!packages/cashc/test/import-fixtures/**/node_modules/ diff --git a/AGENTS.md b/AGENTS.md index 489da0c1e..3d84b4ba6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,7 @@ # CLAUDE.md +NEVER stage or unstage changes, just leave them in the working directory. + This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview @@ -82,7 +84,7 @@ Shared between compiler and SDK: - `source-map.ts` — source map encoding/decoding (format: `sl:sc:el:ec:h` per opcode, `;`-separated, with field inheritance compression) - `bitauth-script.ts` — formats bytecode as human-readable BitAuth script (used in debugging) - `types.ts` — shared types including `SourceTagKind`, `SourceTagEntry` -- `optimisations.ts` / `cashproof-optimisations.ts` — peephole optimization rules +- `optimisations.ts` — peephole optimization rules ## Code Conventions diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 95b52c5a6..a202d27ed 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -38,24 +38,6 @@ When updating the grammar file in `src/grammar/CashScript.g4`, we also need to m yarn antlr ``` -### Running `cashproof` - -Most of the bytecode optimisations that the `cashc` compiler uses can be verified for correctness using the [`cashproof` tool](https://github.com/EyeOfPython/cashproof). This tool needs to be installed separately by installing its dependencies using `pip` and cloning its repository from GitHub. - -From there, you can run `python [filenames]` to verify that the optimisations contained in these files are provably correct. - -Example: -```bash -python packages/cashc/test/cashproof/0.1.2=0.2.0.equiv -``` - -Note that if you want to run `cashproof` on the "main" CashScript optimisations file, you need to first extract the optimisations from the `cashc` compiler and save them in a separate file. This can be done using the following commands: - -```bash -cp packages/utils/src/cashproof-optimisations.ts opt.equiv && sed -i '' '/`/d' opt.equiv -python opt.equiv -``` - ## cashscript ### Running tests diff --git a/examples/package.json b/examples/package.json index 8e8077077..67c9fd3df 100644 --- a/examples/package.json +++ b/examples/package.json @@ -1,7 +1,7 @@ { "name": "cashscript-examples", "private": true, - "version": "0.13.3", + "version": "0.14.0-next.5", "description": "Usage examples of the CashScript SDK", "main": "p2pkh.js", "type": "module", @@ -12,9 +12,9 @@ }, "dependencies": { "@bitauth/libauth": "^3.1.0-next.8", - "@types/node": "^22.17.0", - "cashc": "^0.13.3", - "cashscript": "^0.13.3", + "@types/node": "^24.13.3", + "cashc": "^0.14.0-next.5", + "cashscript": "^0.14.0-next.5", "eslint": "^8.56.0", "typescript": "^5.9.2" } diff --git a/examples/testing-suite/package.json b/examples/testing-suite/package.json index 4398da009..6778fbf83 100644 --- a/examples/testing-suite/package.json +++ b/examples/testing-suite/package.json @@ -1,6 +1,6 @@ { "name": "testing-suite", - "version": "0.13.3", + "version": "0.14.0-next.5", "description": "Example project to develop and test CashScript contracts", "main": "index.js", "type": "module", @@ -18,12 +18,12 @@ }, "dependencies": { "@bitauth/libauth": "^3.1.0-next.8", - "cashc": "^0.13.3", - "cashscript": "^0.13.3" + "cashc": "^0.14.0-next.5", + "cashscript": "^0.14.0-next.5" }, "devDependencies": { - "tsx": "^4.20.3", + "tsx": "^4.23.12", "typescript": "^5.9.2", - "vitest": "^4.0.15" + "vitest": "^4.1.10" } } diff --git a/package.json b/package.json index 003bae23d..64b1fc307 100644 --- a/package.json +++ b/package.json @@ -8,17 +8,20 @@ "examples/testing-suite" ], "devDependencies": { - "@types/node": "^22.17.0", + "@types/node": "^24", "@typescript-eslint/eslint-plugin": "^7.0.0", "@typescript-eslint/parser": "^7.0.0", - "cspell": "^9.2.0", + "cspell": "^10.0.1", "eslint": "^8.54.0", "eslint-config-airbnb-typescript": "^18.0.0", "eslint-plugin-import": "^2.31.0", "lerna": "^3.22.1", - "tsx": "^4.20.3", + "tsx": "^4.23.12", "typescript": "^5.9.2" }, + "resolutions": { + "ws": "^8.21.3" + }, "scripts": { "test": "lerna run test --ignore cashscript-examples --ignore testing-suite", "lint": "lerna run lint --ignore cashscript-examples --ignore testing-suite", diff --git a/packages/cashc/package.json b/packages/cashc/package.json index 393cca964..8e581c4f6 100644 --- a/packages/cashc/package.json +++ b/packages/cashc/package.json @@ -1,6 +1,6 @@ { "name": "cashc", - "version": "0.13.3", + "version": "0.14.0-next.5", "description": "Compile Bitcoin Cash contracts to Bitcoin Cash Script or artifacts", "keywords": [ "bitcoin", @@ -48,21 +48,20 @@ }, "dependencies": { "@bitauth/libauth": "^3.1.0-next.8", - "@cashscript/utils": "^0.13.3", + "@cashscript/utils": "^0.14.0-next.5", "antlr4": "^4.13.2", "commander": "^14.0.0", - "semver": "^7.7.2" + "semver": "^7.8.5" }, "devDependencies": { - "@types/node": "^22.17.0", - "@types/semver": "^7.7.0", - "@vitest/coverage-v8": "^4.0.15", - "cpy-cli": "^5.0.0", + "@types/node": "^24.13.3", + "@types/semver": "^7.8.0", + "@vitest/coverage-v8": "^4.1.10", "eslint": "^8.54.0", "eslint-plugin-import": "^2.31.0", - "tsx": "^4.20.3", + "tsx": "^4.23.12", "typescript": "^5.9.2", - "vitest": "^4.0.15" + "vitest": "^4.1.10" }, "gitHead": "bf02a4b641d5d03c035d052247a545109c17b708" } diff --git a/packages/cashc/src/Errors.ts b/packages/cashc/src/Errors.ts index 511d75214..fd3551216 100644 --- a/packages/cashc/src/Errors.ts +++ b/packages/cashc/src/Errors.ts @@ -1,9 +1,10 @@ import { Type, PrimitiveType } from '@cashscript/utils'; import { IdentifierNode, + ImportNode, FunctionDefinitionNode, + ConstantDefinitionNode, VariableDefinitionNode, - ParameterNode, Node, FunctionCallNode, BinaryOpNode, @@ -11,18 +12,17 @@ import { TimeOpNode, CastNode, AssignNode, - BranchNode, ArrayNode, TupleIndexOpNode, RequireNode, InstantiationNode, StatementNode, ContractNode, - ExpressionNode, SliceNode, IntLiteralNode, + TupleAssignmentNode, } from './ast/AST.js'; -import { Symbol, SymbolType } from './ast/SymbolTable.js'; +import { SymbolType } from './ast/SymbolTable.js'; import { Location } from './ast/Location.js'; import { BinaryOperator } from './ast/Operator.js'; @@ -69,33 +69,32 @@ export class InvalidSymbolTypeError extends CashScriptError { public node: IdentifierNode, public expected: SymbolType, ) { - super(node, `Found symbol ${node.name} with type ${node.definition?.symbolType} where type ${expected} was expected`); + super(node, `Found symbol ${node.name} with type ${node.symbol?.symbolType} where type ${expected} was expected`); } } -export class RedefinitionError extends CashScriptError { } - -export class FunctionRedefinitionError extends RedefinitionError { +export class RedefinitionError extends CashScriptError { constructor( - public node: FunctionDefinitionNode, + public node: Node, + public identifier: string, ) { - super(node, `Redefinition of function ${node.name}`); + super(node, `Redefinition of identifier ${identifier}`); } } -export class VariableRedefinitionError extends RedefinitionError { - constructor( - public node: VariableDefinitionNode | ParameterNode, - ) { - super(node, `Redefinition of variable ${node.name}`); +export class MissingContractError extends Error { + constructor() { + super('Source file does not contain a contract definition'); + this.name = this.constructor.name; } } -export class UnusedVariableError extends CashScriptError { +export class ImportResolutionError extends CashScriptError { constructor( - public symbol: Symbol, + public node: ImportNode, + message: string, ) { - super(symbol.definition as Node, `Unused variable ${symbol.name}`); + super(node, message); } } @@ -123,6 +122,43 @@ export class FinalRequireStatementError extends CashScriptError { } } +export class UnusedFunctionReturnError extends CashScriptError { + constructor( + public node: FunctionCallNode, + ) { + super(node, `Return value of ${node.identifier.name} must be used; only void functions may be called as a statement`); + } +} + +export class MissingReturnError extends CashScriptError { + constructor( + public node: Node, + ) { + super(node, 'A value-returning function must end with a return statement'); + } +} + +export class MisplacedReturnError extends CashScriptError { + constructor( + public node: StatementNode, + ) { + super(node, 'A return statement is only allowed as the final statement of a function body'); + } +} + +export class UnsafeFunctionOperationError extends CashScriptError { + constructor( + node: Node, + operation: string, + ) { + super( + node, + `'${operation}' cannot be used inside a user-defined function. Use it directly in a ` + + 'contract function instead, or pass the resulting value into the function as a parameter', + ); + } +} + export class TypeError extends CashScriptError { constructor( node: Node, @@ -134,6 +170,16 @@ export class TypeError extends CashScriptError { } } +export class ReturnTypeError extends TypeError { + constructor( + node: Node, + actual?: Type, + expected?: Type, + ) { + super(node, actual, expected, `Cannot return type '${actual}' from a function with return type '${expected}'`); + } +} + export class InvalidParameterTypeError extends TypeError { constructor( node: FunctionCallNode | RequireNode | InstantiationNode, @@ -201,38 +247,58 @@ export class CastTypeError extends TypeError { export class AssignTypeError extends TypeError { constructor( - node: AssignNode | VariableDefinitionNode, + node: AssignNode | VariableDefinitionNode | ConstantDefinitionNode, ) { const expected = node instanceof AssignNode ? node.identifier.type : node.type; - super(node, node.expression.type, expected, `Type '${node.expression.type}' can not be assigned to variable of type '${expected}'`); + const expression = node instanceof ConstantDefinitionNode ? node.value : node.expression; + const target = node instanceof ConstantDefinitionNode ? `constant '${node.name}'` : 'variable'; + super(node, expression.type, expected, `Type '${expression.type}' can not be assigned to ${target} of type '${expected}'`); } } -export class TupleAssignmentError extends CashScriptError { +export class InvalidConstantExpressionError extends CashScriptError { constructor( - node: ExpressionNode, + public node: Node, ) { - super(node, 'Expression must return a tuple to use destructuring'); + super( + node, + 'Global constant definitions only support literals, references to other constants, ' + + 'integer arithmetic and concatenation', + ); } } -export class ConstantConditionError extends CashScriptError { +export class DivisionByZeroError extends CashScriptError { constructor( - node: BranchNode | RequireNode, - res: boolean, + public node: BinaryOpNode, ) { - super(node, `Condition always evaluates to ${res}`); + super(node, 'Division by zero'); + } +} + +export class DuplicateTupleTargetError extends CashScriptError { + constructor( + node: TupleAssignmentNode, + name: string, + ) { + super(node, `Duplicate target '${name}' in tuple destructuring`); } } export class ConstantModificationError extends CashScriptError { + constructor(node: VariableDefinitionNode | ConstantDefinitionNode); + constructor(node: Node, name: string); constructor( - node: VariableDefinitionNode, + node: Node, + name?: string, ) { - super(node, `Tried to modify immutable variable '${node.name}'`); + const constantName = name ?? (node as VariableDefinitionNode | ConstantDefinitionNode).name; + super(node, `Tried to modify immutable variable '${constantName}'`); } } +export class InvalidModifierError extends CashScriptError { } + export class ArrayElementError extends CashScriptError { constructor( node: ArrayNode, @@ -273,10 +339,12 @@ export class BitshiftBitcountNegativeError extends CashScriptError { export class VersionError extends Error { constructor( - actual: string, - constraint: string, + readonly actual: string, + readonly constraint: string, + readonly sourceFile?: string, ) { - const message = `cashc version ${actual} does not satisfy version constraint ${constraint}`; + const provenance = sourceFile ? ` (from pragma in imported file '${sourceFile}')` : ''; + const message = `cashc version ${actual} does not satisfy version constraint ${constraint}${provenance}`; super(message); this.name = this.constructor.name; diff --git a/packages/cashc/src/Warnings.ts b/packages/cashc/src/Warnings.ts new file mode 100644 index 000000000..68eb7e147 --- /dev/null +++ b/packages/cashc/src/Warnings.ts @@ -0,0 +1,41 @@ +import { IdentifierNode, Node } from './ast/AST.js'; +import { Symbol } from './ast/SymbolTable.js'; + +export class CashScriptWarning { + name: string; + message: string; + + constructor( + public node: Node, + message: string, + ) { + if (node.location) { + message += ` at ${node.location.start}`; + } + + this.name = this.constructor.name; + this.message = message; + } +} + +export class UnusedVariableWarning extends CashScriptWarning { + constructor( + public symbol: Symbol, + ) { + super(symbol.definition as Node, `Unused variable '${symbol.name}'`); + } +} + +export class UnusedAssignmentWarning extends CashScriptWarning { + constructor( + public identifier: IdentifierNode, + ) { + super(identifier, `Value assigned to '${identifier.name}' is never read`); + } +} + +export type CashScriptWarningListener = (warning: CashScriptWarning) => void; + +export const defaultWarningListener: CashScriptWarningListener = (warning) => { + console.warn(`Warning: ${warning.message}`); +}; diff --git a/packages/cashc/src/artifact/Artifact.ts b/packages/cashc/src/artifact/Artifact.ts index ff09b9f32..159aa0ef3 100644 --- a/packages/cashc/src/artifact/Artifact.ts +++ b/packages/cashc/src/artifact/Artifact.ts @@ -13,6 +13,7 @@ export function generateArtifact( fingerprint: string, ): Artifact { const { contract } = ast; + if (!contract) throw new Error('Internal error: cannot generate an artifact for a source file with no contract'); const constructorInputs = contract.parameters .map((parameter) => ({ name: parameter.name, type: parameter.type.toString() })); diff --git a/packages/cashc/src/ast/AST.ts b/packages/cashc/src/ast/AST.ts index 3b4a869a0..864db595b 100644 --- a/packages/cashc/src/ast/AST.ts +++ b/packages/cashc/src/ast/AST.ts @@ -1,5 +1,5 @@ import { Type, PrimitiveType, BytesType } from '@cashscript/utils'; -import { TimeOp } from './Globals.js'; +import { Modifier, TimeOp } from './Globals.js'; import AstVisitor from './AstVisitor.js'; import { BinaryOperator, NullaryOperator, UnaryOperator } from './Operator.js'; import { Location } from './Location.js'; @@ -21,9 +21,21 @@ export interface Typed { type: Type; } +export enum FunctionKind { + CONTRACT = 'contract', + GLOBAL = 'global', +} + export class SourceFileNode extends Node { + // The source file's scope: the table of global definitions (shared definitions carry a VM function-table id). + symbolTable?: SymbolTable; + constructor( - public contract: ContractNode, + public contract?: ContractNode, + public functions: FunctionDefinitionNode[] = [], + public constants: ConstantDefinitionNode[] = [], + public imports: ImportNode[] = [], + public pragmas: string[] = [], ) { super(); } @@ -33,6 +45,38 @@ export class SourceFileNode extends Node { } } +export class ConstantDefinitionNode extends Node implements Named, Typed { + // Source provenance for debugging. Set on imported constants, left undefined for constants in the contract's own file. + sourceCode?: string; + sourceFile?: string; + + modifiers = [Modifier.CONSTANT]; + + constructor( + public type: Type, + public name: string, + public value: ExpressionNode, + ) { + super(); + } + + accept(visitor: AstVisitor): T { + return visitor.visitConstantDefinition(this); + } +} + +export class ImportNode extends Node { + constructor( + public path: string, + ) { + super(); + } + + accept(visitor: AstVisitor): T { + return visitor.visitImport(this); + } +} + export class ContractNode extends Node implements Named { symbolTable?: SymbolTable; @@ -53,10 +97,19 @@ export class FunctionDefinitionNode extends Node implements Named { symbolTable?: SymbolTable; opRolls: Map = new Map(); + // Set when this is the synthetic zero-argument function used to lower a global constant. + constant?: ConstantDefinitionNode; + + // Source provenance for debugging. Set on imported functions, left undefined for functions in the contract's own file. + sourceCode?: string; + sourceFile?: string; + constructor( + public kind: FunctionKind, public name: string, public parameters: ParameterNode[], public body: BlockNode, + public returnTypes?: Type[], ) { super(); } @@ -67,8 +120,11 @@ export class FunctionDefinitionNode extends Node implements Named { } export class ParameterNode extends Node implements Named, Typed { + symbol?: Symbol; + constructor( public type: Type, + public modifiers: Modifier[], public name: string, ) { super(); @@ -79,14 +135,18 @@ export class ParameterNode extends Node implements Named, Typed { } } +export type DefinitionNode = VariableDefinitionNode | ConstantDefinitionNode | FunctionDefinitionNode | ParameterNode; + export abstract class StatementNode extends Node { } export abstract class ControlStatementNode extends StatementNode { } export abstract class NonControlStatementNode extends StatementNode { } export class VariableDefinitionNode extends NonControlStatementNode implements Named, Typed { + symbol?: Symbol; + constructor( public type: Type, - public modifier: string[], + public modifiers: Modifier[], public name: string, public expression: ExpressionNode, ) { @@ -98,11 +158,16 @@ export class VariableDefinitionNode extends NonControlStatementNode implements N } } +export interface TupleAssignmentTarget { + identifier: IdentifierNode; + type?: Type; + modifiers: Modifier[]; + isReassignment?: boolean; +} + export class TupleAssignmentNode extends NonControlStatementNode { constructor( - // TODO: Use an IdentifierNode instead of a custom type - public left: { name: string, type: Type }, - public right: { name: string, type: Type }, + public targets: TupleAssignmentTarget[], public tuple: ExpressionNode, ) { super(); @@ -168,6 +233,30 @@ export class ConsoleStatementNode extends NonControlStatementNode { } } +export class FunctionCallStatementNode extends NonControlStatementNode { + constructor( + public functionCall: FunctionCallNode, + ) { + super(); + } + + accept(visitor: AstVisitor): T { + return visitor.visitFunctionCallStatement(this); + } +} + +export class ReturnNode extends NonControlStatementNode { + constructor( + public expressions: ExpressionNode[], + ) { + super(); + } + + accept(visitor: AstVisitor): T { + return visitor.visitReturn(this); + } +} + export class BranchNode extends ControlStatementNode { constructor( public condition: ExpressionNode, @@ -362,7 +451,7 @@ export class ArrayNode extends ExpressionNode { } export class IdentifierNode extends ExpressionNode implements Named { - definition?: Symbol; + symbol?: Symbol; constructor( public name: string, @@ -378,6 +467,9 @@ export class IdentifierNode extends ExpressionNode implements Named { export abstract class LiteralNode extends ExpressionNode { public value: T; + // Set when this is the synthetic literal node used to represent a global constant + constant?: ConstantDefinitionNode; + toString(): string { return `${this.value}`; } diff --git a/packages/cashc/src/ast/AstBuilder.ts b/packages/cashc/src/ast/AstBuilder.ts index fc671b030..4362b8ab5 100644 --- a/packages/cashc/src/ast/AstBuilder.ts +++ b/packages/cashc/src/ast/AstBuilder.ts @@ -1,14 +1,16 @@ import { ParseTree, ParseTreeVisitor } from 'antlr4'; import { hexToBin } from '@bitauth/libauth'; -import { parseType } from '@cashscript/utils'; -import semver from 'semver'; +import { parseType, Type } from '@cashscript/utils'; import { Node, SourceFileNode, + ImportNode, ContractNode, ParameterNode, VariableDefinitionNode, FunctionDefinitionNode, + ConstantDefinitionNode, + FunctionKind, AssignNode, IdentifierNode, BranchNode, @@ -28,11 +30,13 @@ import { ArrayNode, TupleIndexOpNode, RequireNode, + ReturnNode, InstantiationNode, TupleAssignmentNode, NullaryOpNode, ConsoleStatementNode, ConsoleParameterNode, + FunctionCallStatementNode, SliceNode, DoWhileNode, WhileNode, @@ -40,8 +44,13 @@ import { } from './AST.js'; import { UnaryOperator, BinaryOperator, NullaryOperator } from './Operator.js'; import type { + ImportDirectiveContext, ContractDefinitionContext, - FunctionDefinitionContext, + ContractFunctionDefinitionContext, + GlobalFunctionDefinitionContext, + ConstantDefinitionContext, + ReturnStatementContext, + FunctionCallStatementContext, VariableDefinitionContext, TupleAssignmentContext, ParameterContext, @@ -83,12 +92,12 @@ import type { import CashScriptVisitor from '../grammar/CashScriptVisitor.js'; import { Location } from './Location.js'; import { + Modifier, NumberUnit, TimeOp, } from './Globals.js'; import { getPragmaName, PragmaName, getVersionOpFromCtx } from './Pragma.js'; -import { version } from '../index.js'; -import { ParseError, VersionError } from '../Errors.js'; +import { ParseError } from '../Errors.js'; export default class AstBuilder extends ParseTreeVisitor @@ -106,46 +115,88 @@ export default class AstBuilder } visitSourceFile(ctx: SourceFileContext): SourceFileNode { - ctx.pragmaDirective_list().forEach((pragma) => { - this.processPragma(pragma); + const pragmas = ctx.pragmaDirective_list().flatMap((pragma) => this.extractVersionConstraints(pragma)); + + const imports = ctx.importDirective_list().map((directive) => this.visit(directive) as ImportNode); + + const functions: FunctionDefinitionNode[] = []; + const constants: ConstantDefinitionNode[] = []; + let contract: ContractNode | undefined; + + ctx.topLevelDefinition_list().forEach((def) => { + if (def.globalFunctionDefinition()) { + functions.push(this.visit(def.globalFunctionDefinition()) as FunctionDefinitionNode); + } else if (def.constantDefinition()) { + constants.push(this.visit(def.constantDefinition()) as ConstantDefinitionNode); + } else if (def.contractDefinition()) { + if (contract) { + throw new ParseError('A source file may define at most one contract', Location.fromCtx(def.contractDefinition())); + } + contract = this.visit(def.contractDefinition()) as ContractNode; + } }); - const contract = this.visit(ctx.contractDefinition()) as ContractNode; - const sourceFileNode = new SourceFileNode(contract); + const sourceFileNode = new SourceFileNode(contract, functions, constants, imports, pragmas); sourceFileNode.location = Location.fromCtx(ctx); return sourceFileNode; } - processPragma(ctx: PragmaDirectiveContext): void { + visitConstantDefinition(ctx: ConstantDefinitionContext): ConstantDefinitionNode { + const type = parseType(ctx.typeName().getText()); + const name = ctx.Identifier().getText(); + const value = this.visit(ctx.expression()) as ExpressionNode; + const constantDefinition = new ConstantDefinitionNode(type, name, value); + constantDefinition.location = Location.fromCtx(ctx); + return constantDefinition; + } + + visitImportDirective(ctx: ImportDirectiveContext): ImportNode { + const raw = ctx.StringLiteral().getText(); + const importNode = new ImportNode(raw.substring(1, raw.length - 1)); + importNode.location = Location.fromCtx(ctx); + return importNode; + } + + extractVersionConstraints(ctx: PragmaDirectiveContext): string[] { const pragmaName = getPragmaName(ctx.pragmaName().getText()); if (pragmaName !== PragmaName.CASHSCRIPT) throw new Error(); // Shouldn't happen - // Strip any -beta tags - const actualVersion = version.replace(/-.*/g, ''); - - ctx.pragmaValue().versionConstraint_list().forEach((constraint) => { + return ctx.pragmaValue().versionConstraint_list().map((constraint) => { const op = getVersionOpFromCtx(constraint.versionOperator()); - const versionConstraint = `${op}${constraint.VersionLiteral().getText()}`; - if (!semver.satisfies(actualVersion, versionConstraint)) { - throw new VersionError(actualVersion, versionConstraint); - } + return `${op}${constraint.VersionLiteral().getText()}`; }); } visitContractDefinition(ctx: ContractDefinitionContext): ContractNode { const name = ctx.Identifier().getText(); const parameters = ctx.parameterList().parameter_list().map((p) => this.visit(p) as ParameterNode); - const functions = ctx.functionDefinition_list().map((f) => this.visit(f) as FunctionDefinitionNode); + const functions = ctx.contractFunctionDefinition_list() + .map((f) => this.visit(f) as FunctionDefinitionNode); const contract = new ContractNode(name, parameters, functions); contract.location = Location.fromCtx(ctx); return contract; } - visitFunctionDefinition(ctx: FunctionDefinitionContext): FunctionDefinitionNode { + visitContractFunctionDefinition(ctx: ContractFunctionDefinitionContext): FunctionDefinitionNode { + return this.buildFunctionDefinition(ctx, FunctionKind.CONTRACT); + } + + visitGlobalFunctionDefinition(ctx: GlobalFunctionDefinitionContext): FunctionDefinitionNode { + const returnTypes = ctx.typeName_list().length > 0 + ? ctx.typeName_list().map((typeName) => parseType(typeName.getText())) + : undefined; + return this.buildFunctionDefinition(ctx, FunctionKind.GLOBAL, returnTypes); + } + + private buildFunctionDefinition( + ctx: ContractFunctionDefinitionContext | GlobalFunctionDefinitionContext, + kind: FunctionKind, + returnTypes?: Type[], + ): FunctionDefinitionNode { const name = ctx.Identifier().getText(); const parameters = ctx.parameterList().parameter_list().map((p) => this.visit(p) as ParameterNode); - const body = this.visit(ctx.functionBody()); - const functionDefinition = new FunctionDefinitionNode(name, parameters, body); + const body = this.visit(ctx.functionBody()) as BlockNode; + const functionDefinition = new FunctionDefinitionNode(kind, name, parameters, body, returnTypes); functionDefinition.location = Location.fromCtx(ctx); return functionDefinition; } @@ -159,8 +210,9 @@ export default class AstBuilder visitParameter(ctx: ParameterContext): ParameterNode { const type = parseType(ctx.typeName().getText()); + const modifiers = ctx.modifier_list().map((modifier) => modifier.getText() as Modifier); const name = ctx.Identifier().getText(); - const parameter = new ParameterNode(type, name); + const parameter = new ParameterNode(type, modifiers, name); parameter.location = Location.fromCtx(ctx); return parameter; } @@ -187,7 +239,7 @@ export default class AstBuilder visitVariableDefinition(ctx: VariableDefinitionContext): VariableDefinitionNode { const type = parseType(ctx.typeName().getText()); - const modifiers = ctx.modifier_list().map((modifier) => modifier.getText()); + const modifiers = ctx.modifier_list().map((modifier) => modifier.getText() as Modifier); const name = ctx.Identifier().getText(); const expression = this.visit(ctx.expression()); const variableDefinition = new VariableDefinitionNode(type, modifiers, name, expression); @@ -197,13 +249,18 @@ export default class AstBuilder visitTupleAssignment(ctx: TupleAssignmentContext): TupleAssignmentNode { const expression = this.visit(ctx.expression()); - const names = ctx.Identifier_list(); - const types = ctx.typeName_list(); - const [var1, var2] = names.map((name, i) => ({ - name: name.getText(), - type: parseType(types[i].getText()), - })); - const tupleAssignment = new TupleAssignmentNode(var1, var2, expression); + const targets = ctx.tupleTarget_list().map((target) => { + const typeName = target.typeName(); + const identifier = new IdentifierNode(target.Identifier().getText()); + identifier.location = Location.fromToken(target.Identifier().symbol); + return { + identifier, + type: typeName ? parseType(typeName.getText()) : undefined, + modifiers: target.modifier_list().map((modifier) => modifier.getText() as Modifier), + isReassignment: !typeName, + }; + }); + const tupleAssignment = new TupleAssignmentNode(targets, expression); tupleAssignment.location = Location.fromCtx(ctx); return tupleAssignment; } @@ -260,6 +317,20 @@ export default class AstBuilder return require; } + visitReturnStatement(ctx: ReturnStatementContext): ReturnNode { + const expressions = ctx.expression_list().map((expression) => this.visit(expression) as ExpressionNode); + const returnNode = new ReturnNode(expressions); + returnNode.location = Location.fromCtx(ctx); + return returnNode; + } + + visitFunctionCallStatement(ctx: FunctionCallStatementContext): FunctionCallStatementNode { + const functionCall = this.visit(ctx.functionCall()) as FunctionCallNode; + const node = new FunctionCallStatementNode(functionCall); + node.location = Location.fromCtx(ctx); + return node; + } + visitIfStatement(ctx: IfStatementContext): BranchNode { const condition = this.visit(ctx.expression()); const ifBlock = this.visit(ctx._ifBlock) as StatementNode; @@ -482,7 +553,8 @@ export default class AstBuilder throw new ParseError('Date should be in format `YYYY-MM-DDThh:mm:ss`', Location.fromCtx(ctx)); } - const timestamp = Math.round(Date.parse(stringValue) / 1000); + // Date literals should always be in UTC, so we append 'Z' to the string + const timestamp = Math.round(Date.parse(`${stringValue}Z`) / 1000); if (Number.isNaN(timestamp)) { throw new ParseError(`Incorrectly formatted date "${stringValue}"`, Location.fromCtx(ctx)); diff --git a/packages/cashc/src/ast/AstTraversal.ts b/packages/cashc/src/ast/AstTraversal.ts index ba73d204a..0dca299eb 100644 --- a/packages/cashc/src/ast/AstTraversal.ts +++ b/packages/cashc/src/ast/AstTraversal.ts @@ -1,10 +1,12 @@ import { Node, SourceFileNode, + ImportNode, ContractNode, ParameterNode, VariableDefinitionNode, FunctionDefinitionNode, + ConstantDefinitionNode, AssignNode, IdentifierNode, BranchNode, @@ -22,11 +24,14 @@ import { ArrayNode, TupleIndexOpNode, RequireNode, + ReturnNode, InstantiationNode, TupleAssignmentNode, NullaryOpNode, ConsoleStatementNode, ConsoleParameterNode, + ExpressionNode, + FunctionCallStatementNode, SliceNode, DoWhileNode, WhileNode, @@ -36,7 +41,13 @@ import AstVisitor from './AstVisitor.js'; export default class AstTraversal extends AstVisitor { visitSourceFile(node: SourceFileNode): Node { - node.contract = this.visit(node.contract) as ContractNode; + node.constants = this.visitList(node.constants) as ConstantDefinitionNode[]; + node.functions = this.visitList(node.functions) as FunctionDefinitionNode[]; + node.contract = this.visitOptional(node.contract) as ContractNode | undefined; + return node; + } + + visitImport(node: ImportNode): Node { return node; } @@ -52,6 +63,11 @@ export default class AstTraversal extends AstVisitor { return node; } + visitConstantDefinition(node: ConstantDefinitionNode): Node { + node.value = this.visit(node.value) as ExpressionNode; + return node; + } + visitParameter(node: ParameterNode): Node { return node; } @@ -82,11 +98,21 @@ export default class AstTraversal extends AstVisitor { return node; } + visitReturn(node: ReturnNode): Node { + node.expressions = this.visitList(node.expressions); + return node; + } + visitConsoleStatement(node: ConsoleStatementNode): Node { node.parameters = this.visitList(node.parameters) as ConsoleParameterNode[]; return node; } + visitFunctionCallStatement(node: FunctionCallStatementNode): Node { + node.functionCall = this.visit(node.functionCall) as FunctionCallNode; + return node; + } + visitBranch(node: BranchNode): Node { node.condition = this.visit(node.condition); node.ifBlock = this.visit(node.ifBlock) as StatementNode; diff --git a/packages/cashc/src/ast/AstVisitor.ts b/packages/cashc/src/ast/AstVisitor.ts index 1c131bf35..2682bfa5d 100644 --- a/packages/cashc/src/ast/AstVisitor.ts +++ b/packages/cashc/src/ast/AstVisitor.ts @@ -1,10 +1,12 @@ import { Node, SourceFileNode, + ImportNode, ContractNode, ParameterNode, VariableDefinitionNode, FunctionDefinitionNode, + ConstantDefinitionNode, AssignNode, IdentifierNode, BranchNode, @@ -21,10 +23,12 @@ import { ArrayNode, TupleIndexOpNode, RequireNode, + ReturnNode, InstantiationNode, TupleAssignmentNode, NullaryOpNode, ConsoleStatementNode, + FunctionCallStatementNode, SliceNode, DoWhileNode, WhileNode, @@ -33,15 +37,19 @@ import { export default abstract class AstVisitor { abstract visitSourceFile(node: SourceFileNode): T; + abstract visitImport(node: ImportNode): T; abstract visitContract(node: ContractNode): T; abstract visitFunctionDefinition(node: FunctionDefinitionNode): T; + abstract visitConstantDefinition(node: ConstantDefinitionNode): T; abstract visitParameter(node: ParameterNode): T; abstract visitVariableDefinition(node: VariableDefinitionNode): T; abstract visitTupleAssignment(node: TupleAssignmentNode): T; abstract visitAssign(node: AssignNode): T; abstract visitTimeOp(node: TimeOpNode): T; abstract visitRequire(node: RequireNode): T; + abstract visitReturn(node: ReturnNode): T; abstract visitConsoleStatement(node: ConsoleStatementNode): T; + abstract visitFunctionCallStatement(node: FunctionCallStatementNode): T; abstract visitBranch(node: BranchNode): T; abstract visitDoWhile(node: DoWhileNode): T; abstract visitWhile(node: WhileNode): T; diff --git a/packages/cashc/src/ast/Globals.ts b/packages/cashc/src/ast/Globals.ts index cc1273d77..70c7b4290 100644 --- a/packages/cashc/src/ast/Globals.ts +++ b/packages/cashc/src/ast/Globals.ts @@ -1,4 +1,4 @@ -import { PrimitiveType, ArrayType, BytesType } from '@cashscript/utils'; +import { PrimitiveType, ArrayType, BytesType, Op } from '@cashscript/utils'; import { SymbolTable, Symbol } from './SymbolTable.js'; export const NumberUnit: { [index: string]: number } = { @@ -44,6 +44,7 @@ export enum Class { export enum Modifier { CONSTANT = 'constant', + UNUSED = 'unused', } export const GLOBAL_SYMBOL_TABLE = new SymbolTable(); @@ -63,47 +64,88 @@ GLOBAL_SYMBOL_TABLE.set( ); // Global functions + +// abs(int) -> int +GLOBAL_SYMBOL_TABLE.set( + Symbol.builtinFunction(GlobalFunction.ABS, PrimitiveType.INT, [PrimitiveType.INT], [Op.OP_ABS]), +); + +// min(int, int) -> int GLOBAL_SYMBOL_TABLE.set( - Symbol.function(GlobalFunction.ABS, PrimitiveType.INT, [PrimitiveType.INT]), + Symbol.builtinFunction(GlobalFunction.MIN, PrimitiveType.INT, [PrimitiveType.INT, PrimitiveType.INT], [Op.OP_MIN]), ); + +// max(int, int) -> int GLOBAL_SYMBOL_TABLE.set( - Symbol.function(GlobalFunction.MIN, PrimitiveType.INT, [PrimitiveType.INT, PrimitiveType.INT]), + Symbol.builtinFunction(GlobalFunction.MAX, PrimitiveType.INT, [PrimitiveType.INT, PrimitiveType.INT], [Op.OP_MAX]), ); + +// within(int, int, int) -> bool GLOBAL_SYMBOL_TABLE.set( - Symbol.function(GlobalFunction.MAX, PrimitiveType.INT, [PrimitiveType.INT, PrimitiveType.INT]), + Symbol.builtinFunction( + GlobalFunction.WITHIN, PrimitiveType.BOOL, + [PrimitiveType.INT, PrimitiveType.INT, PrimitiveType.INT], + [Op.OP_WITHIN], + ), ); -GLOBAL_SYMBOL_TABLE.set(Symbol.function( - GlobalFunction.WITHIN, PrimitiveType.BOOL, - [PrimitiveType.INT, PrimitiveType.INT, PrimitiveType.INT], -)); + +// ripemd160(any) -> bytes20 GLOBAL_SYMBOL_TABLE.set( - Symbol.function(GlobalFunction.RIPEMD160, new BytesType(20), [PrimitiveType.ANY]), + Symbol.builtinFunction(GlobalFunction.RIPEMD160, new BytesType(20), [PrimitiveType.ANY], [Op.OP_RIPEMD160]), ); + +// sha1(any) -> bytes20 GLOBAL_SYMBOL_TABLE.set( - Symbol.function(GlobalFunction.SHA1, new BytesType(20), [PrimitiveType.ANY]), + Symbol.builtinFunction(GlobalFunction.SHA1, new BytesType(20), [PrimitiveType.ANY], [Op.OP_SHA1]), ); + +// sha256(any) -> bytes32 GLOBAL_SYMBOL_TABLE.set( - Symbol.function(GlobalFunction.SHA256, new BytesType(32), [PrimitiveType.ANY]), + Symbol.builtinFunction(GlobalFunction.SHA256, new BytesType(32), [PrimitiveType.ANY], [Op.OP_SHA256]), ); + +// hash160(any) -> bytes20 GLOBAL_SYMBOL_TABLE.set( - Symbol.function(GlobalFunction.HASH160, new BytesType(20), [PrimitiveType.ANY]), + Symbol.builtinFunction(GlobalFunction.HASH160, new BytesType(20), [PrimitiveType.ANY], [Op.OP_HASH160]), ); + +// hash256(any) -> bytes32 +GLOBAL_SYMBOL_TABLE.set( + Symbol.builtinFunction(GlobalFunction.HASH256, new BytesType(32), [PrimitiveType.ANY], [Op.OP_HASH256]), +); + +// checkSig(sig, pubkey) -> bool +GLOBAL_SYMBOL_TABLE.set( + Symbol.builtinFunction( + GlobalFunction.CHECKSIG, PrimitiveType.BOOL, + [PrimitiveType.SIG, PrimitiveType.PUBKEY], + [Op.OP_CHECKSIG], + ), +); + +// checkMultiSig(sig[], pubkey[]) -> bool +GLOBAL_SYMBOL_TABLE.set( + Symbol.builtinFunction( + GlobalFunction.CHECKMULTISIG, PrimitiveType.BOOL, + [new ArrayType(PrimitiveType.SIG), new ArrayType(PrimitiveType.PUBKEY)], + [Op.OP_CHECKMULTISIG], + ), +); + +// checkDataSig(datasig, bytes, pubkey) -> bool +GLOBAL_SYMBOL_TABLE.set( + Symbol.builtinFunction( + GlobalFunction.CHECKDATASIG, PrimitiveType.BOOL, + [PrimitiveType.DATASIG, new BytesType(), PrimitiveType.PUBKEY], + [Op.OP_CHECKDATASIG], + ), +); + +// toPaddedBytes(int, int) -> bytes GLOBAL_SYMBOL_TABLE.set( - Symbol.function(GlobalFunction.HASH256, new BytesType(32), [PrimitiveType.ANY]), + Symbol.builtinFunction( + GlobalFunction.TO_PADDED_BYTES, new BytesType(), + [PrimitiveType.INT, PrimitiveType.INT], + [Op.OP_NUM2BIN], + ), ); -GLOBAL_SYMBOL_TABLE.set(Symbol.function( - GlobalFunction.CHECKSIG, PrimitiveType.BOOL, - [PrimitiveType.SIG, PrimitiveType.PUBKEY], -)); -GLOBAL_SYMBOL_TABLE.set(Symbol.function( - GlobalFunction.CHECKMULTISIG, PrimitiveType.BOOL, - [new ArrayType(PrimitiveType.SIG), new ArrayType(PrimitiveType.PUBKEY)], -)); -GLOBAL_SYMBOL_TABLE.set(Symbol.function( - GlobalFunction.CHECKDATASIG, PrimitiveType.BOOL, - [PrimitiveType.DATASIG, new BytesType(), PrimitiveType.PUBKEY], -)); -GLOBAL_SYMBOL_TABLE.set(Symbol.function( - GlobalFunction.TO_PADDED_BYTES, new BytesType(), - [PrimitiveType.INT, PrimitiveType.INT], -)); diff --git a/packages/cashc/src/ast/Pragma.ts b/packages/cashc/src/ast/Pragma.ts index be0c9b19d..2db187823 100644 --- a/packages/cashc/src/ast/Pragma.ts +++ b/packages/cashc/src/ast/Pragma.ts @@ -1,4 +1,7 @@ +import semver from 'semver'; import type { VersionOperatorContext } from '../grammar/CashScriptParser.js'; +import { version } from '../index.js'; +import { VersionError } from '../Errors.js'; export enum PragmaName { CASHSCRIPT = 'cashscript', @@ -21,3 +24,14 @@ export function getPragmaName(name: string): PragmaName { export function getVersionOpFromCtx(ctx?: VersionOperatorContext): VersionOp { return (ctx ? ctx.getText() : '='); } + +export function checkVersionConstraints(constraints: string[], sourceFile?: string): void { + // Strip any prerelease tags + const actualVersion = version.replace(/-.*/g, ''); + + constraints.forEach((constraint) => { + if (!semver.satisfies(actualVersion, constraint)) { + throw new VersionError(actualVersion, constraint, sourceFile); + } + }); +} diff --git a/packages/cashc/src/ast/SymbolTable.ts b/packages/cashc/src/ast/SymbolTable.ts index fcfada947..32bf1b163 100644 --- a/packages/cashc/src/ast/SymbolTable.ts +++ b/packages/cashc/src/ast/SymbolTable.ts @@ -1,31 +1,82 @@ -import { Type } from '@cashscript/utils'; +import { DebugFrame, Type, Script, Op, encodeInt } from '@cashscript/utils'; import { VariableDefinitionNode, ParameterNode, + FunctionDefinitionNode, + ConstantDefinitionNode, IdentifierNode, - Node, + DefinitionNode, } from './AST.js'; +import { Modifier } from './Globals.js'; +import { functionReturnType } from '../utils.js'; + +export enum ReferenceKind { + READ = 'read', + WRITE = 'write', +} + +export interface Reference { + kind: ReferenceKind; + node: IdentifierNode; +} export class Symbol { - references: IdentifierNode[] = []; + references: Reference[] = []; + inlinedFrame?: DebugFrame; + private constructor( public name: string, public type: Type, public symbolType: SymbolType, - public definition?: Node, + public definition?: DefinitionNode, public parameters?: Type[], - ) {} + public bytecode?: Script, + public functionId?: number, + ) { } + + hasModifier(modifier: Modifier): boolean { + return this.definition !== undefined + && !(this.definition instanceof FunctionDefinitionNode) + && this.definition.modifiers.includes(modifier); + } + + getReferences(kind: ReferenceKind): Reference[] { + return this.references.filter((reference) => reference.kind === kind); + } + + isUnused(): boolean { + return this.getReferences(ReferenceKind.READ).length === 0; + } static variable(node: VariableDefinitionNode | ParameterNode): Symbol { return new Symbol(node.name, node.type, SymbolType.VARIABLE, node); } + static constant(node: ConstantDefinitionNode): Symbol { + return new Symbol(node.name, node.type, SymbolType.VARIABLE, node); + } + static global(name: string, type: Type): Symbol { return new Symbol(name, type, SymbolType.VARIABLE); } - static function(name: string, type: Type, parameters: Type[]): Symbol { - return new Symbol(name, type, SymbolType.FUNCTION, undefined, parameters); + static builtinFunction(name: string, returnType: Type, parameters: Type[], bytecode: Script): Symbol { + return new Symbol(name, returnType, SymbolType.FUNCTION, undefined, parameters, bytecode); + } + + static userFunction(node: FunctionDefinitionNode): Symbol { + const parameterTypes = node.parameters.map((parameter) => parameter.type); + return new Symbol(node.name, functionReturnType(node.returnTypes), SymbolType.FUNCTION, node, parameterTypes); + } + + setFunctionId(functionId: number): void { + this.functionId = functionId; + this.bytecode = [encodeInt(BigInt(functionId)), Op.OP_INVOKE]; + } + + setInlinedBytecode(bytecode: Script, frame: DebugFrame): void { + this.bytecode = bytecode; + this.inlinedFrame = frame; } static class(name: string, type: Type, parameters: Type[]): Symbol { @@ -52,7 +103,7 @@ export class SymbolTable { constructor( public parent?: SymbolTable, - ) {} + ) { } set(symbol: Symbol): void { this.symbols.set(symbol.name, symbol); @@ -70,9 +121,10 @@ export class SymbolTable { return `[${Array.from(this.symbols).map((e) => e[1])}]`; } - unusedSymbols(): Symbol[] { + getUnmarkedUnusedSymbols(): Symbol[] { return Array.from(this.symbols) .map((e) => e[1]) - .filter((s) => s.references.length === 0); + .filter((s) => !s.hasModifier(Modifier.UNUSED)) + .filter((s) => s.isUnused()); } } diff --git a/packages/cashc/src/compiler.ts b/packages/cashc/src/compiler.ts index 46c24927a..dcfcf5fac 100644 --- a/packages/cashc/src/compiler.ts +++ b/packages/cashc/src/compiler.ts @@ -1,4 +1,3 @@ -import { CharStream, CommonTokenStream } from 'antlr4'; import { binToHex } from '@bitauth/libauth'; import { Artifact, @@ -6,24 +5,37 @@ import { computeBytecodeFingerprintWithConstructorArgs, generateSourceMap, generateSourceTags, + generateInlineRanges, optimiseBytecode, - optimiseBytecodeOld, - scriptToAsm, scriptToBytecode, sourceMapToLocationData, } from '@cashscript/utils'; import fs, { PathLike } from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; import { generateArtifact } from './artifact/Artifact.js'; import { Ast } from './ast/AST.js'; -import AstBuilder from './ast/AstBuilder.js'; -import { ThrowingErrorListener, CashScriptErrorListener, ForwardingErrorListener } from './ast/error-listeners.js'; +import { checkVersionConstraints } from './ast/Pragma.js'; +import { CashScriptErrorListener } from './ast/error-listeners.js'; +import { MissingContractError } from './Errors.js'; +import { CashScriptWarningListener, defaultWarningListener } from './Warnings.js'; +import { parseCode } from './parser.js'; +import { + createDiskResolver, + createMemoryResolver, + ImportResolver, + resolveDependencies, +} from './dependency-resolution.js'; import GenerateTargetTraversal from './generation/GenerateTargetTraversal.js'; -import CashScriptLexer from './grammar/CashScriptLexer.js'; -import CashScriptParser from './grammar/CashScriptParser.js'; +import { FoldGlobalConstantsTraversal } from './semantic/FoldGlobalConstantsTraversal.js'; import SymbolTableTraversal from './semantic/SymbolTableTraversal.js'; +import UnusedCodeWarningsTraversal from './semantic/UnusedCodeTraversal.js'; import TypeCheckTraversal from './semantic/TypeCheckTraversal.js'; import EnsureFinalRequireTraversal from './semantic/EnsureFinalRequireTraversal.js'; +import EnsureFunctionsSafeTraversal from './semantic/EnsureFunctionsSafeTraversal.js'; import InjectLocktimeGuardTraversal from './semantic/InjectLocktimeGuardTraversal.js'; +import DeadCodeEliminationTraversal from './semantic/DeadCodeEliminationTraversal.js'; +import { LowerGlobalConstantsTraversal } from './semantic/LowerGlobalConstantsTraversal.js'; export const DEFAULT_COMPILER_OPTIONS: CompilerOptions = { enforceFunctionParameterTypes: true, @@ -32,6 +44,11 @@ export const DEFAULT_COMPILER_OPTIONS: CompilerOptions = { export interface CompileOptions extends CompilerOptions { errorListener?: CashScriptErrorListener; + warningListener?: CashScriptWarningListener; +} + +export interface CompileStringOptions extends CompileOptions { + files?: Record; } /** @@ -40,96 +57,115 @@ export interface CompileOptions extends CompilerOptions { * @param code - The CashScript source code to compile. * @param compilerOptions - Optional compiler options that override the defaults. * @returns The compiled CashScript artifact, including ABI, bytecode and debug information. - * @throws If the source code contains a syntax, semantic, or type error. + * @throws If the source code contains a syntax, semantic, or type error, or an import cannot be resolved. + * @remarks Compilation warnings (e.g. unused variables) are passed to the `warningListener` compiler + * option, or printed with `console.warn` when no listener is provided. + */ +export const compileString: (code: string, compilerOptions?: CompileStringOptions) => Artifact = + compileStringInternal; + +/** + * Read a `.cash` source file from disk and compile it to an `Artifact`. + * + * Import directives are resolved from the filesystem: file paths (starting with './', '../' or '/') + * relative to the importing file's directory, and package imports (e.g. 'pkg/math.cash') + * from node_modules directories, walking up from the importing file's directory. + * + * @param codeFile - The path to the `.cash` source file. + * @param compilerOptions - Optional compiler options that override the defaults. + * @returns The compiled CashScript artifact. + * @throws If the file cannot be read, or if the source contains a compilation error. */ -export function compileString(code: string, compilerOptions: CompileOptions = {}): Artifact { - const { errorListener, ...artifactCompilerOptions } = compilerOptions; +export const compileFile: (codeFile: PathLike, compilerOptions?: CompileOptions) => Artifact = + compileFileInternal; + + +export interface InternalCompilerOptions extends CompilerOptions { + disableInlining?: boolean; +} + +export function compileStringInternal( + code: string, + compilerOptions: CompileStringOptions & InternalCompilerOptions = {}, +): Artifact { + const { files, ...remainingOptions } = compilerOptions; + const resolver = createMemoryResolver(files ?? {}); + return compileCode(code, resolver, remainingOptions); +} + +export function compileFileInternal( + codeFile: PathLike, + compilerOptions: CompileOptions & InternalCompilerOptions = {}, +): Artifact { + const filePath = codeFile instanceof URL ? fileURLToPath(codeFile) : codeFile.toString(); + const code = fs.readFileSync(filePath, { encoding: 'utf-8' }); + const resolver = createDiskResolver(path.dirname(filePath)); + return compileCode(code, resolver, compilerOptions); +} + +function compileCode( + code: string, + resolver: ImportResolver, + compilerOptions: CompileOptions & InternalCompilerOptions, +): Artifact { + const { errorListener, warningListener, disableInlining, ...artifactCompilerOptions } = compilerOptions; const mergedCompilerOptions = { ...DEFAULT_COMPILER_OPTIONS, ...artifactCompilerOptions }; // Lexing + parsing let ast = parseCode(code, errorListener); + checkVersionConstraints(ast.pragmas); + + ast = resolveDependencies(ast, resolver, errorListener) as Ast; + if (!ast.contract) throw new MissingContractError(); + + const constructorParamLength = ast.contract.parameters.length; // Semantic analysis + ast = ast.accept(new FoldGlobalConstantsTraversal()) as Ast; + ast = ast.accept(new SymbolTableTraversal()) as Ast; + ast = ast.accept(new UnusedCodeWarningsTraversal(warningListener ?? defaultWarningListener)) as Ast; + ast = ast.accept(new TypeCheckTraversal()) as Ast; + ast = ast.accept(new EnsureFunctionsSafeTraversal()) as Ast; ast = ast.accept(new EnsureFinalRequireTraversal()) as Ast; if (mergedCompilerOptions.enforceLocktimeGuard) { ast = ast.accept(new InjectLocktimeGuardTraversal()) as Ast; } + // Turn global constants into synthetic zero-argument functions, so they can share reachability analysis, + // inlining, and VM function-ID assignment with user-defined functions + ast = ast.accept(new LowerGlobalConstantsTraversal()) as Ast; + + // Dead-code elimination: drop global functions that are never invoked before code generation + ast = ast.accept(new DeadCodeEliminationTraversal()) as Ast; + // Code generation - const traversal = new GenerateTargetTraversal(mergedCompilerOptions); + const traversal = new GenerateTargetTraversal({ ...mergedCompilerOptions, disableInlining }); ast = ast.accept(traversal) as Ast; - const constructorParamLength = ast.contract.parameters.length; - // Bytecode optimisation - const optimisedBytecodeOld = optimiseBytecodeOld(traversal.output); const optimisationResult = optimiseBytecode( traversal.output, sourceMapToLocationData(traversal.sourceMap), traversal.consoleLogs, traversal.requires, traversal.sourceTags, + traversal.inlineRanges, constructorParamLength, ); - if (scriptToAsm(optimisedBytecodeOld) !== scriptToAsm(optimisationResult.script)) { - console.error(scriptToAsm(optimisedBytecodeOld)); - console.error(scriptToAsm(optimisationResult.script)); - throw new Error('New bytecode optimisation is not backwards compatible, please report this issue to the CashScript team'); - } - - // Attach debug information - const sourceTags = generateSourceTags(optimisationResult.sourceTags); const debug = { bytecode: binToHex(scriptToBytecode(optimisationResult.script)), sourceMap: generateSourceMap(optimisationResult.locationData), logs: optimisationResult.logs, requires: optimisationResult.requires, - ...(sourceTags ? { sourceTags } : {}), + sourceTags: generateSourceTags(optimisationResult.sourceTags) || undefined, + functions: traversal.frames.length > 0 ? traversal.frames : undefined, + inlineRanges: generateInlineRanges(optimisationResult.inlineRanges) || undefined, }; const fingerprint = computeBytecodeFingerprintWithConstructorArgs(optimisationResult.script, constructorParamLength); return generateArtifact(ast, optimisationResult.script, code, debug, mergedCompilerOptions, fingerprint); } - -/** - * Read a `.cash` source file from disk and compile it to an `Artifact`. - * - * @param codeFile - The path to the `.cash` source file. - * @param compilerOptions - Optional compiler options that override the defaults. - * @returns The compiled CashScript artifact. - * @throws If the file cannot be read, or if the source contains a compilation error. - */ -export function compileFile(codeFile: PathLike, compilerOptions: CompileOptions = {}): Artifact { - const code = fs.readFileSync(codeFile, { encoding: 'utf-8' }); - return compileString(code, compilerOptions); -} - -export function parseCode( - code: string, - errorListener: CashScriptErrorListener = ThrowingErrorListener.INSTANCE, -): Ast { - const syntaxErrorListener = new ForwardingErrorListener(errorListener); - - // Lexing (throwing on errors) - const inputStream = new CharStream(code); - const lexer = new CashScriptLexer(inputStream); - lexer.removeErrorListeners(); - lexer.addErrorListener(syntaxErrorListener); - const tokenStream = new CommonTokenStream(lexer); - - // Parsing (throwing on errors) - const parser = new CashScriptParser(tokenStream); - parser.removeErrorListeners(); - parser.addErrorListener(syntaxErrorListener); - const parseTree = parser.sourceFile(); - syntaxErrorListener.throwFirstError(); - - // AST building - const ast = new AstBuilder(parseTree).build() as Ast; - - return ast; -} diff --git a/packages/cashc/src/dependency-resolution.ts b/packages/cashc/src/dependency-resolution.ts new file mode 100644 index 000000000..aaa621fc4 --- /dev/null +++ b/packages/cashc/src/dependency-resolution.ts @@ -0,0 +1,189 @@ +import fs from 'fs'; +import path from 'path'; +import { + SourceFileNode, + FunctionDefinitionNode, + ConstantDefinitionNode, + ImportNode, +} from './ast/AST.js'; +import { checkVersionConstraints } from './ast/Pragma.js'; +import type { CashScriptErrorListener } from './ast/error-listeners.js'; +import { ImportResolutionError } from './Errors.js'; +import { parseCode } from './parser.js'; + +// A minimal virtual filesystem used to resolve import directives. Canonical paths are opaque keys: +// absolute filesystem paths for the disk resolver, normalised POSIX paths relative to the main +// source for the in-memory resolver. `resolve` returns undefined when a package import +// cannot be located. +export interface ImportResolver { + rootDir: string; + resolve(fromDir: string, importPath: string): string | undefined; + read(canonicalPath: string): string | undefined; + dirname(canonicalPath: string): string; + sourceName(canonicalPath: string): string; +} + +export function createDiskResolver(rootDir: string): ImportResolver { + return { + rootDir, + resolve: (fromDir, importPath) => (isPackageImport(importPath) + ? resolveFromNodeModules(fromDir, importPath) + : path.resolve(fromDir, importPath)), + read: (canonicalPath) => { + try { + return fs.readFileSync(canonicalPath, { encoding: 'utf-8' }); + } catch { + return undefined; + } + }, + dirname: (canonicalPath) => path.dirname(canonicalPath), + sourceName: (canonicalPath) => { + const relativePath = getPackageImportPath(canonicalPath) ?? path.relative(rootDir, canonicalPath); + return relativePath.split(path.sep).join(path.posix.sep); + }, + }; +} + +export function createMemoryResolver(files: Record): ImportResolver { + // Normalise keys so that './utils.cash' and 'utils.cash' address the same file + const normalisedFiles = Object.fromEntries( + Object.entries(files).map(([filePath, source]) => [path.posix.normalize(filePath), source]), + ); + + return { + rootDir: '.', + // Package imports are looked up verbatim ('pkg/math.cash'), regardless of the importing file + resolve: (fromDir, importPath) => (isPackageImport(importPath) + ? path.posix.normalize(importPath) + : path.posix.normalize(path.posix.join(fromDir, importPath))), + read: (canonicalPath) => normalisedFiles[canonicalPath], + dirname: (canonicalPath) => path.posix.dirname(canonicalPath), + sourceName: (canonicalPath) => canonicalPath, + }; +} + +export function resolveDependencies( + ast: SourceFileNode, + resolver: ImportResolver | undefined, + errorListener?: CashScriptErrorListener, +): SourceFileNode { + if (ast.imports.length === 0) return ast; + + if (resolver === undefined) { + throw new ImportResolutionError( + ast.imports[0], + 'Cannot resolve imports when compiling from a string, pass in the imported sources using the "files" option or compile from the filesystem using compileFile', + ); + } + + const importedDefinitions = collectImports(ast.imports, resolver, errorListener); + ast.functions = [...importedDefinitions.functions, ...ast.functions]; + ast.constants = [...importedDefinitions.constants, ...ast.constants]; + ast.imports = []; + + return ast; +} + +interface ImportedDefinitions { + functions: FunctionDefinitionNode[]; + constants: ConstantDefinitionNode[]; +} + +// Depth-first walk of the import graph, returning every global definition it reaches. `visitedPaths` +// de-duplicates files by canonical path so a diamond's shared leaf is read once, while `activePaths` +// tracks the files currently being resolved so cyclic imports are rejected. +function collectImports( + imports: ImportNode[], + resolver: ImportResolver, + errorListener?: CashScriptErrorListener, +): ImportedDefinitions { + const visitedPaths = new Set(); + const activePaths = new Set(); + + const collect = (currentImports: ImportNode[], currentDir: string): ImportedDefinitions[] => + currentImports.flatMap((importNode) => { + const canonicalPath = resolver.resolve(currentDir, importNode.path); + if (canonicalPath === undefined) { + throw new ImportResolutionError( + importNode, + `Could not find imported file '${importNode.path}' in any node_modules directory`, + ); + } + if (activePaths.has(canonicalPath)) { + throw new ImportResolutionError(importNode, `Cyclic import of '${importNode.path}'`); + } + if (visitedPaths.has(canonicalPath)) return []; + visitedPaths.add(canonicalPath); + + const importedSource = resolver.read(canonicalPath); + if (importedSource === undefined) { + throw new ImportResolutionError( + importNode, + `Could not read imported file '${importNode.path}' (resolved to '${canonicalPath}')`, + ); + } + + const importedAst = parseCode(importedSource, errorListener); + checkVersionConstraints(importedAst.pragmas, resolver.sourceName(canonicalPath)); + + // Record source provenance so debug frames can attribute to the imported file + importedAst.functions.forEach((func) => { + func.sourceCode = importedSource; + func.sourceFile = resolver.sourceName(canonicalPath); + }); + importedAst.constants.forEach((constant) => { + constant.sourceCode = importedSource; + constant.sourceFile = resolver.sourceName(canonicalPath); + }); + + activePaths.add(canonicalPath); + const transitiveDefinitions = collect(importedAst.imports, resolver.dirname(canonicalPath)); + activePaths.delete(canonicalPath); + + return [ + ...transitiveDefinitions, + { functions: importedAst.functions, constants: importedAst.constants }, + ]; + }); + + const collected = collect(imports, resolver.rootDir); + return { + functions: collected.flatMap((definitions) => definitions.functions), + constants: collected.flatMap((definitions) => definitions.constants), + }; +} + + +function isPackageImport(importPath: string): boolean { + return !importPath.startsWith('./') && !importPath.startsWith('../') && !importPath.startsWith('/'); +} + +// Walk up from the importing file's directory looking for node_modules/, so +// contract libraries can be installed and imported as regular npm packages +function resolveFromNodeModules(fromDir: string, importPath: string): string | undefined { + const currentDir = path.resolve(fromDir); + const nodeModulesDir = path.join(currentDir, 'node_modules'); + const candidate = path.join(nodeModulesDir, importPath); + + if (isValidCandidate(nodeModulesDir, candidate)) return candidate; + + const parentDir = path.dirname(currentDir); + if (parentDir === currentDir) return undefined; + + return resolveFromNodeModules(parentDir, importPath); +} + +function isValidCandidate(nodeModulesDir: string, candidate: string): boolean { + // The prefix check stops '..' segments in a specifier from escaping the node_modules directory + if (!candidate.startsWith(nodeModulesDir + path.sep)) return false; + const stats = fs.statSync(candidate, { throwIfNoEntry: false }); + return stats?.isFile() ?? false; +} + +// A file inside node_modules is named by its package import path ('pkg/math.cash') +function getPackageImportPath(canonicalPath: string): string | undefined { + const nodeModulesSegment = `${path.sep}node_modules${path.sep}`; + const nodeModulesIndex = canonicalPath.lastIndexOf(nodeModulesSegment); + if (nodeModulesIndex === -1) return undefined; + return canonicalPath.slice(nodeModulesIndex + nodeModulesSegment.length); +} diff --git a/packages/cashc/src/generation/GenerateTargetTraversal.ts b/packages/cashc/src/generation/GenerateTargetTraversal.ts index ef0a0096b..ddc25171a 100644 --- a/packages/cashc/src/generation/GenerateTargetTraversal.ts +++ b/packages/cashc/src/generation/GenerateTargetTraversal.ts @@ -1,4 +1,4 @@ -import { hexToBin } from '@bitauth/libauth'; +import { binToHex, hexToBin } from '@bitauth/libauth'; import { asmToScript, encodeBool, @@ -9,15 +9,22 @@ import { PrimitiveType, Script, scriptToAsm, + scriptToBytecode, + optimiseBytecode, + OptimiseBytecodeResult, generateSourceMap, + generateSourceTags, + generateInlineRanges, + parseSourceTags, FullLocationData, + DebugFrame, LogEntry, RequireStatement, PositionHint, SingleLocationData, StackItem, BytesType, - CompilerOptions, + TupleType, SourceTagEntry, SourceTagKind, } from '@cashscript/utils'; @@ -26,6 +33,7 @@ import { ParameterNode, VariableDefinitionNode, FunctionDefinitionNode, + FunctionKind, AssignNode, IdentifierNode, BranchNode, @@ -60,14 +68,22 @@ import { BinaryOperator } from '../ast/Operator.js'; import { compileBinaryOp, compileCast, - compileGlobalFunction, compileNullaryOp, compileTimeOp, compileUnaryOp, } from './utils.js'; import { isNumericType } from '../utils.js'; +import { collectFunctionCalls, isRecursive, shouldInline } from './inlining.js'; +import type { InternalCompilerOptions } from '../compiler.js'; + +interface InlineRange { + startIp: number; + endIp: number; + frame: DebugFrame; + line: number; +} -export default class GenerateTargetTraversalWithLocation extends AstTraversal { +export default class GenerateTargetTraversal extends AstTraversal { private locationData: FullLocationData = []; // detailed location data needed for sourcemap creation sourceMap: string; output: Script = []; @@ -75,13 +91,15 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal { consoleLogs: LogEntry[] = []; requires: RequireStatement[] = []; sourceTags: SourceTagEntry[] = []; + frames: DebugFrame[] = []; finalStackUsage: Record = {}; private scopeDepth = 0; private currentFunction: FunctionDefinitionNode; private constructorParameterCount: number; + inlineRanges: InlineRange[] = []; - constructor(private compilerOptions: CompilerOptions) { + constructor(private compilerOptions: InternalCompilerOptions) { super(); } @@ -133,7 +151,12 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal { } visitSourceFile(node: SourceFileNode): Node { - node.contract = this.visit(node.contract) as ContractNode; + this.defineGlobalFunctions(node); + + // The contract is guaranteed to exist here (compileString throws MissingContractError otherwise). + node.contract = this.visit(node.contract!) as ContractNode; + + this.mergeInlinedDebugInfo(); // Minimally encode output by going Script -> ASM -> Script this.output = asmToScript(scriptToAsm(this.output)); @@ -143,11 +166,143 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal { return node; } + private mergeInlinedDebugInfo(): void { + this.inlineRanges.forEach(({ startIp, frame, line }) => { + this.requires.push(...frame.requires.map((entry) => ({ + ...entry, + ip: entry.ip + startIp, + line, + }))); + + this.consoleLogs.push(...frame.logs.map((entry) => ({ + ...entry, + ip: entry.ip + startIp, + data: entry.data.map((item) => (typeof item === 'string' ? item : { ...item, ip: item.ip + startIp })), + line, + }))); + + // Source tags use opcode indices rather than ips, which excludes the constructor arguments + const startIndex = startIp - this.constructorParameterCount; + this.sourceTags.push(...parseSourceTags(frame.sourceTags ?? '').map((entry) => ({ + ...entry, + startIndex: entry.startIndex + startIndex, + endIndex: entry.endIndex + startIndex, + }))); + }); + + // Restore overall program order, since the merged entries were appended after this program's own + this.requires.sort((a, b) => a.ip - b.ip); + this.consoleLogs.sort((a, b) => a.ip - b.ip); + this.sourceTags.sort((a, b) => a.startIndex - b.startIndex); + } + + private defineGlobalFunctions(node: SourceFileNode): void { + // Assign function IDs to recursive functions first + const recursiveFunctions = node.functions.filter(isRecursive); + recursiveFunctions.forEach((func, functionId) => { + node.symbolTable!.getFromThis(func.name)!.setFunctionId(functionId); + }); + + const reachableCalls = [node.contract!, ...node.functions].flatMap((n) => collectFunctionCalls(n)); + const definedFunctions: Array<{ func: FunctionDefinitionNode, compiledResult: OptimiseBytecodeResult }> = []; + let nextFunctionId = recursiveFunctions.length; + + node.functions.forEach((func) => { + const symbol = node.symbolTable!.getFromThis(func.name)!; + const compiledResult = this.compileGlobalFunctionBody(func); + + // If the function should be inlined, we ONLY update the symbol + if (shouldInline(symbol, compiledResult, reachableCalls, nextFunctionId, this.compilerOptions)) { + symbol.setInlinedBytecode(compiledResult.script, this.buildDebugFrame(func, compiledResult)); + return; + } + + // If the function ID is not yet assigned, we assign it (skipped for recursive functions which were assigned above) + if (symbol.functionId === undefined) { + symbol.setFunctionId(nextFunctionId); + nextFunctionId += 1; + } + + // Pre-assigned recursive functions and non-inlined functions should be defined + definedFunctions[symbol.functionId!] = { func, compiledResult }; + }); + + // Emit definitions in ID order so debug.functions[n] corresponds to the n-th define site (id n). + definedFunctions.forEach(({ func, compiledResult }, functionId) => { + this.frames.push(this.buildDebugFrame(func, compiledResult, functionId)); + const locationData = { location: func.location, positionHint: PositionHint.START }; + this.emit(scriptToBytecode(compiledResult.script), locationData); // + this.emit(encodeInt(BigInt(functionId)), locationData); // + this.emit(Op.OP_DEFINE, { ...locationData, positionHint: PositionHint.END }); + }); + + // Inlined callables are documented as id-less frames after the defined ones + node.functions + .flatMap((func) => node.symbolTable!.getFromThis(func.name)!.inlinedFrame ?? []) + .forEach((frame) => this.frames.push(frame)); + } + + private compileGlobalFunctionBody(node: FunctionDefinitionNode): OptimiseBytecodeResult { + const bodyTraversal = new GenerateTargetTraversal(this.compilerOptions); + bodyTraversal.currentFunction = node; + bodyTraversal.constructorParameterCount = 0; + + // Seed the stack with parameters in reverse order so the last parameter is on top + // (similar to how builtin functions work) + for (let i = node.parameters.length - 1; i >= 0; i -= 1) { + bodyTraversal.visit(node.parameters[i]); + } + bodyTraversal.dropUnusedParameters(node.parameters); + + bodyTraversal.visit(node.body); + bodyTraversal.cleanGlobalFunctionStack(node); + bodyTraversal.mergeInlinedDebugInfo(); + + const optimisedResult = optimiseBytecode( + bodyTraversal.output, + bodyTraversal.locationData, + bodyTraversal.consoleLogs, + bodyTraversal.requires, + bodyTraversal.sourceTags, + bodyTraversal.inlineRanges, + 0, + ); + + return optimisedResult; + } + + private buildDebugFrame( + node: FunctionDefinitionNode, + optimised: OptimiseBytecodeResult, + functionId?: number, + ): DebugFrame { + return { + id: functionId, + name: node.name, + kind: node.constant ? ('constant' as const) : undefined, + inputs: node.parameters.map((parameter) => ({ name: parameter.name, type: parameter.type.toString() })), + bytecode: binToHex(scriptToBytecode(optimised.script)), + sourceMap: generateSourceMap(optimised.locationData), + sourceTags: generateSourceTags(optimised.sourceTags) || undefined, + source: node.sourceCode, + sourceFile: node.sourceFile, + logs: optimised.logs, + requires: optimised.requires, + inlineRanges: generateInlineRanges(optimised.inlineRanges) || undefined, + }; + } + + cleanGlobalFunctionStack(node: FunctionDefinitionNode): void { + // Drop everything below the return values on top (or the entire frame for a void function) + this.cleanStack(node.body, node.returnTypes?.length ?? 0); + } + visitContract(node: ContractNode): Node { node.parameters = this.visitList(node.parameters) as ParameterNode[]; // Keep track of constructor parameter count for instructor pointer calculation this.constructorParameterCount = node.parameters.length; + this.dropUnusedParameters(node.parameters); if (node.functions.length === 1) { node.functions = this.visitList(node.functions) as FunctionDefinitionNode[]; @@ -196,9 +351,14 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal { } visitFunctionDefinition(node: FunctionDefinitionNode): Node { + if (node.kind !== FunctionKind.CONTRACT) { + throw new Error('Internal error: global functions are compiled via defineGlobalFunctions'); + } + this.currentFunction = node; node.parameters = this.visitList(node.parameters) as ParameterNode[]; + this.dropUnusedParameters(node.parameters); if (this.compilerOptions.enforceFunctionParameterTypes) { this.enforceFunctionParameterTypes(node); @@ -245,17 +405,42 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal { } } - cleanStack(functionBodyNode: Node): void { - // Keep final verification value, OP_NIP the other stack values + // Keep only the top `keepCount` values (a contract function's verification value, or a global + // function's return values), dropping everything below them while preserving their order. + cleanStack(functionBodyNode: Node, keepCount: number = 1): void { + this.dropFromStack(functionBodyNode, keepCount, this.stack.length - keepCount); + } + + private dropFromStack(node: Node, keepCount: number, dropCount: number): void { const tagStartIndex = this.output.length; - const stackSize = this.stack.length; - for (let i = 0; i < stackSize - 1; i += 1) { - this.emit(Op.OP_NIP, { location: functionBodyNode.location, positionHint: PositionHint.END }); - this.nipFromStack(); + const locationData = { location: node.location, positionHint: PositionHint.END }; + + // Note that in case of keepCount = 1, this gets optimised to just OP_NIP, or for keepCount = 0 to OP_DROP + for (let i = 0; i < dropCount; i += 1) { + this.emit(encodeInt(BigInt(keepCount)), locationData); + this.emit(Op.OP_ROLL, locationData); + this.emit(Op.OP_DROP, locationData); + this.removeFromStack(keepCount); } + this.tagScopeCleanup(tagStartIndex); } + private dropUnusedParameters(parameters: ParameterNode[]): void { + parameters + .filter((parameter) => parameter.symbol!.isUnused()) + .sort((a, b) => this.getStackIndex(a.name) - this.getStackIndex(b.name)) + .forEach((parameter) => { + const stackIndex = this.getStackIndex(parameter.name); + const locationData = { location: parameter.location, positionHint: PositionHint.START }; + + this.emit(encodeInt(BigInt(stackIndex)), locationData); + this.emit(Op.OP_ROLL, locationData); + this.emit(Op.OP_DROP, locationData); + this.removeFromStack(stackIndex); + }); + } + enforceFunctionParameterTypes(node: FunctionDefinitionNode): void { node.parameters.forEach((parameter) => this.enforceFunctionParameterType(parameter)); } @@ -296,6 +481,7 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal { } shouldEnforceFunctionParameterType(node: ParameterNode): boolean { + if (node.symbol!.isUnused()) return false; if (node.type === PrimitiveType.BOOL) return true; if (node.type instanceof BytesType && node.type.bound !== undefined) return true; return false; @@ -308,6 +494,13 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal { visitVariableDefinition(node: VariableDefinitionNode): Node { node.expression = this.visit(node.expression); + + if (node.symbol!.isUnused()) { + this.emit(Op.OP_DROP, { location: node.location, positionHint: PositionHint.END }); + this.popFromStack(); + return node; + } + this.popFromStack(); this.pushToStack(node.name); return node; @@ -315,14 +508,66 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal { visitTupleAssignment(node: TupleAssignmentNode): Node { node.tuple = this.visit(node.tuple); - this.popFromStack(2); - this.pushToStack(node.left.name); - this.pushToStack(node.right.name); + + // Outside of a loop/branch, a reassignment is just a rename (the old value stays on the stack) + const scopedReassign = this.scopeDepth > 0 && node.targets.some((target) => target.isReassignment); + if (!scopedReassign) { + this.popFromStack(node.targets.length); + node.targets.forEach((target) => this.pushToStack(target.identifier.name)); + this.dropUnusedTupleTargets(node); + return node; + } + + const locationData = { location: node.location, positionHint: PositionHint.END }; + const parkedDeclarations: string[] = []; + + const reversedTargets = [...node.targets].reverse(); + reversedTargets.forEach((target) => { + // Unused variables are never added the stack, so their defined or re-assigned value is dropped + if (target.identifier.symbol!.isUnused()) { + this.emit(Op.OP_DROP, locationData); + } else if (target.isReassignment) { + this.emitReplace(this.getStackIndex(target.identifier.name), node); + } else { + this.emit(Op.OP_TOALTSTACK, locationData); + parkedDeclarations.push(target.identifier.name); + } + this.popFromStack(); + }); + + parkedDeclarations.reverse().forEach((name) => { + this.emit(Op.OP_FROMALTSTACK, locationData); + this.pushToStack(name); + }); + return node; } + private dropUnusedTupleTargets(node: TupleAssignmentNode): void { + const locationData = { location: node.location, positionHint: PositionHint.END }; + + node.targets + .filter((target) => target.identifier.symbol!.isUnused()) + .sort((a, b) => this.getStackIndex(a.identifier.name) - this.getStackIndex(b.identifier.name)) + .forEach((target) => { + const stackIndex = this.getStackIndex(target.identifier.name); + this.emit(encodeInt(BigInt(stackIndex)), locationData); + this.emit(Op.OP_ROLL, locationData); + this.emit(Op.OP_DROP, locationData); + this.removeFromStack(stackIndex); + }); + } + visitAssign(node: AssignNode): Node { node.expression = this.visit(node.expression); + + // An unused variable never gets added to the stack, so the assigned value is dropped as well + if (node.identifier.symbol!.isUnused()) { + this.emit(Op.OP_DROP, { location: node.location, positionHint: PositionHint.END }); + this.popFromStack(); + return node; + } + if (this.scopeDepth > 0) { this.emitReplace(this.getStackIndex(node.identifier.name), node); this.popFromStack(); @@ -402,7 +647,7 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal { const data = node.parameters.map((parameter: ConsoleParameterNode) => { if (parameter instanceof IdentifierNode) { - const symbol = parameter.definition!; + const symbol = parameter.symbol!; // If the variable is not on the stack, then we add the final stack usage to the console log const stackIndex = this.getStackIndex(parameter.name, true); @@ -551,14 +796,9 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal { }); } + // Drop the values that a scope (a branch or loop body) added on top of the pre-scope stack. removeScopedVariables(depthBeforeScope: number, node: Node): void { - const tagStartIndex = this.output.length; - const dropCount = this.stack.length - depthBeforeScope; - for (let i = 0; i < dropCount; i += 1) { - this.emit(Op.OP_DROP, { location: node.location, positionHint: PositionHint.END }); - this.popFromStack(); - } - this.tagScopeCleanup(tagStartIndex); + this.dropFromStack(node, 0, this.stack.length - depthBeforeScope); } private tagScopeCleanup(tagStartIndex: number): void { @@ -587,14 +827,25 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal { return this.visitMultiSig(node); } + const symbol = node.identifier.symbol!; node.parameters = this.visitList(node.parameters); - this.emit( - compileGlobalFunction(node.identifier.name as GlobalFunction), - { location: node.location, positionHint: PositionHint.END }, - ); + const startIp = this.output.length + this.constructorParameterCount; + const endIp = startIp + symbol.bytecode!.length - 1; + + this.emit(symbol.bytecode!, { location: node.location, positionHint: PositionHint.END }); + + if (symbol.inlinedFrame) { + this.inlineRanges.push({ startIp, endIp, frame: symbol.inlinedFrame, line: node.location.start.line }); + } + this.popFromStack(node.parameters.length); - this.pushToStack('(value)'); + + // The call leaves one value per declared return type (none for a void function); a multi-return + // function's values (a TupleType) are subsequently bound by visitTupleAssignment. + const returnValueCount = symbol.type === PrimitiveType.VOID ? 0 + : symbol.type instanceof TupleType ? symbol.type.elementTypes.length : 1; + for (let i = 0; i < returnValueCount; i += 1) this.pushToStack('(value)'); return node; } @@ -774,7 +1025,7 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal { // If the final use is inside an if-statement, we still OP_PICK it // We do this so that there's no difference in stack depths between execution paths if (this.isOpRoll(node)) { - const symbol = node.definition!; + const symbol = node.symbol!; this.finalStackUsage[node.name] = { type: symbol.type.toString(), stackIndex, diff --git a/packages/cashc/src/generation/inlining.ts b/packages/cashc/src/generation/inlining.ts new file mode 100644 index 000000000..a25e40bcb --- /dev/null +++ b/packages/cashc/src/generation/inlining.ts @@ -0,0 +1,78 @@ +import { + encodeInt, + Op, + OptimiseBytecodeResult, + Script, + scriptToBytecode, +} from '@cashscript/utils'; +import { FunctionCallNode, FunctionDefinitionNode, Node } from '../ast/AST.js'; +import AstTraversal from '../ast/AstTraversal.js'; +import { Symbol } from '../ast/SymbolTable.js'; +import type { InternalCompilerOptions } from '../compiler.js'; + +export const shouldInline = ( + symbol: Symbol, + optimisedResult: OptimiseBytecodeResult, + reachableCalls: FunctionCallNode[], + nextFunctionId: number, + compilerOptions: InternalCompilerOptions, +): boolean => { + if (compilerOptions.disableInlining) return false; + if (symbol.functionId !== undefined) return false; + + const callCount = reachableCalls.filter((call) => call.identifier.symbol === symbol).length; + return isWorthInlining(nextFunctionId, optimisedResult.script, callCount); +}; + +function isWorthInlining(candidateFunctionId: number, bodyScript: Script, callCount: number): boolean { + const bodyBytecode = scriptToBytecode(bodyScript); + const functionId = encodeInt(BigInt(candidateFunctionId)); + + const defineBytes = scriptToBytecode([bodyBytecode, functionId, Op.OP_DEFINE]).length; + const invokeBytes = scriptToBytecode([functionId, Op.OP_INVOKE]).length; + + const bytesWhenDefined = defineBytes + callCount * invokeBytes; + const bytesWhenInlined = callCount * bodyBytecode.length; + + return bytesWhenInlined <= bytesWhenDefined; +} + +class FunctionCallCollector extends AstTraversal { + functionCalls: FunctionCallNode[] = []; + + visitFunctionCall(node: FunctionCallNode): Node { + this.functionCalls.push(node); + node.parameters = this.visitList(node.parameters); + return node; + } +} + +export function collectFunctionCalls(node: Node): FunctionCallNode[] { + const collector = new FunctionCallCollector(); + collector.visit(node); + return collector.functionCalls; +} + +export function isRecursive(func: FunctionDefinitionNode): boolean { + return transitiveCalledFunctions(func).includes(func); +} + +function transitiveCalledFunctions(func: FunctionDefinitionNode): FunctionDefinitionNode[] { + const callees: FunctionDefinitionNode[] = []; + + const visit = (current: FunctionDefinitionNode): void => calledFunctions(current).forEach((callee) => { + if (callees.includes(callee)) return; + callees.push(callee); + visit(callee); + }); + + visit(func); + return callees; +} + +function calledFunctions(func: FunctionDefinitionNode): FunctionDefinitionNode[] { + return collectFunctionCalls(func.body) + .map((call) => call.identifier.symbol?.definition) + .filter((definition): definition is FunctionDefinitionNode => definition instanceof FunctionDefinitionNode) + .filter((definition, index, definitions) => definitions.indexOf(definition) === index); +} diff --git a/packages/cashc/src/generation/utils.ts b/packages/cashc/src/generation/utils.ts index fa017aac5..b6bac2195 100644 --- a/packages/cashc/src/generation/utils.ts +++ b/packages/cashc/src/generation/utils.ts @@ -7,7 +7,7 @@ import { Type, } from '@cashscript/utils'; import { UnaryOperator, BinaryOperator, NullaryOperator } from '../ast/Operator.js'; -import { GlobalFunction, TimeOp } from '../ast/Globals.js'; +import { TimeOp } from '../ast/Globals.js'; export function compileTimeOp(op: TimeOp): Script { const mapping = { @@ -36,26 +36,6 @@ export function compileCast(from: Type, to: Type, isUnsafe: boolean): Script { return []; } -export function compileGlobalFunction(fn: GlobalFunction): Script { - const mapping = { - [GlobalFunction.ABS]: [Op.OP_ABS], - [GlobalFunction.CHECKDATASIG]: [Op.OP_CHECKDATASIG], - [GlobalFunction.CHECKMULTISIG]: [Op.OP_CHECKMULTISIG], - [GlobalFunction.CHECKSIG]: [Op.OP_CHECKSIG], - [GlobalFunction.MAX]: [Op.OP_MAX], - [GlobalFunction.MIN]: [Op.OP_MIN], - [GlobalFunction.RIPEMD160]: [Op.OP_RIPEMD160], - [GlobalFunction.SHA1]: [Op.OP_SHA1], - [GlobalFunction.SHA256]: [Op.OP_SHA256], - [GlobalFunction.HASH160]: [Op.OP_HASH160], - [GlobalFunction.HASH256]: [Op.OP_HASH256], - [GlobalFunction.WITHIN]: [Op.OP_WITHIN], - [GlobalFunction.TO_PADDED_BYTES]: [Op.OP_NUM2BIN], - }; - - return mapping[fn]; -} - export function compileBinaryOp(op: BinaryOperator, numeric: boolean = false): Script { const mapping: { [key in BinaryOperator]: Script } = { [BinaryOperator.MUL]: [Op.OP_MUL], diff --git a/packages/cashc/src/grammar/CashScript.g4 b/packages/cashc/src/grammar/CashScript.g4 index e4ad1d4bf..3ed22c30c 100644 --- a/packages/cashc/src/grammar/CashScript.g4 +++ b/packages/cashc/src/grammar/CashScript.g4 @@ -1,7 +1,7 @@ grammar CashScript; sourceFile - : pragmaDirective* contractDefinition EOF + : pragmaDirective* importDirective* topLevelDefinition* EOF ; pragmaDirective @@ -24,11 +24,29 @@ versionOperator : '^' | '~' | '>=' | '>' | '<' | '<=' | '=' ; +importDirective + : 'import' StringLiteral ';' + ; + +topLevelDefinition + : globalFunctionDefinition + | constantDefinition + | contractDefinition + ; + +globalFunctionDefinition + : 'function' Identifier parameterList ('returns' '(' typeName (',' typeName)* ')')? functionBody + ; + +constantDefinition + : typeName 'constant' Identifier '=' expression ';' + ; + contractDefinition - : 'contract' Identifier parameterList '{' functionDefinition* '}' + : 'contract' Identifier parameterList '{' contractFunctionDefinition* '}' ; -functionDefinition +contractFunctionDefinition : 'function' Identifier parameterList functionBody ; @@ -41,7 +59,7 @@ parameterList ; parameter - : typeName Identifier + : typeName modifier* Identifier ; block @@ -60,7 +78,17 @@ nonControlStatement | assignStatement | timeOpStatement | requireStatement + | functionCallStatement | consoleStatement + | returnStatement + ; + +functionCallStatement + : functionCall + ; + +returnStatement + : 'return' expression (',' expression)* ; controlStatement @@ -73,7 +101,13 @@ variableDefinition ; tupleAssignment - : typeName Identifier ',' typeName Identifier '=' expression + : tupleTarget (',' tupleTarget)+ '=' expression + | '(' tupleTarget (',' tupleTarget)+ ')' '=' expression + ; + +tupleTarget + : typeName modifier* Identifier + | Identifier ; assignStatement @@ -134,7 +168,7 @@ consoleParameterList ; functionCall - : Identifier expressionList // Only built-in functions are accepted + : Identifier expressionList // Built-in global functions and user-defined global functions ; expressionList @@ -171,6 +205,7 @@ expression modifier : 'constant' + | 'unused' ; literal @@ -186,8 +221,8 @@ numberLiteral ; typeName - : PrimitiveType - | BoundedBytes + : PrimitiveType + | BoundedBytes | UnboundedBytes ; diff --git a/packages/cashc/src/grammar/CashScript.interp b/packages/cashc/src/grammar/CashScript.interp index 7368b9dd3..b3c240aa7 100644 --- a/packages/cashc/src/grammar/CashScript.interp +++ b/packages/cashc/src/grammar/CashScript.interp @@ -10,13 +10,17 @@ null '<' '<=' '=' -'contract' -'{' -'}' +'import' 'function' +'returns' '(' ',' ')' +'constant' +'contract' +'{' +'}' +'return' '+=' '-=' '++' @@ -60,7 +64,7 @@ null '|' '&&' '||' -'constant' +'unused' null null null @@ -145,6 +149,10 @@ null null null null +null +null +null +null VersionLiteral BooleanLiteral NumberUnit @@ -173,17 +181,24 @@ pragmaName pragmaValue versionConstraint versionOperator +importDirective +topLevelDefinition +globalFunctionDefinition +constantDefinition contractDefinition -functionDefinition +contractFunctionDefinition functionBody parameterList parameter block statement nonControlStatement +functionCallStatement +returnStatement controlStatement variableDefinition tupleAssignment +tupleTarget assignStatement timeOpStatement requireStatement @@ -208,4 +223,4 @@ typeCast atn: -[4, 1, 81, 433, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 1, 0, 5, 0, 78, 8, 0, 10, 0, 12, 0, 81, 9, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 3, 3, 95, 8, 3, 1, 4, 3, 4, 98, 8, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 5, 6, 109, 8, 6, 10, 6, 12, 6, 112, 9, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 5, 8, 123, 8, 8, 10, 8, 12, 8, 126, 9, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 5, 9, 134, 8, 9, 10, 9, 12, 9, 137, 9, 9, 1, 9, 3, 9, 140, 8, 9, 3, 9, 142, 8, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 5, 11, 151, 8, 11, 10, 11, 12, 11, 154, 9, 11, 1, 11, 1, 11, 3, 11, 158, 8, 11, 1, 12, 1, 12, 1, 12, 1, 12, 3, 12, 164, 8, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 3, 13, 172, 8, 13, 1, 14, 1, 14, 3, 14, 176, 8, 14, 1, 15, 1, 15, 5, 15, 180, 8, 15, 10, 15, 12, 15, 183, 9, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 3, 17, 202, 8, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 3, 18, 211, 8, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 3, 19, 220, 8, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 3, 21, 234, 8, 21, 1, 22, 1, 22, 1, 22, 3, 22, 239, 8, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 3, 26, 267, 8, 26, 1, 27, 1, 27, 1, 28, 1, 28, 3, 28, 273, 8, 28, 1, 29, 1, 29, 1, 29, 1, 29, 5, 29, 279, 8, 29, 10, 29, 12, 29, 282, 9, 29, 1, 29, 3, 29, 285, 8, 29, 3, 29, 287, 8, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 5, 31, 298, 8, 31, 10, 31, 12, 31, 301, 9, 31, 1, 31, 3, 31, 304, 8, 31, 3, 31, 306, 8, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 3, 32, 319, 8, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 5, 32, 345, 8, 32, 10, 32, 12, 32, 348, 9, 32, 1, 32, 3, 32, 351, 8, 32, 3, 32, 353, 8, 32, 1, 32, 1, 32, 1, 32, 1, 32, 3, 32, 359, 8, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 5, 32, 411, 8, 32, 10, 32, 12, 32, 414, 9, 32, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 3, 34, 423, 8, 34, 1, 35, 1, 35, 3, 35, 427, 8, 35, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 0, 1, 64, 38, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 0, 14, 1, 0, 4, 10, 2, 0, 10, 10, 18, 19, 1, 0, 20, 21, 1, 0, 33, 37, 2, 0, 33, 37, 39, 42, 2, 0, 5, 5, 47, 48, 1, 0, 49, 51, 2, 0, 48, 48, 52, 52, 1, 0, 53, 54, 1, 0, 6, 9, 1, 0, 55, 56, 1, 0, 43, 44, 1, 0, 68, 70, 2, 0, 68, 69, 76, 76, 459, 0, 79, 1, 0, 0, 0, 2, 85, 1, 0, 0, 0, 4, 90, 1, 0, 0, 0, 6, 92, 1, 0, 0, 0, 8, 97, 1, 0, 0, 0, 10, 101, 1, 0, 0, 0, 12, 103, 1, 0, 0, 0, 14, 115, 1, 0, 0, 0, 16, 120, 1, 0, 0, 0, 18, 129, 1, 0, 0, 0, 20, 145, 1, 0, 0, 0, 22, 157, 1, 0, 0, 0, 24, 163, 1, 0, 0, 0, 26, 171, 1, 0, 0, 0, 28, 175, 1, 0, 0, 0, 30, 177, 1, 0, 0, 0, 32, 188, 1, 0, 0, 0, 34, 201, 1, 0, 0, 0, 36, 203, 1, 0, 0, 0, 38, 214, 1, 0, 0, 0, 40, 223, 1, 0, 0, 0, 42, 226, 1, 0, 0, 0, 44, 238, 1, 0, 0, 0, 46, 240, 1, 0, 0, 0, 48, 248, 1, 0, 0, 0, 50, 254, 1, 0, 0, 0, 52, 266, 1, 0, 0, 0, 54, 268, 1, 0, 0, 0, 56, 272, 1, 0, 0, 0, 58, 274, 1, 0, 0, 0, 60, 290, 1, 0, 0, 0, 62, 293, 1, 0, 0, 0, 64, 358, 1, 0, 0, 0, 66, 415, 1, 0, 0, 0, 68, 422, 1, 0, 0, 0, 70, 424, 1, 0, 0, 0, 72, 428, 1, 0, 0, 0, 74, 430, 1, 0, 0, 0, 76, 78, 3, 2, 1, 0, 77, 76, 1, 0, 0, 0, 78, 81, 1, 0, 0, 0, 79, 77, 1, 0, 0, 0, 79, 80, 1, 0, 0, 0, 80, 82, 1, 0, 0, 0, 81, 79, 1, 0, 0, 0, 82, 83, 3, 12, 6, 0, 83, 84, 5, 0, 0, 1, 84, 1, 1, 0, 0, 0, 85, 86, 5, 1, 0, 0, 86, 87, 3, 4, 2, 0, 87, 88, 3, 6, 3, 0, 88, 89, 5, 2, 0, 0, 89, 3, 1, 0, 0, 0, 90, 91, 5, 3, 0, 0, 91, 5, 1, 0, 0, 0, 92, 94, 3, 8, 4, 0, 93, 95, 3, 8, 4, 0, 94, 93, 1, 0, 0, 0, 94, 95, 1, 0, 0, 0, 95, 7, 1, 0, 0, 0, 96, 98, 3, 10, 5, 0, 97, 96, 1, 0, 0, 0, 97, 98, 1, 0, 0, 0, 98, 99, 1, 0, 0, 0, 99, 100, 5, 62, 0, 0, 100, 9, 1, 0, 0, 0, 101, 102, 7, 0, 0, 0, 102, 11, 1, 0, 0, 0, 103, 104, 5, 11, 0, 0, 104, 105, 5, 78, 0, 0, 105, 106, 3, 18, 9, 0, 106, 110, 5, 12, 0, 0, 107, 109, 3, 14, 7, 0, 108, 107, 1, 0, 0, 0, 109, 112, 1, 0, 0, 0, 110, 108, 1, 0, 0, 0, 110, 111, 1, 0, 0, 0, 111, 113, 1, 0, 0, 0, 112, 110, 1, 0, 0, 0, 113, 114, 5, 13, 0, 0, 114, 13, 1, 0, 0, 0, 115, 116, 5, 14, 0, 0, 116, 117, 5, 78, 0, 0, 117, 118, 3, 18, 9, 0, 118, 119, 3, 16, 8, 0, 119, 15, 1, 0, 0, 0, 120, 124, 5, 12, 0, 0, 121, 123, 3, 24, 12, 0, 122, 121, 1, 0, 0, 0, 123, 126, 1, 0, 0, 0, 124, 122, 1, 0, 0, 0, 124, 125, 1, 0, 0, 0, 125, 127, 1, 0, 0, 0, 126, 124, 1, 0, 0, 0, 127, 128, 5, 13, 0, 0, 128, 17, 1, 0, 0, 0, 129, 141, 5, 15, 0, 0, 130, 135, 3, 20, 10, 0, 131, 132, 5, 16, 0, 0, 132, 134, 3, 20, 10, 0, 133, 131, 1, 0, 0, 0, 134, 137, 1, 0, 0, 0, 135, 133, 1, 0, 0, 0, 135, 136, 1, 0, 0, 0, 136, 139, 1, 0, 0, 0, 137, 135, 1, 0, 0, 0, 138, 140, 5, 16, 0, 0, 139, 138, 1, 0, 0, 0, 139, 140, 1, 0, 0, 0, 140, 142, 1, 0, 0, 0, 141, 130, 1, 0, 0, 0, 141, 142, 1, 0, 0, 0, 142, 143, 1, 0, 0, 0, 143, 144, 5, 17, 0, 0, 144, 19, 1, 0, 0, 0, 145, 146, 3, 72, 36, 0, 146, 147, 5, 78, 0, 0, 147, 21, 1, 0, 0, 0, 148, 152, 5, 12, 0, 0, 149, 151, 3, 24, 12, 0, 150, 149, 1, 0, 0, 0, 151, 154, 1, 0, 0, 0, 152, 150, 1, 0, 0, 0, 152, 153, 1, 0, 0, 0, 153, 155, 1, 0, 0, 0, 154, 152, 1, 0, 0, 0, 155, 158, 5, 13, 0, 0, 156, 158, 3, 24, 12, 0, 157, 148, 1, 0, 0, 0, 157, 156, 1, 0, 0, 0, 158, 23, 1, 0, 0, 0, 159, 164, 3, 28, 14, 0, 160, 161, 3, 26, 13, 0, 161, 162, 5, 2, 0, 0, 162, 164, 1, 0, 0, 0, 163, 159, 1, 0, 0, 0, 163, 160, 1, 0, 0, 0, 164, 25, 1, 0, 0, 0, 165, 172, 3, 30, 15, 0, 166, 172, 3, 32, 16, 0, 167, 172, 3, 34, 17, 0, 168, 172, 3, 36, 18, 0, 169, 172, 3, 38, 19, 0, 170, 172, 3, 40, 20, 0, 171, 165, 1, 0, 0, 0, 171, 166, 1, 0, 0, 0, 171, 167, 1, 0, 0, 0, 171, 168, 1, 0, 0, 0, 171, 169, 1, 0, 0, 0, 171, 170, 1, 0, 0, 0, 172, 27, 1, 0, 0, 0, 173, 176, 3, 42, 21, 0, 174, 176, 3, 44, 22, 0, 175, 173, 1, 0, 0, 0, 175, 174, 1, 0, 0, 0, 176, 29, 1, 0, 0, 0, 177, 181, 3, 72, 36, 0, 178, 180, 3, 66, 33, 0, 179, 178, 1, 0, 0, 0, 180, 183, 1, 0, 0, 0, 181, 179, 1, 0, 0, 0, 181, 182, 1, 0, 0, 0, 182, 184, 1, 0, 0, 0, 183, 181, 1, 0, 0, 0, 184, 185, 5, 78, 0, 0, 185, 186, 5, 10, 0, 0, 186, 187, 3, 64, 32, 0, 187, 31, 1, 0, 0, 0, 188, 189, 3, 72, 36, 0, 189, 190, 5, 78, 0, 0, 190, 191, 5, 16, 0, 0, 191, 192, 3, 72, 36, 0, 192, 193, 5, 78, 0, 0, 193, 194, 5, 10, 0, 0, 194, 195, 3, 64, 32, 0, 195, 33, 1, 0, 0, 0, 196, 197, 5, 78, 0, 0, 197, 198, 7, 1, 0, 0, 198, 202, 3, 64, 32, 0, 199, 200, 5, 78, 0, 0, 200, 202, 7, 2, 0, 0, 201, 196, 1, 0, 0, 0, 201, 199, 1, 0, 0, 0, 202, 35, 1, 0, 0, 0, 203, 204, 5, 22, 0, 0, 204, 205, 5, 15, 0, 0, 205, 206, 5, 75, 0, 0, 206, 207, 5, 6, 0, 0, 207, 210, 3, 64, 32, 0, 208, 209, 5, 16, 0, 0, 209, 211, 3, 54, 27, 0, 210, 208, 1, 0, 0, 0, 210, 211, 1, 0, 0, 0, 211, 212, 1, 0, 0, 0, 212, 213, 5, 17, 0, 0, 213, 37, 1, 0, 0, 0, 214, 215, 5, 22, 0, 0, 215, 216, 5, 15, 0, 0, 216, 219, 3, 64, 32, 0, 217, 218, 5, 16, 0, 0, 218, 220, 3, 54, 27, 0, 219, 217, 1, 0, 0, 0, 219, 220, 1, 0, 0, 0, 220, 221, 1, 0, 0, 0, 221, 222, 5, 17, 0, 0, 222, 39, 1, 0, 0, 0, 223, 224, 5, 23, 0, 0, 224, 225, 3, 58, 29, 0, 225, 41, 1, 0, 0, 0, 226, 227, 5, 24, 0, 0, 227, 228, 5, 15, 0, 0, 228, 229, 3, 64, 32, 0, 229, 230, 5, 17, 0, 0, 230, 233, 3, 22, 11, 0, 231, 232, 5, 25, 0, 0, 232, 234, 3, 22, 11, 0, 233, 231, 1, 0, 0, 0, 233, 234, 1, 0, 0, 0, 234, 43, 1, 0, 0, 0, 235, 239, 3, 46, 23, 0, 236, 239, 3, 48, 24, 0, 237, 239, 3, 50, 25, 0, 238, 235, 1, 0, 0, 0, 238, 236, 1, 0, 0, 0, 238, 237, 1, 0, 0, 0, 239, 45, 1, 0, 0, 0, 240, 241, 5, 26, 0, 0, 241, 242, 3, 22, 11, 0, 242, 243, 5, 27, 0, 0, 243, 244, 5, 15, 0, 0, 244, 245, 3, 64, 32, 0, 245, 246, 5, 17, 0, 0, 246, 247, 5, 2, 0, 0, 247, 47, 1, 0, 0, 0, 248, 249, 5, 27, 0, 0, 249, 250, 5, 15, 0, 0, 250, 251, 3, 64, 32, 0, 251, 252, 5, 17, 0, 0, 252, 253, 3, 22, 11, 0, 253, 49, 1, 0, 0, 0, 254, 255, 5, 28, 0, 0, 255, 256, 5, 15, 0, 0, 256, 257, 3, 52, 26, 0, 257, 258, 5, 2, 0, 0, 258, 259, 3, 64, 32, 0, 259, 260, 5, 2, 0, 0, 260, 261, 3, 34, 17, 0, 261, 262, 5, 17, 0, 0, 262, 263, 3, 22, 11, 0, 263, 51, 1, 0, 0, 0, 264, 267, 3, 30, 15, 0, 265, 267, 3, 34, 17, 0, 266, 264, 1, 0, 0, 0, 266, 265, 1, 0, 0, 0, 267, 53, 1, 0, 0, 0, 268, 269, 5, 72, 0, 0, 269, 55, 1, 0, 0, 0, 270, 273, 5, 78, 0, 0, 271, 273, 3, 68, 34, 0, 272, 270, 1, 0, 0, 0, 272, 271, 1, 0, 0, 0, 273, 57, 1, 0, 0, 0, 274, 286, 5, 15, 0, 0, 275, 280, 3, 56, 28, 0, 276, 277, 5, 16, 0, 0, 277, 279, 3, 56, 28, 0, 278, 276, 1, 0, 0, 0, 279, 282, 1, 0, 0, 0, 280, 278, 1, 0, 0, 0, 280, 281, 1, 0, 0, 0, 281, 284, 1, 0, 0, 0, 282, 280, 1, 0, 0, 0, 283, 285, 5, 16, 0, 0, 284, 283, 1, 0, 0, 0, 284, 285, 1, 0, 0, 0, 285, 287, 1, 0, 0, 0, 286, 275, 1, 0, 0, 0, 286, 287, 1, 0, 0, 0, 287, 288, 1, 0, 0, 0, 288, 289, 5, 17, 0, 0, 289, 59, 1, 0, 0, 0, 290, 291, 5, 78, 0, 0, 291, 292, 3, 62, 31, 0, 292, 61, 1, 0, 0, 0, 293, 305, 5, 15, 0, 0, 294, 299, 3, 64, 32, 0, 295, 296, 5, 16, 0, 0, 296, 298, 3, 64, 32, 0, 297, 295, 1, 0, 0, 0, 298, 301, 1, 0, 0, 0, 299, 297, 1, 0, 0, 0, 299, 300, 1, 0, 0, 0, 300, 303, 1, 0, 0, 0, 301, 299, 1, 0, 0, 0, 302, 304, 5, 16, 0, 0, 303, 302, 1, 0, 0, 0, 303, 304, 1, 0, 0, 0, 304, 306, 1, 0, 0, 0, 305, 294, 1, 0, 0, 0, 305, 306, 1, 0, 0, 0, 306, 307, 1, 0, 0, 0, 307, 308, 5, 17, 0, 0, 308, 63, 1, 0, 0, 0, 309, 310, 6, 32, -1, 0, 310, 311, 5, 15, 0, 0, 311, 312, 3, 64, 32, 0, 312, 313, 5, 17, 0, 0, 313, 359, 1, 0, 0, 0, 314, 315, 3, 74, 37, 0, 315, 316, 5, 15, 0, 0, 316, 318, 3, 64, 32, 0, 317, 319, 5, 16, 0, 0, 318, 317, 1, 0, 0, 0, 318, 319, 1, 0, 0, 0, 319, 320, 1, 0, 0, 0, 320, 321, 5, 17, 0, 0, 321, 359, 1, 0, 0, 0, 322, 359, 3, 60, 30, 0, 323, 324, 5, 29, 0, 0, 324, 325, 5, 78, 0, 0, 325, 359, 3, 62, 31, 0, 326, 327, 5, 32, 0, 0, 327, 328, 5, 30, 0, 0, 328, 329, 3, 64, 32, 0, 329, 330, 5, 31, 0, 0, 330, 331, 7, 3, 0, 0, 331, 359, 1, 0, 0, 0, 332, 333, 5, 38, 0, 0, 333, 334, 5, 30, 0, 0, 334, 335, 3, 64, 32, 0, 335, 336, 5, 31, 0, 0, 336, 337, 7, 4, 0, 0, 337, 359, 1, 0, 0, 0, 338, 339, 7, 5, 0, 0, 339, 359, 3, 64, 32, 15, 340, 352, 5, 30, 0, 0, 341, 346, 3, 64, 32, 0, 342, 343, 5, 16, 0, 0, 343, 345, 3, 64, 32, 0, 344, 342, 1, 0, 0, 0, 345, 348, 1, 0, 0, 0, 346, 344, 1, 0, 0, 0, 346, 347, 1, 0, 0, 0, 347, 350, 1, 0, 0, 0, 348, 346, 1, 0, 0, 0, 349, 351, 5, 16, 0, 0, 350, 349, 1, 0, 0, 0, 350, 351, 1, 0, 0, 0, 351, 353, 1, 0, 0, 0, 352, 341, 1, 0, 0, 0, 352, 353, 1, 0, 0, 0, 353, 354, 1, 0, 0, 0, 354, 359, 5, 31, 0, 0, 355, 359, 5, 77, 0, 0, 356, 359, 5, 78, 0, 0, 357, 359, 3, 68, 34, 0, 358, 309, 1, 0, 0, 0, 358, 314, 1, 0, 0, 0, 358, 322, 1, 0, 0, 0, 358, 323, 1, 0, 0, 0, 358, 326, 1, 0, 0, 0, 358, 332, 1, 0, 0, 0, 358, 338, 1, 0, 0, 0, 358, 340, 1, 0, 0, 0, 358, 355, 1, 0, 0, 0, 358, 356, 1, 0, 0, 0, 358, 357, 1, 0, 0, 0, 359, 412, 1, 0, 0, 0, 360, 361, 10, 14, 0, 0, 361, 362, 7, 6, 0, 0, 362, 411, 3, 64, 32, 15, 363, 364, 10, 13, 0, 0, 364, 365, 7, 7, 0, 0, 365, 411, 3, 64, 32, 14, 366, 367, 10, 12, 0, 0, 367, 368, 7, 8, 0, 0, 368, 411, 3, 64, 32, 13, 369, 370, 10, 11, 0, 0, 370, 371, 7, 9, 0, 0, 371, 411, 3, 64, 32, 12, 372, 373, 10, 10, 0, 0, 373, 374, 7, 10, 0, 0, 374, 411, 3, 64, 32, 11, 375, 376, 10, 9, 0, 0, 376, 377, 5, 57, 0, 0, 377, 411, 3, 64, 32, 10, 378, 379, 10, 8, 0, 0, 379, 380, 5, 4, 0, 0, 380, 411, 3, 64, 32, 9, 381, 382, 10, 7, 0, 0, 382, 383, 5, 58, 0, 0, 383, 411, 3, 64, 32, 8, 384, 385, 10, 6, 0, 0, 385, 386, 5, 59, 0, 0, 386, 411, 3, 64, 32, 7, 387, 388, 10, 5, 0, 0, 388, 389, 5, 60, 0, 0, 389, 411, 3, 64, 32, 6, 390, 391, 10, 21, 0, 0, 391, 392, 5, 30, 0, 0, 392, 393, 5, 65, 0, 0, 393, 411, 5, 31, 0, 0, 394, 395, 10, 18, 0, 0, 395, 411, 7, 11, 0, 0, 396, 397, 10, 17, 0, 0, 397, 398, 5, 45, 0, 0, 398, 399, 5, 15, 0, 0, 399, 400, 3, 64, 32, 0, 400, 401, 5, 17, 0, 0, 401, 411, 1, 0, 0, 0, 402, 403, 10, 16, 0, 0, 403, 404, 5, 46, 0, 0, 404, 405, 5, 15, 0, 0, 405, 406, 3, 64, 32, 0, 406, 407, 5, 16, 0, 0, 407, 408, 3, 64, 32, 0, 408, 409, 5, 17, 0, 0, 409, 411, 1, 0, 0, 0, 410, 360, 1, 0, 0, 0, 410, 363, 1, 0, 0, 0, 410, 366, 1, 0, 0, 0, 410, 369, 1, 0, 0, 0, 410, 372, 1, 0, 0, 0, 410, 375, 1, 0, 0, 0, 410, 378, 1, 0, 0, 0, 410, 381, 1, 0, 0, 0, 410, 384, 1, 0, 0, 0, 410, 387, 1, 0, 0, 0, 410, 390, 1, 0, 0, 0, 410, 394, 1, 0, 0, 0, 410, 396, 1, 0, 0, 0, 410, 402, 1, 0, 0, 0, 411, 414, 1, 0, 0, 0, 412, 410, 1, 0, 0, 0, 412, 413, 1, 0, 0, 0, 413, 65, 1, 0, 0, 0, 414, 412, 1, 0, 0, 0, 415, 416, 5, 61, 0, 0, 416, 67, 1, 0, 0, 0, 417, 423, 5, 63, 0, 0, 418, 423, 3, 70, 35, 0, 419, 423, 5, 72, 0, 0, 420, 423, 5, 73, 0, 0, 421, 423, 5, 74, 0, 0, 422, 417, 1, 0, 0, 0, 422, 418, 1, 0, 0, 0, 422, 419, 1, 0, 0, 0, 422, 420, 1, 0, 0, 0, 422, 421, 1, 0, 0, 0, 423, 69, 1, 0, 0, 0, 424, 426, 5, 65, 0, 0, 425, 427, 5, 64, 0, 0, 426, 425, 1, 0, 0, 0, 426, 427, 1, 0, 0, 0, 427, 71, 1, 0, 0, 0, 428, 429, 7, 12, 0, 0, 429, 73, 1, 0, 0, 0, 430, 431, 7, 13, 0, 0, 431, 75, 1, 0, 0, 0, 36, 79, 94, 97, 110, 124, 135, 139, 141, 152, 157, 163, 171, 175, 181, 201, 210, 219, 233, 238, 266, 272, 280, 284, 286, 299, 303, 305, 318, 346, 350, 352, 358, 410, 412, 422, 426] \ No newline at end of file +[4, 1, 85, 540, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 1, 0, 5, 0, 92, 8, 0, 10, 0, 12, 0, 95, 9, 0, 1, 0, 5, 0, 98, 8, 0, 10, 0, 12, 0, 101, 9, 0, 1, 0, 5, 0, 104, 8, 0, 10, 0, 12, 0, 107, 9, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 3, 3, 120, 8, 3, 1, 4, 3, 4, 123, 8, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 3, 7, 136, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 5, 8, 146, 8, 8, 10, 8, 12, 8, 149, 9, 8, 1, 8, 1, 8, 3, 8, 153, 8, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 5, 10, 169, 8, 10, 10, 10, 12, 10, 172, 9, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 5, 12, 183, 8, 12, 10, 12, 12, 12, 186, 9, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 5, 13, 194, 8, 13, 10, 13, 12, 13, 197, 9, 13, 1, 13, 3, 13, 200, 8, 13, 3, 13, 202, 8, 13, 1, 13, 1, 13, 1, 14, 1, 14, 5, 14, 208, 8, 14, 10, 14, 12, 14, 211, 9, 14, 1, 14, 1, 14, 1, 15, 1, 15, 5, 15, 217, 8, 15, 10, 15, 12, 15, 220, 9, 15, 1, 15, 1, 15, 3, 15, 224, 8, 15, 1, 16, 1, 16, 1, 16, 1, 16, 3, 16, 230, 8, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 3, 17, 240, 8, 17, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 5, 19, 248, 8, 19, 10, 19, 12, 19, 251, 9, 19, 1, 20, 1, 20, 3, 20, 255, 8, 20, 1, 21, 1, 21, 5, 21, 259, 8, 21, 10, 21, 12, 21, 262, 9, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 4, 22, 271, 8, 22, 11, 22, 12, 22, 272, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 4, 22, 282, 8, 22, 11, 22, 12, 22, 283, 1, 22, 1, 22, 1, 22, 1, 22, 3, 22, 290, 8, 22, 1, 23, 1, 23, 5, 23, 294, 8, 23, 10, 23, 12, 23, 297, 9, 23, 1, 23, 1, 23, 1, 23, 3, 23, 302, 8, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 3, 24, 309, 8, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 3, 25, 318, 8, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 3, 26, 327, 8, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 3, 28, 341, 8, 28, 1, 29, 1, 29, 1, 29, 3, 29, 346, 8, 29, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 3, 33, 374, 8, 33, 1, 34, 1, 34, 1, 35, 1, 35, 3, 35, 380, 8, 35, 1, 36, 1, 36, 1, 36, 1, 36, 5, 36, 386, 8, 36, 10, 36, 12, 36, 389, 9, 36, 1, 36, 3, 36, 392, 8, 36, 3, 36, 394, 8, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 5, 38, 405, 8, 38, 10, 38, 12, 38, 408, 9, 38, 1, 38, 3, 38, 411, 8, 38, 3, 38, 413, 8, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 3, 39, 426, 8, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 5, 39, 452, 8, 39, 10, 39, 12, 39, 455, 9, 39, 1, 39, 3, 39, 458, 8, 39, 3, 39, 460, 8, 39, 1, 39, 1, 39, 1, 39, 1, 39, 3, 39, 466, 8, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 5, 39, 518, 8, 39, 10, 39, 12, 39, 521, 9, 39, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 3, 41, 530, 8, 41, 1, 42, 1, 42, 3, 42, 534, 8, 42, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 0, 1, 78, 45, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 0, 15, 1, 0, 4, 10, 2, 0, 10, 10, 22, 23, 1, 0, 24, 25, 1, 0, 37, 41, 2, 0, 37, 41, 43, 46, 2, 0, 5, 5, 51, 52, 1, 0, 53, 55, 2, 0, 52, 52, 56, 56, 1, 0, 57, 58, 1, 0, 6, 9, 1, 0, 59, 60, 1, 0, 47, 48, 2, 0, 17, 17, 65, 65, 1, 0, 72, 74, 2, 0, 72, 73, 80, 80, 574, 0, 93, 1, 0, 0, 0, 2, 110, 1, 0, 0, 0, 4, 115, 1, 0, 0, 0, 6, 117, 1, 0, 0, 0, 8, 122, 1, 0, 0, 0, 10, 126, 1, 0, 0, 0, 12, 128, 1, 0, 0, 0, 14, 135, 1, 0, 0, 0, 16, 137, 1, 0, 0, 0, 18, 156, 1, 0, 0, 0, 20, 163, 1, 0, 0, 0, 22, 175, 1, 0, 0, 0, 24, 180, 1, 0, 0, 0, 26, 189, 1, 0, 0, 0, 28, 205, 1, 0, 0, 0, 30, 223, 1, 0, 0, 0, 32, 229, 1, 0, 0, 0, 34, 239, 1, 0, 0, 0, 36, 241, 1, 0, 0, 0, 38, 243, 1, 0, 0, 0, 40, 254, 1, 0, 0, 0, 42, 256, 1, 0, 0, 0, 44, 289, 1, 0, 0, 0, 46, 301, 1, 0, 0, 0, 48, 308, 1, 0, 0, 0, 50, 310, 1, 0, 0, 0, 52, 321, 1, 0, 0, 0, 54, 330, 1, 0, 0, 0, 56, 333, 1, 0, 0, 0, 58, 345, 1, 0, 0, 0, 60, 347, 1, 0, 0, 0, 62, 355, 1, 0, 0, 0, 64, 361, 1, 0, 0, 0, 66, 373, 1, 0, 0, 0, 68, 375, 1, 0, 0, 0, 70, 379, 1, 0, 0, 0, 72, 381, 1, 0, 0, 0, 74, 397, 1, 0, 0, 0, 76, 400, 1, 0, 0, 0, 78, 465, 1, 0, 0, 0, 80, 522, 1, 0, 0, 0, 82, 529, 1, 0, 0, 0, 84, 531, 1, 0, 0, 0, 86, 535, 1, 0, 0, 0, 88, 537, 1, 0, 0, 0, 90, 92, 3, 2, 1, 0, 91, 90, 1, 0, 0, 0, 92, 95, 1, 0, 0, 0, 93, 91, 1, 0, 0, 0, 93, 94, 1, 0, 0, 0, 94, 99, 1, 0, 0, 0, 95, 93, 1, 0, 0, 0, 96, 98, 3, 12, 6, 0, 97, 96, 1, 0, 0, 0, 98, 101, 1, 0, 0, 0, 99, 97, 1, 0, 0, 0, 99, 100, 1, 0, 0, 0, 100, 105, 1, 0, 0, 0, 101, 99, 1, 0, 0, 0, 102, 104, 3, 14, 7, 0, 103, 102, 1, 0, 0, 0, 104, 107, 1, 0, 0, 0, 105, 103, 1, 0, 0, 0, 105, 106, 1, 0, 0, 0, 106, 108, 1, 0, 0, 0, 107, 105, 1, 0, 0, 0, 108, 109, 5, 0, 0, 1, 109, 1, 1, 0, 0, 0, 110, 111, 5, 1, 0, 0, 111, 112, 3, 4, 2, 0, 112, 113, 3, 6, 3, 0, 113, 114, 5, 2, 0, 0, 114, 3, 1, 0, 0, 0, 115, 116, 5, 3, 0, 0, 116, 5, 1, 0, 0, 0, 117, 119, 3, 8, 4, 0, 118, 120, 3, 8, 4, 0, 119, 118, 1, 0, 0, 0, 119, 120, 1, 0, 0, 0, 120, 7, 1, 0, 0, 0, 121, 123, 3, 10, 5, 0, 122, 121, 1, 0, 0, 0, 122, 123, 1, 0, 0, 0, 123, 124, 1, 0, 0, 0, 124, 125, 5, 66, 0, 0, 125, 9, 1, 0, 0, 0, 126, 127, 7, 0, 0, 0, 127, 11, 1, 0, 0, 0, 128, 129, 5, 11, 0, 0, 129, 130, 5, 76, 0, 0, 130, 131, 5, 2, 0, 0, 131, 13, 1, 0, 0, 0, 132, 136, 3, 16, 8, 0, 133, 136, 3, 18, 9, 0, 134, 136, 3, 20, 10, 0, 135, 132, 1, 0, 0, 0, 135, 133, 1, 0, 0, 0, 135, 134, 1, 0, 0, 0, 136, 15, 1, 0, 0, 0, 137, 138, 5, 12, 0, 0, 138, 139, 5, 82, 0, 0, 139, 152, 3, 26, 13, 0, 140, 141, 5, 13, 0, 0, 141, 142, 5, 14, 0, 0, 142, 147, 3, 86, 43, 0, 143, 144, 5, 15, 0, 0, 144, 146, 3, 86, 43, 0, 145, 143, 1, 0, 0, 0, 146, 149, 1, 0, 0, 0, 147, 145, 1, 0, 0, 0, 147, 148, 1, 0, 0, 0, 148, 150, 1, 0, 0, 0, 149, 147, 1, 0, 0, 0, 150, 151, 5, 16, 0, 0, 151, 153, 1, 0, 0, 0, 152, 140, 1, 0, 0, 0, 152, 153, 1, 0, 0, 0, 153, 154, 1, 0, 0, 0, 154, 155, 3, 24, 12, 0, 155, 17, 1, 0, 0, 0, 156, 157, 3, 86, 43, 0, 157, 158, 5, 17, 0, 0, 158, 159, 5, 82, 0, 0, 159, 160, 5, 10, 0, 0, 160, 161, 3, 78, 39, 0, 161, 162, 5, 2, 0, 0, 162, 19, 1, 0, 0, 0, 163, 164, 5, 18, 0, 0, 164, 165, 5, 82, 0, 0, 165, 166, 3, 26, 13, 0, 166, 170, 5, 19, 0, 0, 167, 169, 3, 22, 11, 0, 168, 167, 1, 0, 0, 0, 169, 172, 1, 0, 0, 0, 170, 168, 1, 0, 0, 0, 170, 171, 1, 0, 0, 0, 171, 173, 1, 0, 0, 0, 172, 170, 1, 0, 0, 0, 173, 174, 5, 20, 0, 0, 174, 21, 1, 0, 0, 0, 175, 176, 5, 12, 0, 0, 176, 177, 5, 82, 0, 0, 177, 178, 3, 26, 13, 0, 178, 179, 3, 24, 12, 0, 179, 23, 1, 0, 0, 0, 180, 184, 5, 19, 0, 0, 181, 183, 3, 32, 16, 0, 182, 181, 1, 0, 0, 0, 183, 186, 1, 0, 0, 0, 184, 182, 1, 0, 0, 0, 184, 185, 1, 0, 0, 0, 185, 187, 1, 0, 0, 0, 186, 184, 1, 0, 0, 0, 187, 188, 5, 20, 0, 0, 188, 25, 1, 0, 0, 0, 189, 201, 5, 14, 0, 0, 190, 195, 3, 28, 14, 0, 191, 192, 5, 15, 0, 0, 192, 194, 3, 28, 14, 0, 193, 191, 1, 0, 0, 0, 194, 197, 1, 0, 0, 0, 195, 193, 1, 0, 0, 0, 195, 196, 1, 0, 0, 0, 196, 199, 1, 0, 0, 0, 197, 195, 1, 0, 0, 0, 198, 200, 5, 15, 0, 0, 199, 198, 1, 0, 0, 0, 199, 200, 1, 0, 0, 0, 200, 202, 1, 0, 0, 0, 201, 190, 1, 0, 0, 0, 201, 202, 1, 0, 0, 0, 202, 203, 1, 0, 0, 0, 203, 204, 5, 16, 0, 0, 204, 27, 1, 0, 0, 0, 205, 209, 3, 86, 43, 0, 206, 208, 3, 80, 40, 0, 207, 206, 1, 0, 0, 0, 208, 211, 1, 0, 0, 0, 209, 207, 1, 0, 0, 0, 209, 210, 1, 0, 0, 0, 210, 212, 1, 0, 0, 0, 211, 209, 1, 0, 0, 0, 212, 213, 5, 82, 0, 0, 213, 29, 1, 0, 0, 0, 214, 218, 5, 19, 0, 0, 215, 217, 3, 32, 16, 0, 216, 215, 1, 0, 0, 0, 217, 220, 1, 0, 0, 0, 218, 216, 1, 0, 0, 0, 218, 219, 1, 0, 0, 0, 219, 221, 1, 0, 0, 0, 220, 218, 1, 0, 0, 0, 221, 224, 5, 20, 0, 0, 222, 224, 3, 32, 16, 0, 223, 214, 1, 0, 0, 0, 223, 222, 1, 0, 0, 0, 224, 31, 1, 0, 0, 0, 225, 230, 3, 40, 20, 0, 226, 227, 3, 34, 17, 0, 227, 228, 5, 2, 0, 0, 228, 230, 1, 0, 0, 0, 229, 225, 1, 0, 0, 0, 229, 226, 1, 0, 0, 0, 230, 33, 1, 0, 0, 0, 231, 240, 3, 42, 21, 0, 232, 240, 3, 44, 22, 0, 233, 240, 3, 48, 24, 0, 234, 240, 3, 50, 25, 0, 235, 240, 3, 52, 26, 0, 236, 240, 3, 36, 18, 0, 237, 240, 3, 54, 27, 0, 238, 240, 3, 38, 19, 0, 239, 231, 1, 0, 0, 0, 239, 232, 1, 0, 0, 0, 239, 233, 1, 0, 0, 0, 239, 234, 1, 0, 0, 0, 239, 235, 1, 0, 0, 0, 239, 236, 1, 0, 0, 0, 239, 237, 1, 0, 0, 0, 239, 238, 1, 0, 0, 0, 240, 35, 1, 0, 0, 0, 241, 242, 3, 74, 37, 0, 242, 37, 1, 0, 0, 0, 243, 244, 5, 21, 0, 0, 244, 249, 3, 78, 39, 0, 245, 246, 5, 15, 0, 0, 246, 248, 3, 78, 39, 0, 247, 245, 1, 0, 0, 0, 248, 251, 1, 0, 0, 0, 249, 247, 1, 0, 0, 0, 249, 250, 1, 0, 0, 0, 250, 39, 1, 0, 0, 0, 251, 249, 1, 0, 0, 0, 252, 255, 3, 56, 28, 0, 253, 255, 3, 58, 29, 0, 254, 252, 1, 0, 0, 0, 254, 253, 1, 0, 0, 0, 255, 41, 1, 0, 0, 0, 256, 260, 3, 86, 43, 0, 257, 259, 3, 80, 40, 0, 258, 257, 1, 0, 0, 0, 259, 262, 1, 0, 0, 0, 260, 258, 1, 0, 0, 0, 260, 261, 1, 0, 0, 0, 261, 263, 1, 0, 0, 0, 262, 260, 1, 0, 0, 0, 263, 264, 5, 82, 0, 0, 264, 265, 5, 10, 0, 0, 265, 266, 3, 78, 39, 0, 266, 43, 1, 0, 0, 0, 267, 270, 3, 46, 23, 0, 268, 269, 5, 15, 0, 0, 269, 271, 3, 46, 23, 0, 270, 268, 1, 0, 0, 0, 271, 272, 1, 0, 0, 0, 272, 270, 1, 0, 0, 0, 272, 273, 1, 0, 0, 0, 273, 274, 1, 0, 0, 0, 274, 275, 5, 10, 0, 0, 275, 276, 3, 78, 39, 0, 276, 290, 1, 0, 0, 0, 277, 278, 5, 14, 0, 0, 278, 281, 3, 46, 23, 0, 279, 280, 5, 15, 0, 0, 280, 282, 3, 46, 23, 0, 281, 279, 1, 0, 0, 0, 282, 283, 1, 0, 0, 0, 283, 281, 1, 0, 0, 0, 283, 284, 1, 0, 0, 0, 284, 285, 1, 0, 0, 0, 285, 286, 5, 16, 0, 0, 286, 287, 5, 10, 0, 0, 287, 288, 3, 78, 39, 0, 288, 290, 1, 0, 0, 0, 289, 267, 1, 0, 0, 0, 289, 277, 1, 0, 0, 0, 290, 45, 1, 0, 0, 0, 291, 295, 3, 86, 43, 0, 292, 294, 3, 80, 40, 0, 293, 292, 1, 0, 0, 0, 294, 297, 1, 0, 0, 0, 295, 293, 1, 0, 0, 0, 295, 296, 1, 0, 0, 0, 296, 298, 1, 0, 0, 0, 297, 295, 1, 0, 0, 0, 298, 299, 5, 82, 0, 0, 299, 302, 1, 0, 0, 0, 300, 302, 5, 82, 0, 0, 301, 291, 1, 0, 0, 0, 301, 300, 1, 0, 0, 0, 302, 47, 1, 0, 0, 0, 303, 304, 5, 82, 0, 0, 304, 305, 7, 1, 0, 0, 305, 309, 3, 78, 39, 0, 306, 307, 5, 82, 0, 0, 307, 309, 7, 2, 0, 0, 308, 303, 1, 0, 0, 0, 308, 306, 1, 0, 0, 0, 309, 49, 1, 0, 0, 0, 310, 311, 5, 26, 0, 0, 311, 312, 5, 14, 0, 0, 312, 313, 5, 79, 0, 0, 313, 314, 5, 6, 0, 0, 314, 317, 3, 78, 39, 0, 315, 316, 5, 15, 0, 0, 316, 318, 3, 68, 34, 0, 317, 315, 1, 0, 0, 0, 317, 318, 1, 0, 0, 0, 318, 319, 1, 0, 0, 0, 319, 320, 5, 16, 0, 0, 320, 51, 1, 0, 0, 0, 321, 322, 5, 26, 0, 0, 322, 323, 5, 14, 0, 0, 323, 326, 3, 78, 39, 0, 324, 325, 5, 15, 0, 0, 325, 327, 3, 68, 34, 0, 326, 324, 1, 0, 0, 0, 326, 327, 1, 0, 0, 0, 327, 328, 1, 0, 0, 0, 328, 329, 5, 16, 0, 0, 329, 53, 1, 0, 0, 0, 330, 331, 5, 27, 0, 0, 331, 332, 3, 72, 36, 0, 332, 55, 1, 0, 0, 0, 333, 334, 5, 28, 0, 0, 334, 335, 5, 14, 0, 0, 335, 336, 3, 78, 39, 0, 336, 337, 5, 16, 0, 0, 337, 340, 3, 30, 15, 0, 338, 339, 5, 29, 0, 0, 339, 341, 3, 30, 15, 0, 340, 338, 1, 0, 0, 0, 340, 341, 1, 0, 0, 0, 341, 57, 1, 0, 0, 0, 342, 346, 3, 60, 30, 0, 343, 346, 3, 62, 31, 0, 344, 346, 3, 64, 32, 0, 345, 342, 1, 0, 0, 0, 345, 343, 1, 0, 0, 0, 345, 344, 1, 0, 0, 0, 346, 59, 1, 0, 0, 0, 347, 348, 5, 30, 0, 0, 348, 349, 3, 30, 15, 0, 349, 350, 5, 31, 0, 0, 350, 351, 5, 14, 0, 0, 351, 352, 3, 78, 39, 0, 352, 353, 5, 16, 0, 0, 353, 354, 5, 2, 0, 0, 354, 61, 1, 0, 0, 0, 355, 356, 5, 31, 0, 0, 356, 357, 5, 14, 0, 0, 357, 358, 3, 78, 39, 0, 358, 359, 5, 16, 0, 0, 359, 360, 3, 30, 15, 0, 360, 63, 1, 0, 0, 0, 361, 362, 5, 32, 0, 0, 362, 363, 5, 14, 0, 0, 363, 364, 3, 66, 33, 0, 364, 365, 5, 2, 0, 0, 365, 366, 3, 78, 39, 0, 366, 367, 5, 2, 0, 0, 367, 368, 3, 48, 24, 0, 368, 369, 5, 16, 0, 0, 369, 370, 3, 30, 15, 0, 370, 65, 1, 0, 0, 0, 371, 374, 3, 42, 21, 0, 372, 374, 3, 48, 24, 0, 373, 371, 1, 0, 0, 0, 373, 372, 1, 0, 0, 0, 374, 67, 1, 0, 0, 0, 375, 376, 5, 76, 0, 0, 376, 69, 1, 0, 0, 0, 377, 380, 5, 82, 0, 0, 378, 380, 3, 82, 41, 0, 379, 377, 1, 0, 0, 0, 379, 378, 1, 0, 0, 0, 380, 71, 1, 0, 0, 0, 381, 393, 5, 14, 0, 0, 382, 387, 3, 70, 35, 0, 383, 384, 5, 15, 0, 0, 384, 386, 3, 70, 35, 0, 385, 383, 1, 0, 0, 0, 386, 389, 1, 0, 0, 0, 387, 385, 1, 0, 0, 0, 387, 388, 1, 0, 0, 0, 388, 391, 1, 0, 0, 0, 389, 387, 1, 0, 0, 0, 390, 392, 5, 15, 0, 0, 391, 390, 1, 0, 0, 0, 391, 392, 1, 0, 0, 0, 392, 394, 1, 0, 0, 0, 393, 382, 1, 0, 0, 0, 393, 394, 1, 0, 0, 0, 394, 395, 1, 0, 0, 0, 395, 396, 5, 16, 0, 0, 396, 73, 1, 0, 0, 0, 397, 398, 5, 82, 0, 0, 398, 399, 3, 76, 38, 0, 399, 75, 1, 0, 0, 0, 400, 412, 5, 14, 0, 0, 401, 406, 3, 78, 39, 0, 402, 403, 5, 15, 0, 0, 403, 405, 3, 78, 39, 0, 404, 402, 1, 0, 0, 0, 405, 408, 1, 0, 0, 0, 406, 404, 1, 0, 0, 0, 406, 407, 1, 0, 0, 0, 407, 410, 1, 0, 0, 0, 408, 406, 1, 0, 0, 0, 409, 411, 5, 15, 0, 0, 410, 409, 1, 0, 0, 0, 410, 411, 1, 0, 0, 0, 411, 413, 1, 0, 0, 0, 412, 401, 1, 0, 0, 0, 412, 413, 1, 0, 0, 0, 413, 414, 1, 0, 0, 0, 414, 415, 5, 16, 0, 0, 415, 77, 1, 0, 0, 0, 416, 417, 6, 39, -1, 0, 417, 418, 5, 14, 0, 0, 418, 419, 3, 78, 39, 0, 419, 420, 5, 16, 0, 0, 420, 466, 1, 0, 0, 0, 421, 422, 3, 88, 44, 0, 422, 423, 5, 14, 0, 0, 423, 425, 3, 78, 39, 0, 424, 426, 5, 15, 0, 0, 425, 424, 1, 0, 0, 0, 425, 426, 1, 0, 0, 0, 426, 427, 1, 0, 0, 0, 427, 428, 5, 16, 0, 0, 428, 466, 1, 0, 0, 0, 429, 466, 3, 74, 37, 0, 430, 431, 5, 33, 0, 0, 431, 432, 5, 82, 0, 0, 432, 466, 3, 76, 38, 0, 433, 434, 5, 36, 0, 0, 434, 435, 5, 34, 0, 0, 435, 436, 3, 78, 39, 0, 436, 437, 5, 35, 0, 0, 437, 438, 7, 3, 0, 0, 438, 466, 1, 0, 0, 0, 439, 440, 5, 42, 0, 0, 440, 441, 5, 34, 0, 0, 441, 442, 3, 78, 39, 0, 442, 443, 5, 35, 0, 0, 443, 444, 7, 4, 0, 0, 444, 466, 1, 0, 0, 0, 445, 446, 7, 5, 0, 0, 446, 466, 3, 78, 39, 15, 447, 459, 5, 34, 0, 0, 448, 453, 3, 78, 39, 0, 449, 450, 5, 15, 0, 0, 450, 452, 3, 78, 39, 0, 451, 449, 1, 0, 0, 0, 452, 455, 1, 0, 0, 0, 453, 451, 1, 0, 0, 0, 453, 454, 1, 0, 0, 0, 454, 457, 1, 0, 0, 0, 455, 453, 1, 0, 0, 0, 456, 458, 5, 15, 0, 0, 457, 456, 1, 0, 0, 0, 457, 458, 1, 0, 0, 0, 458, 460, 1, 0, 0, 0, 459, 448, 1, 0, 0, 0, 459, 460, 1, 0, 0, 0, 460, 461, 1, 0, 0, 0, 461, 466, 5, 35, 0, 0, 462, 466, 5, 81, 0, 0, 463, 466, 5, 82, 0, 0, 464, 466, 3, 82, 41, 0, 465, 416, 1, 0, 0, 0, 465, 421, 1, 0, 0, 0, 465, 429, 1, 0, 0, 0, 465, 430, 1, 0, 0, 0, 465, 433, 1, 0, 0, 0, 465, 439, 1, 0, 0, 0, 465, 445, 1, 0, 0, 0, 465, 447, 1, 0, 0, 0, 465, 462, 1, 0, 0, 0, 465, 463, 1, 0, 0, 0, 465, 464, 1, 0, 0, 0, 466, 519, 1, 0, 0, 0, 467, 468, 10, 14, 0, 0, 468, 469, 7, 6, 0, 0, 469, 518, 3, 78, 39, 15, 470, 471, 10, 13, 0, 0, 471, 472, 7, 7, 0, 0, 472, 518, 3, 78, 39, 14, 473, 474, 10, 12, 0, 0, 474, 475, 7, 8, 0, 0, 475, 518, 3, 78, 39, 13, 476, 477, 10, 11, 0, 0, 477, 478, 7, 9, 0, 0, 478, 518, 3, 78, 39, 12, 479, 480, 10, 10, 0, 0, 480, 481, 7, 10, 0, 0, 481, 518, 3, 78, 39, 11, 482, 483, 10, 9, 0, 0, 483, 484, 5, 61, 0, 0, 484, 518, 3, 78, 39, 10, 485, 486, 10, 8, 0, 0, 486, 487, 5, 4, 0, 0, 487, 518, 3, 78, 39, 9, 488, 489, 10, 7, 0, 0, 489, 490, 5, 62, 0, 0, 490, 518, 3, 78, 39, 8, 491, 492, 10, 6, 0, 0, 492, 493, 5, 63, 0, 0, 493, 518, 3, 78, 39, 7, 494, 495, 10, 5, 0, 0, 495, 496, 5, 64, 0, 0, 496, 518, 3, 78, 39, 6, 497, 498, 10, 21, 0, 0, 498, 499, 5, 34, 0, 0, 499, 500, 5, 69, 0, 0, 500, 518, 5, 35, 0, 0, 501, 502, 10, 18, 0, 0, 502, 518, 7, 11, 0, 0, 503, 504, 10, 17, 0, 0, 504, 505, 5, 49, 0, 0, 505, 506, 5, 14, 0, 0, 506, 507, 3, 78, 39, 0, 507, 508, 5, 16, 0, 0, 508, 518, 1, 0, 0, 0, 509, 510, 10, 16, 0, 0, 510, 511, 5, 50, 0, 0, 511, 512, 5, 14, 0, 0, 512, 513, 3, 78, 39, 0, 513, 514, 5, 15, 0, 0, 514, 515, 3, 78, 39, 0, 515, 516, 5, 16, 0, 0, 516, 518, 1, 0, 0, 0, 517, 467, 1, 0, 0, 0, 517, 470, 1, 0, 0, 0, 517, 473, 1, 0, 0, 0, 517, 476, 1, 0, 0, 0, 517, 479, 1, 0, 0, 0, 517, 482, 1, 0, 0, 0, 517, 485, 1, 0, 0, 0, 517, 488, 1, 0, 0, 0, 517, 491, 1, 0, 0, 0, 517, 494, 1, 0, 0, 0, 517, 497, 1, 0, 0, 0, 517, 501, 1, 0, 0, 0, 517, 503, 1, 0, 0, 0, 517, 509, 1, 0, 0, 0, 518, 521, 1, 0, 0, 0, 519, 517, 1, 0, 0, 0, 519, 520, 1, 0, 0, 0, 520, 79, 1, 0, 0, 0, 521, 519, 1, 0, 0, 0, 522, 523, 7, 12, 0, 0, 523, 81, 1, 0, 0, 0, 524, 530, 5, 67, 0, 0, 525, 530, 3, 84, 42, 0, 526, 530, 5, 76, 0, 0, 527, 530, 5, 77, 0, 0, 528, 530, 5, 78, 0, 0, 529, 524, 1, 0, 0, 0, 529, 525, 1, 0, 0, 0, 529, 526, 1, 0, 0, 0, 529, 527, 1, 0, 0, 0, 529, 528, 1, 0, 0, 0, 530, 83, 1, 0, 0, 0, 531, 533, 5, 69, 0, 0, 532, 534, 5, 68, 0, 0, 533, 532, 1, 0, 0, 0, 533, 534, 1, 0, 0, 0, 534, 85, 1, 0, 0, 0, 535, 536, 7, 13, 0, 0, 536, 87, 1, 0, 0, 0, 537, 538, 7, 14, 0, 0, 538, 89, 1, 0, 0, 0, 48, 93, 99, 105, 119, 122, 135, 147, 152, 170, 184, 195, 199, 201, 209, 218, 223, 229, 239, 249, 254, 260, 272, 283, 289, 295, 301, 308, 317, 326, 340, 345, 373, 379, 387, 391, 393, 406, 410, 412, 425, 453, 457, 459, 465, 517, 519, 529, 533] \ No newline at end of file diff --git a/packages/cashc/src/grammar/CashScript.tokens b/packages/cashc/src/grammar/CashScript.tokens index b9cfb61bd..1ca45fe0a 100644 --- a/packages/cashc/src/grammar/CashScript.tokens +++ b/packages/cashc/src/grammar/CashScript.tokens @@ -59,26 +59,30 @@ T__57=58 T__58=59 T__59=60 T__60=61 -VersionLiteral=62 -BooleanLiteral=63 -NumberUnit=64 -NumberLiteral=65 -NumberPart=66 -ExponentPart=67 -PrimitiveType=68 -UnboundedBytes=69 -BoundedBytes=70 -Bound=71 -StringLiteral=72 -DateLiteral=73 -HexLiteral=74 -TxVar=75 -UnsafeCast=76 -NullaryOp=77 -Identifier=78 -WHITESPACE=79 -COMMENT=80 -LINE_COMMENT=81 +T__61=62 +T__62=63 +T__63=64 +T__64=65 +VersionLiteral=66 +BooleanLiteral=67 +NumberUnit=68 +NumberLiteral=69 +NumberPart=70 +ExponentPart=71 +PrimitiveType=72 +UnboundedBytes=73 +BoundedBytes=74 +Bound=75 +StringLiteral=76 +DateLiteral=77 +HexLiteral=78 +TxVar=79 +UnsafeCast=80 +NullaryOp=81 +Identifier=82 +WHITESPACE=83 +COMMENT=84 +LINE_COMMENT=85 'pragma'=1 ';'=2 'cashscript'=3 @@ -89,55 +93,59 @@ LINE_COMMENT=81 '<'=8 '<='=9 '='=10 -'contract'=11 -'{'=12 -'}'=13 -'function'=14 -'('=15 -','=16 -')'=17 -'+='=18 -'-='=19 -'++'=20 -'--'=21 -'require'=22 -'console.log'=23 -'if'=24 -'else'=25 -'do'=26 -'while'=27 -'for'=28 -'new'=29 -'['=30 -']'=31 -'tx.outputs'=32 -'.value'=33 -'.lockingBytecode'=34 -'.tokenCategory'=35 -'.nftCommitment'=36 -'.tokenAmount'=37 -'tx.inputs'=38 -'.outpointTransactionHash'=39 -'.outpointIndex'=40 -'.unlockingBytecode'=41 -'.sequenceNumber'=42 -'.reverse()'=43 -'.length'=44 -'.split'=45 -'.slice'=46 -'!'=47 -'-'=48 -'*'=49 -'/'=50 -'%'=51 -'+'=52 -'>>'=53 -'<<'=54 -'=='=55 -'!='=56 -'&'=57 -'|'=58 -'&&'=59 -'||'=60 -'constant'=61 -'bytes'=69 +'import'=11 +'function'=12 +'returns'=13 +'('=14 +','=15 +')'=16 +'constant'=17 +'contract'=18 +'{'=19 +'}'=20 +'return'=21 +'+='=22 +'-='=23 +'++'=24 +'--'=25 +'require'=26 +'console.log'=27 +'if'=28 +'else'=29 +'do'=30 +'while'=31 +'for'=32 +'new'=33 +'['=34 +']'=35 +'tx.outputs'=36 +'.value'=37 +'.lockingBytecode'=38 +'.tokenCategory'=39 +'.nftCommitment'=40 +'.tokenAmount'=41 +'tx.inputs'=42 +'.outpointTransactionHash'=43 +'.outpointIndex'=44 +'.unlockingBytecode'=45 +'.sequenceNumber'=46 +'.reverse()'=47 +'.length'=48 +'.split'=49 +'.slice'=50 +'!'=51 +'-'=52 +'*'=53 +'/'=54 +'%'=55 +'+'=56 +'>>'=57 +'<<'=58 +'=='=59 +'!='=60 +'&'=61 +'|'=62 +'&&'=63 +'||'=64 +'unused'=65 +'bytes'=73 diff --git a/packages/cashc/src/grammar/CashScriptLexer.interp b/packages/cashc/src/grammar/CashScriptLexer.interp index e2bb72fc8..53253d139 100644 --- a/packages/cashc/src/grammar/CashScriptLexer.interp +++ b/packages/cashc/src/grammar/CashScriptLexer.interp @@ -10,13 +10,17 @@ null '<' '<=' '=' -'contract' -'{' -'}' +'import' 'function' +'returns' '(' ',' ')' +'constant' +'contract' +'{' +'}' +'return' '+=' '-=' '++' @@ -60,7 +64,7 @@ null '|' '&&' '||' -'constant' +'unused' null null null @@ -145,6 +149,10 @@ null null null null +null +null +null +null VersionLiteral BooleanLiteral NumberUnit @@ -228,6 +236,10 @@ T__57 T__58 T__59 T__60 +T__61 +T__62 +T__63 +T__64 VersionLiteral BooleanLiteral NumberUnit @@ -257,4 +269,4 @@ mode names: DEFAULT_MODE atn: -[4, 0, 81, 938, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 47, 1, 47, 1, 48, 1, 48, 1, 49, 1, 49, 1, 50, 1, 50, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 4, 61, 529, 8, 61, 11, 61, 12, 61, 530, 1, 61, 1, 61, 4, 61, 535, 8, 61, 11, 61, 12, 61, 536, 1, 61, 1, 61, 4, 61, 541, 8, 61, 11, 61, 12, 61, 542, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 3, 62, 554, 8, 62, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 3, 63, 613, 8, 63, 1, 64, 3, 64, 616, 8, 64, 1, 64, 1, 64, 3, 64, 620, 8, 64, 1, 65, 4, 65, 623, 8, 65, 11, 65, 12, 65, 624, 1, 65, 1, 65, 4, 65, 629, 8, 65, 11, 65, 12, 65, 630, 5, 65, 633, 8, 65, 10, 65, 12, 65, 636, 9, 65, 1, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 3, 67, 670, 8, 67, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 3, 69, 689, 8, 69, 1, 70, 1, 70, 5, 70, 693, 8, 70, 10, 70, 12, 70, 696, 9, 70, 1, 71, 1, 71, 1, 71, 1, 71, 5, 71, 702, 8, 71, 10, 71, 12, 71, 705, 9, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 5, 71, 712, 8, 71, 10, 71, 12, 71, 715, 9, 71, 1, 71, 3, 71, 718, 8, 71, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 73, 1, 73, 1, 73, 5, 73, 732, 8, 73, 10, 73, 12, 73, 735, 9, 73, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 3, 74, 752, 8, 74, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 3, 75, 789, 8, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 3, 75, 802, 8, 75, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 3, 76, 898, 8, 76, 1, 77, 1, 77, 5, 77, 902, 8, 77, 10, 77, 12, 77, 905, 9, 77, 1, 78, 4, 78, 908, 8, 78, 11, 78, 12, 78, 909, 1, 78, 1, 78, 1, 79, 1, 79, 1, 79, 1, 79, 5, 79, 918, 8, 79, 10, 79, 12, 79, 921, 9, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 80, 1, 80, 1, 80, 1, 80, 5, 80, 932, 8, 80, 10, 80, 12, 80, 935, 9, 80, 1, 80, 1, 80, 3, 703, 713, 919, 0, 81, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, 35, 71, 36, 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, 42, 85, 43, 87, 44, 89, 45, 91, 46, 93, 47, 95, 48, 97, 49, 99, 50, 101, 51, 103, 52, 105, 53, 107, 54, 109, 55, 111, 56, 113, 57, 115, 58, 117, 59, 119, 60, 121, 61, 123, 62, 125, 63, 127, 64, 129, 65, 131, 66, 133, 67, 135, 68, 137, 69, 139, 70, 141, 71, 143, 72, 145, 73, 147, 74, 149, 75, 151, 76, 153, 77, 155, 78, 157, 79, 159, 80, 161, 81, 1, 0, 11, 1, 0, 48, 57, 2, 0, 69, 69, 101, 101, 1, 0, 49, 57, 3, 0, 10, 10, 13, 13, 34, 34, 3, 0, 10, 10, 13, 13, 39, 39, 2, 0, 88, 88, 120, 120, 3, 0, 48, 57, 65, 70, 97, 102, 2, 0, 65, 90, 97, 122, 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 3, 0, 9, 10, 12, 13, 32, 32, 2, 0, 10, 10, 13, 13, 982, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0, 0, 0, 95, 1, 0, 0, 0, 0, 97, 1, 0, 0, 0, 0, 99, 1, 0, 0, 0, 0, 101, 1, 0, 0, 0, 0, 103, 1, 0, 0, 0, 0, 105, 1, 0, 0, 0, 0, 107, 1, 0, 0, 0, 0, 109, 1, 0, 0, 0, 0, 111, 1, 0, 0, 0, 0, 113, 1, 0, 0, 0, 0, 115, 1, 0, 0, 0, 0, 117, 1, 0, 0, 0, 0, 119, 1, 0, 0, 0, 0, 121, 1, 0, 0, 0, 0, 123, 1, 0, 0, 0, 0, 125, 1, 0, 0, 0, 0, 127, 1, 0, 0, 0, 0, 129, 1, 0, 0, 0, 0, 131, 1, 0, 0, 0, 0, 133, 1, 0, 0, 0, 0, 135, 1, 0, 0, 0, 0, 137, 1, 0, 0, 0, 0, 139, 1, 0, 0, 0, 0, 141, 1, 0, 0, 0, 0, 143, 1, 0, 0, 0, 0, 145, 1, 0, 0, 0, 0, 147, 1, 0, 0, 0, 0, 149, 1, 0, 0, 0, 0, 151, 1, 0, 0, 0, 0, 153, 1, 0, 0, 0, 0, 155, 1, 0, 0, 0, 0, 157, 1, 0, 0, 0, 0, 159, 1, 0, 0, 0, 0, 161, 1, 0, 0, 0, 1, 163, 1, 0, 0, 0, 3, 170, 1, 0, 0, 0, 5, 172, 1, 0, 0, 0, 7, 183, 1, 0, 0, 0, 9, 185, 1, 0, 0, 0, 11, 187, 1, 0, 0, 0, 13, 190, 1, 0, 0, 0, 15, 192, 1, 0, 0, 0, 17, 194, 1, 0, 0, 0, 19, 197, 1, 0, 0, 0, 21, 199, 1, 0, 0, 0, 23, 208, 1, 0, 0, 0, 25, 210, 1, 0, 0, 0, 27, 212, 1, 0, 0, 0, 29, 221, 1, 0, 0, 0, 31, 223, 1, 0, 0, 0, 33, 225, 1, 0, 0, 0, 35, 227, 1, 0, 0, 0, 37, 230, 1, 0, 0, 0, 39, 233, 1, 0, 0, 0, 41, 236, 1, 0, 0, 0, 43, 239, 1, 0, 0, 0, 45, 247, 1, 0, 0, 0, 47, 259, 1, 0, 0, 0, 49, 262, 1, 0, 0, 0, 51, 267, 1, 0, 0, 0, 53, 270, 1, 0, 0, 0, 55, 276, 1, 0, 0, 0, 57, 280, 1, 0, 0, 0, 59, 284, 1, 0, 0, 0, 61, 286, 1, 0, 0, 0, 63, 288, 1, 0, 0, 0, 65, 299, 1, 0, 0, 0, 67, 306, 1, 0, 0, 0, 69, 323, 1, 0, 0, 0, 71, 338, 1, 0, 0, 0, 73, 353, 1, 0, 0, 0, 75, 366, 1, 0, 0, 0, 77, 376, 1, 0, 0, 0, 79, 401, 1, 0, 0, 0, 81, 416, 1, 0, 0, 0, 83, 435, 1, 0, 0, 0, 85, 451, 1, 0, 0, 0, 87, 462, 1, 0, 0, 0, 89, 470, 1, 0, 0, 0, 91, 477, 1, 0, 0, 0, 93, 484, 1, 0, 0, 0, 95, 486, 1, 0, 0, 0, 97, 488, 1, 0, 0, 0, 99, 490, 1, 0, 0, 0, 101, 492, 1, 0, 0, 0, 103, 494, 1, 0, 0, 0, 105, 496, 1, 0, 0, 0, 107, 499, 1, 0, 0, 0, 109, 502, 1, 0, 0, 0, 111, 505, 1, 0, 0, 0, 113, 508, 1, 0, 0, 0, 115, 510, 1, 0, 0, 0, 117, 512, 1, 0, 0, 0, 119, 515, 1, 0, 0, 0, 121, 518, 1, 0, 0, 0, 123, 528, 1, 0, 0, 0, 125, 553, 1, 0, 0, 0, 127, 612, 1, 0, 0, 0, 129, 615, 1, 0, 0, 0, 131, 622, 1, 0, 0, 0, 133, 637, 1, 0, 0, 0, 135, 669, 1, 0, 0, 0, 137, 671, 1, 0, 0, 0, 139, 688, 1, 0, 0, 0, 141, 690, 1, 0, 0, 0, 143, 717, 1, 0, 0, 0, 145, 719, 1, 0, 0, 0, 147, 728, 1, 0, 0, 0, 149, 751, 1, 0, 0, 0, 151, 801, 1, 0, 0, 0, 153, 897, 1, 0, 0, 0, 155, 899, 1, 0, 0, 0, 157, 907, 1, 0, 0, 0, 159, 913, 1, 0, 0, 0, 161, 927, 1, 0, 0, 0, 163, 164, 5, 112, 0, 0, 164, 165, 5, 114, 0, 0, 165, 166, 5, 97, 0, 0, 166, 167, 5, 103, 0, 0, 167, 168, 5, 109, 0, 0, 168, 169, 5, 97, 0, 0, 169, 2, 1, 0, 0, 0, 170, 171, 5, 59, 0, 0, 171, 4, 1, 0, 0, 0, 172, 173, 5, 99, 0, 0, 173, 174, 5, 97, 0, 0, 174, 175, 5, 115, 0, 0, 175, 176, 5, 104, 0, 0, 176, 177, 5, 115, 0, 0, 177, 178, 5, 99, 0, 0, 178, 179, 5, 114, 0, 0, 179, 180, 5, 105, 0, 0, 180, 181, 5, 112, 0, 0, 181, 182, 5, 116, 0, 0, 182, 6, 1, 0, 0, 0, 183, 184, 5, 94, 0, 0, 184, 8, 1, 0, 0, 0, 185, 186, 5, 126, 0, 0, 186, 10, 1, 0, 0, 0, 187, 188, 5, 62, 0, 0, 188, 189, 5, 61, 0, 0, 189, 12, 1, 0, 0, 0, 190, 191, 5, 62, 0, 0, 191, 14, 1, 0, 0, 0, 192, 193, 5, 60, 0, 0, 193, 16, 1, 0, 0, 0, 194, 195, 5, 60, 0, 0, 195, 196, 5, 61, 0, 0, 196, 18, 1, 0, 0, 0, 197, 198, 5, 61, 0, 0, 198, 20, 1, 0, 0, 0, 199, 200, 5, 99, 0, 0, 200, 201, 5, 111, 0, 0, 201, 202, 5, 110, 0, 0, 202, 203, 5, 116, 0, 0, 203, 204, 5, 114, 0, 0, 204, 205, 5, 97, 0, 0, 205, 206, 5, 99, 0, 0, 206, 207, 5, 116, 0, 0, 207, 22, 1, 0, 0, 0, 208, 209, 5, 123, 0, 0, 209, 24, 1, 0, 0, 0, 210, 211, 5, 125, 0, 0, 211, 26, 1, 0, 0, 0, 212, 213, 5, 102, 0, 0, 213, 214, 5, 117, 0, 0, 214, 215, 5, 110, 0, 0, 215, 216, 5, 99, 0, 0, 216, 217, 5, 116, 0, 0, 217, 218, 5, 105, 0, 0, 218, 219, 5, 111, 0, 0, 219, 220, 5, 110, 0, 0, 220, 28, 1, 0, 0, 0, 221, 222, 5, 40, 0, 0, 222, 30, 1, 0, 0, 0, 223, 224, 5, 44, 0, 0, 224, 32, 1, 0, 0, 0, 225, 226, 5, 41, 0, 0, 226, 34, 1, 0, 0, 0, 227, 228, 5, 43, 0, 0, 228, 229, 5, 61, 0, 0, 229, 36, 1, 0, 0, 0, 230, 231, 5, 45, 0, 0, 231, 232, 5, 61, 0, 0, 232, 38, 1, 0, 0, 0, 233, 234, 5, 43, 0, 0, 234, 235, 5, 43, 0, 0, 235, 40, 1, 0, 0, 0, 236, 237, 5, 45, 0, 0, 237, 238, 5, 45, 0, 0, 238, 42, 1, 0, 0, 0, 239, 240, 5, 114, 0, 0, 240, 241, 5, 101, 0, 0, 241, 242, 5, 113, 0, 0, 242, 243, 5, 117, 0, 0, 243, 244, 5, 105, 0, 0, 244, 245, 5, 114, 0, 0, 245, 246, 5, 101, 0, 0, 246, 44, 1, 0, 0, 0, 247, 248, 5, 99, 0, 0, 248, 249, 5, 111, 0, 0, 249, 250, 5, 110, 0, 0, 250, 251, 5, 115, 0, 0, 251, 252, 5, 111, 0, 0, 252, 253, 5, 108, 0, 0, 253, 254, 5, 101, 0, 0, 254, 255, 5, 46, 0, 0, 255, 256, 5, 108, 0, 0, 256, 257, 5, 111, 0, 0, 257, 258, 5, 103, 0, 0, 258, 46, 1, 0, 0, 0, 259, 260, 5, 105, 0, 0, 260, 261, 5, 102, 0, 0, 261, 48, 1, 0, 0, 0, 262, 263, 5, 101, 0, 0, 263, 264, 5, 108, 0, 0, 264, 265, 5, 115, 0, 0, 265, 266, 5, 101, 0, 0, 266, 50, 1, 0, 0, 0, 267, 268, 5, 100, 0, 0, 268, 269, 5, 111, 0, 0, 269, 52, 1, 0, 0, 0, 270, 271, 5, 119, 0, 0, 271, 272, 5, 104, 0, 0, 272, 273, 5, 105, 0, 0, 273, 274, 5, 108, 0, 0, 274, 275, 5, 101, 0, 0, 275, 54, 1, 0, 0, 0, 276, 277, 5, 102, 0, 0, 277, 278, 5, 111, 0, 0, 278, 279, 5, 114, 0, 0, 279, 56, 1, 0, 0, 0, 280, 281, 5, 110, 0, 0, 281, 282, 5, 101, 0, 0, 282, 283, 5, 119, 0, 0, 283, 58, 1, 0, 0, 0, 284, 285, 5, 91, 0, 0, 285, 60, 1, 0, 0, 0, 286, 287, 5, 93, 0, 0, 287, 62, 1, 0, 0, 0, 288, 289, 5, 116, 0, 0, 289, 290, 5, 120, 0, 0, 290, 291, 5, 46, 0, 0, 291, 292, 5, 111, 0, 0, 292, 293, 5, 117, 0, 0, 293, 294, 5, 116, 0, 0, 294, 295, 5, 112, 0, 0, 295, 296, 5, 117, 0, 0, 296, 297, 5, 116, 0, 0, 297, 298, 5, 115, 0, 0, 298, 64, 1, 0, 0, 0, 299, 300, 5, 46, 0, 0, 300, 301, 5, 118, 0, 0, 301, 302, 5, 97, 0, 0, 302, 303, 5, 108, 0, 0, 303, 304, 5, 117, 0, 0, 304, 305, 5, 101, 0, 0, 305, 66, 1, 0, 0, 0, 306, 307, 5, 46, 0, 0, 307, 308, 5, 108, 0, 0, 308, 309, 5, 111, 0, 0, 309, 310, 5, 99, 0, 0, 310, 311, 5, 107, 0, 0, 311, 312, 5, 105, 0, 0, 312, 313, 5, 110, 0, 0, 313, 314, 5, 103, 0, 0, 314, 315, 5, 66, 0, 0, 315, 316, 5, 121, 0, 0, 316, 317, 5, 116, 0, 0, 317, 318, 5, 101, 0, 0, 318, 319, 5, 99, 0, 0, 319, 320, 5, 111, 0, 0, 320, 321, 5, 100, 0, 0, 321, 322, 5, 101, 0, 0, 322, 68, 1, 0, 0, 0, 323, 324, 5, 46, 0, 0, 324, 325, 5, 116, 0, 0, 325, 326, 5, 111, 0, 0, 326, 327, 5, 107, 0, 0, 327, 328, 5, 101, 0, 0, 328, 329, 5, 110, 0, 0, 329, 330, 5, 67, 0, 0, 330, 331, 5, 97, 0, 0, 331, 332, 5, 116, 0, 0, 332, 333, 5, 101, 0, 0, 333, 334, 5, 103, 0, 0, 334, 335, 5, 111, 0, 0, 335, 336, 5, 114, 0, 0, 336, 337, 5, 121, 0, 0, 337, 70, 1, 0, 0, 0, 338, 339, 5, 46, 0, 0, 339, 340, 5, 110, 0, 0, 340, 341, 5, 102, 0, 0, 341, 342, 5, 116, 0, 0, 342, 343, 5, 67, 0, 0, 343, 344, 5, 111, 0, 0, 344, 345, 5, 109, 0, 0, 345, 346, 5, 109, 0, 0, 346, 347, 5, 105, 0, 0, 347, 348, 5, 116, 0, 0, 348, 349, 5, 109, 0, 0, 349, 350, 5, 101, 0, 0, 350, 351, 5, 110, 0, 0, 351, 352, 5, 116, 0, 0, 352, 72, 1, 0, 0, 0, 353, 354, 5, 46, 0, 0, 354, 355, 5, 116, 0, 0, 355, 356, 5, 111, 0, 0, 356, 357, 5, 107, 0, 0, 357, 358, 5, 101, 0, 0, 358, 359, 5, 110, 0, 0, 359, 360, 5, 65, 0, 0, 360, 361, 5, 109, 0, 0, 361, 362, 5, 111, 0, 0, 362, 363, 5, 117, 0, 0, 363, 364, 5, 110, 0, 0, 364, 365, 5, 116, 0, 0, 365, 74, 1, 0, 0, 0, 366, 367, 5, 116, 0, 0, 367, 368, 5, 120, 0, 0, 368, 369, 5, 46, 0, 0, 369, 370, 5, 105, 0, 0, 370, 371, 5, 110, 0, 0, 371, 372, 5, 112, 0, 0, 372, 373, 5, 117, 0, 0, 373, 374, 5, 116, 0, 0, 374, 375, 5, 115, 0, 0, 375, 76, 1, 0, 0, 0, 376, 377, 5, 46, 0, 0, 377, 378, 5, 111, 0, 0, 378, 379, 5, 117, 0, 0, 379, 380, 5, 116, 0, 0, 380, 381, 5, 112, 0, 0, 381, 382, 5, 111, 0, 0, 382, 383, 5, 105, 0, 0, 383, 384, 5, 110, 0, 0, 384, 385, 5, 116, 0, 0, 385, 386, 5, 84, 0, 0, 386, 387, 5, 114, 0, 0, 387, 388, 5, 97, 0, 0, 388, 389, 5, 110, 0, 0, 389, 390, 5, 115, 0, 0, 390, 391, 5, 97, 0, 0, 391, 392, 5, 99, 0, 0, 392, 393, 5, 116, 0, 0, 393, 394, 5, 105, 0, 0, 394, 395, 5, 111, 0, 0, 395, 396, 5, 110, 0, 0, 396, 397, 5, 72, 0, 0, 397, 398, 5, 97, 0, 0, 398, 399, 5, 115, 0, 0, 399, 400, 5, 104, 0, 0, 400, 78, 1, 0, 0, 0, 401, 402, 5, 46, 0, 0, 402, 403, 5, 111, 0, 0, 403, 404, 5, 117, 0, 0, 404, 405, 5, 116, 0, 0, 405, 406, 5, 112, 0, 0, 406, 407, 5, 111, 0, 0, 407, 408, 5, 105, 0, 0, 408, 409, 5, 110, 0, 0, 409, 410, 5, 116, 0, 0, 410, 411, 5, 73, 0, 0, 411, 412, 5, 110, 0, 0, 412, 413, 5, 100, 0, 0, 413, 414, 5, 101, 0, 0, 414, 415, 5, 120, 0, 0, 415, 80, 1, 0, 0, 0, 416, 417, 5, 46, 0, 0, 417, 418, 5, 117, 0, 0, 418, 419, 5, 110, 0, 0, 419, 420, 5, 108, 0, 0, 420, 421, 5, 111, 0, 0, 421, 422, 5, 99, 0, 0, 422, 423, 5, 107, 0, 0, 423, 424, 5, 105, 0, 0, 424, 425, 5, 110, 0, 0, 425, 426, 5, 103, 0, 0, 426, 427, 5, 66, 0, 0, 427, 428, 5, 121, 0, 0, 428, 429, 5, 116, 0, 0, 429, 430, 5, 101, 0, 0, 430, 431, 5, 99, 0, 0, 431, 432, 5, 111, 0, 0, 432, 433, 5, 100, 0, 0, 433, 434, 5, 101, 0, 0, 434, 82, 1, 0, 0, 0, 435, 436, 5, 46, 0, 0, 436, 437, 5, 115, 0, 0, 437, 438, 5, 101, 0, 0, 438, 439, 5, 113, 0, 0, 439, 440, 5, 117, 0, 0, 440, 441, 5, 101, 0, 0, 441, 442, 5, 110, 0, 0, 442, 443, 5, 99, 0, 0, 443, 444, 5, 101, 0, 0, 444, 445, 5, 78, 0, 0, 445, 446, 5, 117, 0, 0, 446, 447, 5, 109, 0, 0, 447, 448, 5, 98, 0, 0, 448, 449, 5, 101, 0, 0, 449, 450, 5, 114, 0, 0, 450, 84, 1, 0, 0, 0, 451, 452, 5, 46, 0, 0, 452, 453, 5, 114, 0, 0, 453, 454, 5, 101, 0, 0, 454, 455, 5, 118, 0, 0, 455, 456, 5, 101, 0, 0, 456, 457, 5, 114, 0, 0, 457, 458, 5, 115, 0, 0, 458, 459, 5, 101, 0, 0, 459, 460, 5, 40, 0, 0, 460, 461, 5, 41, 0, 0, 461, 86, 1, 0, 0, 0, 462, 463, 5, 46, 0, 0, 463, 464, 5, 108, 0, 0, 464, 465, 5, 101, 0, 0, 465, 466, 5, 110, 0, 0, 466, 467, 5, 103, 0, 0, 467, 468, 5, 116, 0, 0, 468, 469, 5, 104, 0, 0, 469, 88, 1, 0, 0, 0, 470, 471, 5, 46, 0, 0, 471, 472, 5, 115, 0, 0, 472, 473, 5, 112, 0, 0, 473, 474, 5, 108, 0, 0, 474, 475, 5, 105, 0, 0, 475, 476, 5, 116, 0, 0, 476, 90, 1, 0, 0, 0, 477, 478, 5, 46, 0, 0, 478, 479, 5, 115, 0, 0, 479, 480, 5, 108, 0, 0, 480, 481, 5, 105, 0, 0, 481, 482, 5, 99, 0, 0, 482, 483, 5, 101, 0, 0, 483, 92, 1, 0, 0, 0, 484, 485, 5, 33, 0, 0, 485, 94, 1, 0, 0, 0, 486, 487, 5, 45, 0, 0, 487, 96, 1, 0, 0, 0, 488, 489, 5, 42, 0, 0, 489, 98, 1, 0, 0, 0, 490, 491, 5, 47, 0, 0, 491, 100, 1, 0, 0, 0, 492, 493, 5, 37, 0, 0, 493, 102, 1, 0, 0, 0, 494, 495, 5, 43, 0, 0, 495, 104, 1, 0, 0, 0, 496, 497, 5, 62, 0, 0, 497, 498, 5, 62, 0, 0, 498, 106, 1, 0, 0, 0, 499, 500, 5, 60, 0, 0, 500, 501, 5, 60, 0, 0, 501, 108, 1, 0, 0, 0, 502, 503, 5, 61, 0, 0, 503, 504, 5, 61, 0, 0, 504, 110, 1, 0, 0, 0, 505, 506, 5, 33, 0, 0, 506, 507, 5, 61, 0, 0, 507, 112, 1, 0, 0, 0, 508, 509, 5, 38, 0, 0, 509, 114, 1, 0, 0, 0, 510, 511, 5, 124, 0, 0, 511, 116, 1, 0, 0, 0, 512, 513, 5, 38, 0, 0, 513, 514, 5, 38, 0, 0, 514, 118, 1, 0, 0, 0, 515, 516, 5, 124, 0, 0, 516, 517, 5, 124, 0, 0, 517, 120, 1, 0, 0, 0, 518, 519, 5, 99, 0, 0, 519, 520, 5, 111, 0, 0, 520, 521, 5, 110, 0, 0, 521, 522, 5, 115, 0, 0, 522, 523, 5, 116, 0, 0, 523, 524, 5, 97, 0, 0, 524, 525, 5, 110, 0, 0, 525, 526, 5, 116, 0, 0, 526, 122, 1, 0, 0, 0, 527, 529, 7, 0, 0, 0, 528, 527, 1, 0, 0, 0, 529, 530, 1, 0, 0, 0, 530, 528, 1, 0, 0, 0, 530, 531, 1, 0, 0, 0, 531, 532, 1, 0, 0, 0, 532, 534, 5, 46, 0, 0, 533, 535, 7, 0, 0, 0, 534, 533, 1, 0, 0, 0, 535, 536, 1, 0, 0, 0, 536, 534, 1, 0, 0, 0, 536, 537, 1, 0, 0, 0, 537, 538, 1, 0, 0, 0, 538, 540, 5, 46, 0, 0, 539, 541, 7, 0, 0, 0, 540, 539, 1, 0, 0, 0, 541, 542, 1, 0, 0, 0, 542, 540, 1, 0, 0, 0, 542, 543, 1, 0, 0, 0, 543, 124, 1, 0, 0, 0, 544, 545, 5, 116, 0, 0, 545, 546, 5, 114, 0, 0, 546, 547, 5, 117, 0, 0, 547, 554, 5, 101, 0, 0, 548, 549, 5, 102, 0, 0, 549, 550, 5, 97, 0, 0, 550, 551, 5, 108, 0, 0, 551, 552, 5, 115, 0, 0, 552, 554, 5, 101, 0, 0, 553, 544, 1, 0, 0, 0, 553, 548, 1, 0, 0, 0, 554, 126, 1, 0, 0, 0, 555, 556, 5, 115, 0, 0, 556, 557, 5, 97, 0, 0, 557, 558, 5, 116, 0, 0, 558, 559, 5, 111, 0, 0, 559, 560, 5, 115, 0, 0, 560, 561, 5, 104, 0, 0, 561, 562, 5, 105, 0, 0, 562, 613, 5, 115, 0, 0, 563, 564, 5, 115, 0, 0, 564, 565, 5, 97, 0, 0, 565, 566, 5, 116, 0, 0, 566, 613, 5, 115, 0, 0, 567, 568, 5, 102, 0, 0, 568, 569, 5, 105, 0, 0, 569, 570, 5, 110, 0, 0, 570, 571, 5, 110, 0, 0, 571, 572, 5, 101, 0, 0, 572, 613, 5, 121, 0, 0, 573, 574, 5, 98, 0, 0, 574, 575, 5, 105, 0, 0, 575, 576, 5, 116, 0, 0, 576, 613, 5, 115, 0, 0, 577, 578, 5, 98, 0, 0, 578, 579, 5, 105, 0, 0, 579, 580, 5, 116, 0, 0, 580, 581, 5, 99, 0, 0, 581, 582, 5, 111, 0, 0, 582, 583, 5, 105, 0, 0, 583, 613, 5, 110, 0, 0, 584, 585, 5, 115, 0, 0, 585, 586, 5, 101, 0, 0, 586, 587, 5, 99, 0, 0, 587, 588, 5, 111, 0, 0, 588, 589, 5, 110, 0, 0, 589, 590, 5, 100, 0, 0, 590, 613, 5, 115, 0, 0, 591, 592, 5, 109, 0, 0, 592, 593, 5, 105, 0, 0, 593, 594, 5, 110, 0, 0, 594, 595, 5, 117, 0, 0, 595, 596, 5, 116, 0, 0, 596, 597, 5, 101, 0, 0, 597, 613, 5, 115, 0, 0, 598, 599, 5, 104, 0, 0, 599, 600, 5, 111, 0, 0, 600, 601, 5, 117, 0, 0, 601, 602, 5, 114, 0, 0, 602, 613, 5, 115, 0, 0, 603, 604, 5, 100, 0, 0, 604, 605, 5, 97, 0, 0, 605, 606, 5, 121, 0, 0, 606, 613, 5, 115, 0, 0, 607, 608, 5, 119, 0, 0, 608, 609, 5, 101, 0, 0, 609, 610, 5, 101, 0, 0, 610, 611, 5, 107, 0, 0, 611, 613, 5, 115, 0, 0, 612, 555, 1, 0, 0, 0, 612, 563, 1, 0, 0, 0, 612, 567, 1, 0, 0, 0, 612, 573, 1, 0, 0, 0, 612, 577, 1, 0, 0, 0, 612, 584, 1, 0, 0, 0, 612, 591, 1, 0, 0, 0, 612, 598, 1, 0, 0, 0, 612, 603, 1, 0, 0, 0, 612, 607, 1, 0, 0, 0, 613, 128, 1, 0, 0, 0, 614, 616, 5, 45, 0, 0, 615, 614, 1, 0, 0, 0, 615, 616, 1, 0, 0, 0, 616, 617, 1, 0, 0, 0, 617, 619, 3, 131, 65, 0, 618, 620, 3, 133, 66, 0, 619, 618, 1, 0, 0, 0, 619, 620, 1, 0, 0, 0, 620, 130, 1, 0, 0, 0, 621, 623, 7, 0, 0, 0, 622, 621, 1, 0, 0, 0, 623, 624, 1, 0, 0, 0, 624, 622, 1, 0, 0, 0, 624, 625, 1, 0, 0, 0, 625, 634, 1, 0, 0, 0, 626, 628, 5, 95, 0, 0, 627, 629, 7, 0, 0, 0, 628, 627, 1, 0, 0, 0, 629, 630, 1, 0, 0, 0, 630, 628, 1, 0, 0, 0, 630, 631, 1, 0, 0, 0, 631, 633, 1, 0, 0, 0, 632, 626, 1, 0, 0, 0, 633, 636, 1, 0, 0, 0, 634, 632, 1, 0, 0, 0, 634, 635, 1, 0, 0, 0, 635, 132, 1, 0, 0, 0, 636, 634, 1, 0, 0, 0, 637, 638, 7, 1, 0, 0, 638, 639, 3, 131, 65, 0, 639, 134, 1, 0, 0, 0, 640, 641, 5, 105, 0, 0, 641, 642, 5, 110, 0, 0, 642, 670, 5, 116, 0, 0, 643, 644, 5, 98, 0, 0, 644, 645, 5, 111, 0, 0, 645, 646, 5, 111, 0, 0, 646, 670, 5, 108, 0, 0, 647, 648, 5, 115, 0, 0, 648, 649, 5, 116, 0, 0, 649, 650, 5, 114, 0, 0, 650, 651, 5, 105, 0, 0, 651, 652, 5, 110, 0, 0, 652, 670, 5, 103, 0, 0, 653, 654, 5, 112, 0, 0, 654, 655, 5, 117, 0, 0, 655, 656, 5, 98, 0, 0, 656, 657, 5, 107, 0, 0, 657, 658, 5, 101, 0, 0, 658, 670, 5, 121, 0, 0, 659, 660, 5, 115, 0, 0, 660, 661, 5, 105, 0, 0, 661, 670, 5, 103, 0, 0, 662, 663, 5, 100, 0, 0, 663, 664, 5, 97, 0, 0, 664, 665, 5, 116, 0, 0, 665, 666, 5, 97, 0, 0, 666, 667, 5, 115, 0, 0, 667, 668, 5, 105, 0, 0, 668, 670, 5, 103, 0, 0, 669, 640, 1, 0, 0, 0, 669, 643, 1, 0, 0, 0, 669, 647, 1, 0, 0, 0, 669, 653, 1, 0, 0, 0, 669, 659, 1, 0, 0, 0, 669, 662, 1, 0, 0, 0, 670, 136, 1, 0, 0, 0, 671, 672, 5, 98, 0, 0, 672, 673, 5, 121, 0, 0, 673, 674, 5, 116, 0, 0, 674, 675, 5, 101, 0, 0, 675, 676, 5, 115, 0, 0, 676, 138, 1, 0, 0, 0, 677, 678, 5, 98, 0, 0, 678, 679, 5, 121, 0, 0, 679, 680, 5, 116, 0, 0, 680, 681, 5, 101, 0, 0, 681, 682, 5, 115, 0, 0, 682, 683, 1, 0, 0, 0, 683, 689, 3, 141, 70, 0, 684, 685, 5, 98, 0, 0, 685, 686, 5, 121, 0, 0, 686, 687, 5, 116, 0, 0, 687, 689, 5, 101, 0, 0, 688, 677, 1, 0, 0, 0, 688, 684, 1, 0, 0, 0, 689, 140, 1, 0, 0, 0, 690, 694, 7, 2, 0, 0, 691, 693, 7, 0, 0, 0, 692, 691, 1, 0, 0, 0, 693, 696, 1, 0, 0, 0, 694, 692, 1, 0, 0, 0, 694, 695, 1, 0, 0, 0, 695, 142, 1, 0, 0, 0, 696, 694, 1, 0, 0, 0, 697, 703, 5, 34, 0, 0, 698, 699, 5, 92, 0, 0, 699, 702, 5, 34, 0, 0, 700, 702, 8, 3, 0, 0, 701, 698, 1, 0, 0, 0, 701, 700, 1, 0, 0, 0, 702, 705, 1, 0, 0, 0, 703, 704, 1, 0, 0, 0, 703, 701, 1, 0, 0, 0, 704, 706, 1, 0, 0, 0, 705, 703, 1, 0, 0, 0, 706, 718, 5, 34, 0, 0, 707, 713, 5, 39, 0, 0, 708, 709, 5, 92, 0, 0, 709, 712, 5, 39, 0, 0, 710, 712, 8, 4, 0, 0, 711, 708, 1, 0, 0, 0, 711, 710, 1, 0, 0, 0, 712, 715, 1, 0, 0, 0, 713, 714, 1, 0, 0, 0, 713, 711, 1, 0, 0, 0, 714, 716, 1, 0, 0, 0, 715, 713, 1, 0, 0, 0, 716, 718, 5, 39, 0, 0, 717, 697, 1, 0, 0, 0, 717, 707, 1, 0, 0, 0, 718, 144, 1, 0, 0, 0, 719, 720, 5, 100, 0, 0, 720, 721, 5, 97, 0, 0, 721, 722, 5, 116, 0, 0, 722, 723, 5, 101, 0, 0, 723, 724, 5, 40, 0, 0, 724, 725, 1, 0, 0, 0, 725, 726, 3, 143, 71, 0, 726, 727, 5, 41, 0, 0, 727, 146, 1, 0, 0, 0, 728, 729, 5, 48, 0, 0, 729, 733, 7, 5, 0, 0, 730, 732, 7, 6, 0, 0, 731, 730, 1, 0, 0, 0, 732, 735, 1, 0, 0, 0, 733, 731, 1, 0, 0, 0, 733, 734, 1, 0, 0, 0, 734, 148, 1, 0, 0, 0, 735, 733, 1, 0, 0, 0, 736, 737, 5, 116, 0, 0, 737, 738, 5, 104, 0, 0, 738, 739, 5, 105, 0, 0, 739, 740, 5, 115, 0, 0, 740, 741, 5, 46, 0, 0, 741, 742, 5, 97, 0, 0, 742, 743, 5, 103, 0, 0, 743, 752, 5, 101, 0, 0, 744, 745, 5, 116, 0, 0, 745, 746, 5, 120, 0, 0, 746, 747, 5, 46, 0, 0, 747, 748, 5, 116, 0, 0, 748, 749, 5, 105, 0, 0, 749, 750, 5, 109, 0, 0, 750, 752, 5, 101, 0, 0, 751, 736, 1, 0, 0, 0, 751, 744, 1, 0, 0, 0, 752, 150, 1, 0, 0, 0, 753, 754, 5, 117, 0, 0, 754, 755, 5, 110, 0, 0, 755, 756, 5, 115, 0, 0, 756, 757, 5, 97, 0, 0, 757, 758, 5, 102, 0, 0, 758, 759, 5, 101, 0, 0, 759, 760, 5, 95, 0, 0, 760, 761, 5, 105, 0, 0, 761, 762, 5, 110, 0, 0, 762, 802, 5, 116, 0, 0, 763, 764, 5, 117, 0, 0, 764, 765, 5, 110, 0, 0, 765, 766, 5, 115, 0, 0, 766, 767, 5, 97, 0, 0, 767, 768, 5, 102, 0, 0, 768, 769, 5, 101, 0, 0, 769, 770, 5, 95, 0, 0, 770, 771, 5, 98, 0, 0, 771, 772, 5, 111, 0, 0, 772, 773, 5, 111, 0, 0, 773, 802, 5, 108, 0, 0, 774, 775, 5, 117, 0, 0, 775, 776, 5, 110, 0, 0, 776, 777, 5, 115, 0, 0, 777, 778, 5, 97, 0, 0, 778, 779, 5, 102, 0, 0, 779, 780, 5, 101, 0, 0, 780, 781, 5, 95, 0, 0, 781, 782, 5, 98, 0, 0, 782, 783, 5, 121, 0, 0, 783, 784, 5, 116, 0, 0, 784, 785, 5, 101, 0, 0, 785, 786, 5, 115, 0, 0, 786, 788, 1, 0, 0, 0, 787, 789, 3, 141, 70, 0, 788, 787, 1, 0, 0, 0, 788, 789, 1, 0, 0, 0, 789, 802, 1, 0, 0, 0, 790, 791, 5, 117, 0, 0, 791, 792, 5, 110, 0, 0, 792, 793, 5, 115, 0, 0, 793, 794, 5, 97, 0, 0, 794, 795, 5, 102, 0, 0, 795, 796, 5, 101, 0, 0, 796, 797, 5, 95, 0, 0, 797, 798, 5, 98, 0, 0, 798, 799, 5, 121, 0, 0, 799, 800, 5, 116, 0, 0, 800, 802, 5, 101, 0, 0, 801, 753, 1, 0, 0, 0, 801, 763, 1, 0, 0, 0, 801, 774, 1, 0, 0, 0, 801, 790, 1, 0, 0, 0, 802, 152, 1, 0, 0, 0, 803, 804, 5, 116, 0, 0, 804, 805, 5, 104, 0, 0, 805, 806, 5, 105, 0, 0, 806, 807, 5, 115, 0, 0, 807, 808, 5, 46, 0, 0, 808, 809, 5, 97, 0, 0, 809, 810, 5, 99, 0, 0, 810, 811, 5, 116, 0, 0, 811, 812, 5, 105, 0, 0, 812, 813, 5, 118, 0, 0, 813, 814, 5, 101, 0, 0, 814, 815, 5, 73, 0, 0, 815, 816, 5, 110, 0, 0, 816, 817, 5, 112, 0, 0, 817, 818, 5, 117, 0, 0, 818, 819, 5, 116, 0, 0, 819, 820, 5, 73, 0, 0, 820, 821, 5, 110, 0, 0, 821, 822, 5, 100, 0, 0, 822, 823, 5, 101, 0, 0, 823, 898, 5, 120, 0, 0, 824, 825, 5, 116, 0, 0, 825, 826, 5, 104, 0, 0, 826, 827, 5, 105, 0, 0, 827, 828, 5, 115, 0, 0, 828, 829, 5, 46, 0, 0, 829, 830, 5, 97, 0, 0, 830, 831, 5, 99, 0, 0, 831, 832, 5, 116, 0, 0, 832, 833, 5, 105, 0, 0, 833, 834, 5, 118, 0, 0, 834, 835, 5, 101, 0, 0, 835, 836, 5, 66, 0, 0, 836, 837, 5, 121, 0, 0, 837, 838, 5, 116, 0, 0, 838, 839, 5, 101, 0, 0, 839, 840, 5, 99, 0, 0, 840, 841, 5, 111, 0, 0, 841, 842, 5, 100, 0, 0, 842, 898, 5, 101, 0, 0, 843, 844, 5, 116, 0, 0, 844, 845, 5, 120, 0, 0, 845, 846, 5, 46, 0, 0, 846, 847, 5, 105, 0, 0, 847, 848, 5, 110, 0, 0, 848, 849, 5, 112, 0, 0, 849, 850, 5, 117, 0, 0, 850, 851, 5, 116, 0, 0, 851, 852, 5, 115, 0, 0, 852, 853, 5, 46, 0, 0, 853, 854, 5, 108, 0, 0, 854, 855, 5, 101, 0, 0, 855, 856, 5, 110, 0, 0, 856, 857, 5, 103, 0, 0, 857, 858, 5, 116, 0, 0, 858, 898, 5, 104, 0, 0, 859, 860, 5, 116, 0, 0, 860, 861, 5, 120, 0, 0, 861, 862, 5, 46, 0, 0, 862, 863, 5, 111, 0, 0, 863, 864, 5, 117, 0, 0, 864, 865, 5, 116, 0, 0, 865, 866, 5, 112, 0, 0, 866, 867, 5, 117, 0, 0, 867, 868, 5, 116, 0, 0, 868, 869, 5, 115, 0, 0, 869, 870, 5, 46, 0, 0, 870, 871, 5, 108, 0, 0, 871, 872, 5, 101, 0, 0, 872, 873, 5, 110, 0, 0, 873, 874, 5, 103, 0, 0, 874, 875, 5, 116, 0, 0, 875, 898, 5, 104, 0, 0, 876, 877, 5, 116, 0, 0, 877, 878, 5, 120, 0, 0, 878, 879, 5, 46, 0, 0, 879, 880, 5, 118, 0, 0, 880, 881, 5, 101, 0, 0, 881, 882, 5, 114, 0, 0, 882, 883, 5, 115, 0, 0, 883, 884, 5, 105, 0, 0, 884, 885, 5, 111, 0, 0, 885, 898, 5, 110, 0, 0, 886, 887, 5, 116, 0, 0, 887, 888, 5, 120, 0, 0, 888, 889, 5, 46, 0, 0, 889, 890, 5, 108, 0, 0, 890, 891, 5, 111, 0, 0, 891, 892, 5, 99, 0, 0, 892, 893, 5, 107, 0, 0, 893, 894, 5, 116, 0, 0, 894, 895, 5, 105, 0, 0, 895, 896, 5, 109, 0, 0, 896, 898, 5, 101, 0, 0, 897, 803, 1, 0, 0, 0, 897, 824, 1, 0, 0, 0, 897, 843, 1, 0, 0, 0, 897, 859, 1, 0, 0, 0, 897, 876, 1, 0, 0, 0, 897, 886, 1, 0, 0, 0, 898, 154, 1, 0, 0, 0, 899, 903, 7, 7, 0, 0, 900, 902, 7, 8, 0, 0, 901, 900, 1, 0, 0, 0, 902, 905, 1, 0, 0, 0, 903, 901, 1, 0, 0, 0, 903, 904, 1, 0, 0, 0, 904, 156, 1, 0, 0, 0, 905, 903, 1, 0, 0, 0, 906, 908, 7, 9, 0, 0, 907, 906, 1, 0, 0, 0, 908, 909, 1, 0, 0, 0, 909, 907, 1, 0, 0, 0, 909, 910, 1, 0, 0, 0, 910, 911, 1, 0, 0, 0, 911, 912, 6, 78, 0, 0, 912, 158, 1, 0, 0, 0, 913, 914, 5, 47, 0, 0, 914, 915, 5, 42, 0, 0, 915, 919, 1, 0, 0, 0, 916, 918, 9, 0, 0, 0, 917, 916, 1, 0, 0, 0, 918, 921, 1, 0, 0, 0, 919, 920, 1, 0, 0, 0, 919, 917, 1, 0, 0, 0, 920, 922, 1, 0, 0, 0, 921, 919, 1, 0, 0, 0, 922, 923, 5, 42, 0, 0, 923, 924, 5, 47, 0, 0, 924, 925, 1, 0, 0, 0, 925, 926, 6, 79, 1, 0, 926, 160, 1, 0, 0, 0, 927, 928, 5, 47, 0, 0, 928, 929, 5, 47, 0, 0, 929, 933, 1, 0, 0, 0, 930, 932, 8, 10, 0, 0, 931, 930, 1, 0, 0, 0, 932, 935, 1, 0, 0, 0, 933, 931, 1, 0, 0, 0, 933, 934, 1, 0, 0, 0, 934, 936, 1, 0, 0, 0, 935, 933, 1, 0, 0, 0, 936, 937, 6, 80, 1, 0, 937, 162, 1, 0, 0, 0, 28, 0, 530, 536, 542, 553, 612, 615, 619, 624, 630, 634, 669, 688, 694, 701, 703, 711, 713, 717, 733, 751, 788, 801, 897, 903, 909, 919, 933, 2, 6, 0, 0, 0, 1, 0] \ No newline at end of file +[4, 0, 85, 975, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 51, 1, 51, 1, 52, 1, 52, 1, 53, 1, 53, 1, 54, 1, 54, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 61, 1, 61, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 65, 4, 65, 566, 8, 65, 11, 65, 12, 65, 567, 1, 65, 1, 65, 4, 65, 572, 8, 65, 11, 65, 12, 65, 573, 1, 65, 1, 65, 4, 65, 578, 8, 65, 11, 65, 12, 65, 579, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 3, 66, 591, 8, 66, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 3, 67, 650, 8, 67, 1, 68, 3, 68, 653, 8, 68, 1, 68, 1, 68, 3, 68, 657, 8, 68, 1, 69, 4, 69, 660, 8, 69, 11, 69, 12, 69, 661, 1, 69, 1, 69, 4, 69, 666, 8, 69, 11, 69, 12, 69, 667, 5, 69, 670, 8, 69, 10, 69, 12, 69, 673, 9, 69, 1, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 3, 71, 707, 8, 71, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 3, 73, 726, 8, 73, 1, 74, 1, 74, 5, 74, 730, 8, 74, 10, 74, 12, 74, 733, 9, 74, 1, 75, 1, 75, 1, 75, 1, 75, 5, 75, 739, 8, 75, 10, 75, 12, 75, 742, 9, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 5, 75, 749, 8, 75, 10, 75, 12, 75, 752, 9, 75, 1, 75, 3, 75, 755, 8, 75, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 77, 1, 77, 1, 77, 5, 77, 769, 8, 77, 10, 77, 12, 77, 772, 9, 77, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 3, 78, 789, 8, 78, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 3, 79, 826, 8, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 3, 79, 839, 8, 79, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 3, 80, 935, 8, 80, 1, 81, 1, 81, 5, 81, 939, 8, 81, 10, 81, 12, 81, 942, 9, 81, 1, 82, 4, 82, 945, 8, 82, 11, 82, 12, 82, 946, 1, 82, 1, 82, 1, 83, 1, 83, 1, 83, 1, 83, 5, 83, 955, 8, 83, 10, 83, 12, 83, 958, 9, 83, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 1, 84, 1, 84, 1, 84, 1, 84, 5, 84, 969, 8, 84, 10, 84, 12, 84, 972, 9, 84, 1, 84, 1, 84, 3, 740, 750, 956, 0, 85, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, 35, 71, 36, 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, 42, 85, 43, 87, 44, 89, 45, 91, 46, 93, 47, 95, 48, 97, 49, 99, 50, 101, 51, 103, 52, 105, 53, 107, 54, 109, 55, 111, 56, 113, 57, 115, 58, 117, 59, 119, 60, 121, 61, 123, 62, 125, 63, 127, 64, 129, 65, 131, 66, 133, 67, 135, 68, 137, 69, 139, 70, 141, 71, 143, 72, 145, 73, 147, 74, 149, 75, 151, 76, 153, 77, 155, 78, 157, 79, 159, 80, 161, 81, 163, 82, 165, 83, 167, 84, 169, 85, 1, 0, 11, 1, 0, 48, 57, 2, 0, 69, 69, 101, 101, 1, 0, 49, 57, 3, 0, 10, 10, 13, 13, 34, 34, 3, 0, 10, 10, 13, 13, 39, 39, 2, 0, 88, 88, 120, 120, 3, 0, 48, 57, 65, 70, 97, 102, 2, 0, 65, 90, 97, 122, 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 3, 0, 9, 10, 12, 13, 32, 32, 2, 0, 10, 10, 13, 13, 1019, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0, 0, 0, 95, 1, 0, 0, 0, 0, 97, 1, 0, 0, 0, 0, 99, 1, 0, 0, 0, 0, 101, 1, 0, 0, 0, 0, 103, 1, 0, 0, 0, 0, 105, 1, 0, 0, 0, 0, 107, 1, 0, 0, 0, 0, 109, 1, 0, 0, 0, 0, 111, 1, 0, 0, 0, 0, 113, 1, 0, 0, 0, 0, 115, 1, 0, 0, 0, 0, 117, 1, 0, 0, 0, 0, 119, 1, 0, 0, 0, 0, 121, 1, 0, 0, 0, 0, 123, 1, 0, 0, 0, 0, 125, 1, 0, 0, 0, 0, 127, 1, 0, 0, 0, 0, 129, 1, 0, 0, 0, 0, 131, 1, 0, 0, 0, 0, 133, 1, 0, 0, 0, 0, 135, 1, 0, 0, 0, 0, 137, 1, 0, 0, 0, 0, 139, 1, 0, 0, 0, 0, 141, 1, 0, 0, 0, 0, 143, 1, 0, 0, 0, 0, 145, 1, 0, 0, 0, 0, 147, 1, 0, 0, 0, 0, 149, 1, 0, 0, 0, 0, 151, 1, 0, 0, 0, 0, 153, 1, 0, 0, 0, 0, 155, 1, 0, 0, 0, 0, 157, 1, 0, 0, 0, 0, 159, 1, 0, 0, 0, 0, 161, 1, 0, 0, 0, 0, 163, 1, 0, 0, 0, 0, 165, 1, 0, 0, 0, 0, 167, 1, 0, 0, 0, 0, 169, 1, 0, 0, 0, 1, 171, 1, 0, 0, 0, 3, 178, 1, 0, 0, 0, 5, 180, 1, 0, 0, 0, 7, 191, 1, 0, 0, 0, 9, 193, 1, 0, 0, 0, 11, 195, 1, 0, 0, 0, 13, 198, 1, 0, 0, 0, 15, 200, 1, 0, 0, 0, 17, 202, 1, 0, 0, 0, 19, 205, 1, 0, 0, 0, 21, 207, 1, 0, 0, 0, 23, 214, 1, 0, 0, 0, 25, 223, 1, 0, 0, 0, 27, 231, 1, 0, 0, 0, 29, 233, 1, 0, 0, 0, 31, 235, 1, 0, 0, 0, 33, 237, 1, 0, 0, 0, 35, 246, 1, 0, 0, 0, 37, 255, 1, 0, 0, 0, 39, 257, 1, 0, 0, 0, 41, 259, 1, 0, 0, 0, 43, 266, 1, 0, 0, 0, 45, 269, 1, 0, 0, 0, 47, 272, 1, 0, 0, 0, 49, 275, 1, 0, 0, 0, 51, 278, 1, 0, 0, 0, 53, 286, 1, 0, 0, 0, 55, 298, 1, 0, 0, 0, 57, 301, 1, 0, 0, 0, 59, 306, 1, 0, 0, 0, 61, 309, 1, 0, 0, 0, 63, 315, 1, 0, 0, 0, 65, 319, 1, 0, 0, 0, 67, 323, 1, 0, 0, 0, 69, 325, 1, 0, 0, 0, 71, 327, 1, 0, 0, 0, 73, 338, 1, 0, 0, 0, 75, 345, 1, 0, 0, 0, 77, 362, 1, 0, 0, 0, 79, 377, 1, 0, 0, 0, 81, 392, 1, 0, 0, 0, 83, 405, 1, 0, 0, 0, 85, 415, 1, 0, 0, 0, 87, 440, 1, 0, 0, 0, 89, 455, 1, 0, 0, 0, 91, 474, 1, 0, 0, 0, 93, 490, 1, 0, 0, 0, 95, 501, 1, 0, 0, 0, 97, 509, 1, 0, 0, 0, 99, 516, 1, 0, 0, 0, 101, 523, 1, 0, 0, 0, 103, 525, 1, 0, 0, 0, 105, 527, 1, 0, 0, 0, 107, 529, 1, 0, 0, 0, 109, 531, 1, 0, 0, 0, 111, 533, 1, 0, 0, 0, 113, 535, 1, 0, 0, 0, 115, 538, 1, 0, 0, 0, 117, 541, 1, 0, 0, 0, 119, 544, 1, 0, 0, 0, 121, 547, 1, 0, 0, 0, 123, 549, 1, 0, 0, 0, 125, 551, 1, 0, 0, 0, 127, 554, 1, 0, 0, 0, 129, 557, 1, 0, 0, 0, 131, 565, 1, 0, 0, 0, 133, 590, 1, 0, 0, 0, 135, 649, 1, 0, 0, 0, 137, 652, 1, 0, 0, 0, 139, 659, 1, 0, 0, 0, 141, 674, 1, 0, 0, 0, 143, 706, 1, 0, 0, 0, 145, 708, 1, 0, 0, 0, 147, 725, 1, 0, 0, 0, 149, 727, 1, 0, 0, 0, 151, 754, 1, 0, 0, 0, 153, 756, 1, 0, 0, 0, 155, 765, 1, 0, 0, 0, 157, 788, 1, 0, 0, 0, 159, 838, 1, 0, 0, 0, 161, 934, 1, 0, 0, 0, 163, 936, 1, 0, 0, 0, 165, 944, 1, 0, 0, 0, 167, 950, 1, 0, 0, 0, 169, 964, 1, 0, 0, 0, 171, 172, 5, 112, 0, 0, 172, 173, 5, 114, 0, 0, 173, 174, 5, 97, 0, 0, 174, 175, 5, 103, 0, 0, 175, 176, 5, 109, 0, 0, 176, 177, 5, 97, 0, 0, 177, 2, 1, 0, 0, 0, 178, 179, 5, 59, 0, 0, 179, 4, 1, 0, 0, 0, 180, 181, 5, 99, 0, 0, 181, 182, 5, 97, 0, 0, 182, 183, 5, 115, 0, 0, 183, 184, 5, 104, 0, 0, 184, 185, 5, 115, 0, 0, 185, 186, 5, 99, 0, 0, 186, 187, 5, 114, 0, 0, 187, 188, 5, 105, 0, 0, 188, 189, 5, 112, 0, 0, 189, 190, 5, 116, 0, 0, 190, 6, 1, 0, 0, 0, 191, 192, 5, 94, 0, 0, 192, 8, 1, 0, 0, 0, 193, 194, 5, 126, 0, 0, 194, 10, 1, 0, 0, 0, 195, 196, 5, 62, 0, 0, 196, 197, 5, 61, 0, 0, 197, 12, 1, 0, 0, 0, 198, 199, 5, 62, 0, 0, 199, 14, 1, 0, 0, 0, 200, 201, 5, 60, 0, 0, 201, 16, 1, 0, 0, 0, 202, 203, 5, 60, 0, 0, 203, 204, 5, 61, 0, 0, 204, 18, 1, 0, 0, 0, 205, 206, 5, 61, 0, 0, 206, 20, 1, 0, 0, 0, 207, 208, 5, 105, 0, 0, 208, 209, 5, 109, 0, 0, 209, 210, 5, 112, 0, 0, 210, 211, 5, 111, 0, 0, 211, 212, 5, 114, 0, 0, 212, 213, 5, 116, 0, 0, 213, 22, 1, 0, 0, 0, 214, 215, 5, 102, 0, 0, 215, 216, 5, 117, 0, 0, 216, 217, 5, 110, 0, 0, 217, 218, 5, 99, 0, 0, 218, 219, 5, 116, 0, 0, 219, 220, 5, 105, 0, 0, 220, 221, 5, 111, 0, 0, 221, 222, 5, 110, 0, 0, 222, 24, 1, 0, 0, 0, 223, 224, 5, 114, 0, 0, 224, 225, 5, 101, 0, 0, 225, 226, 5, 116, 0, 0, 226, 227, 5, 117, 0, 0, 227, 228, 5, 114, 0, 0, 228, 229, 5, 110, 0, 0, 229, 230, 5, 115, 0, 0, 230, 26, 1, 0, 0, 0, 231, 232, 5, 40, 0, 0, 232, 28, 1, 0, 0, 0, 233, 234, 5, 44, 0, 0, 234, 30, 1, 0, 0, 0, 235, 236, 5, 41, 0, 0, 236, 32, 1, 0, 0, 0, 237, 238, 5, 99, 0, 0, 238, 239, 5, 111, 0, 0, 239, 240, 5, 110, 0, 0, 240, 241, 5, 115, 0, 0, 241, 242, 5, 116, 0, 0, 242, 243, 5, 97, 0, 0, 243, 244, 5, 110, 0, 0, 244, 245, 5, 116, 0, 0, 245, 34, 1, 0, 0, 0, 246, 247, 5, 99, 0, 0, 247, 248, 5, 111, 0, 0, 248, 249, 5, 110, 0, 0, 249, 250, 5, 116, 0, 0, 250, 251, 5, 114, 0, 0, 251, 252, 5, 97, 0, 0, 252, 253, 5, 99, 0, 0, 253, 254, 5, 116, 0, 0, 254, 36, 1, 0, 0, 0, 255, 256, 5, 123, 0, 0, 256, 38, 1, 0, 0, 0, 257, 258, 5, 125, 0, 0, 258, 40, 1, 0, 0, 0, 259, 260, 5, 114, 0, 0, 260, 261, 5, 101, 0, 0, 261, 262, 5, 116, 0, 0, 262, 263, 5, 117, 0, 0, 263, 264, 5, 114, 0, 0, 264, 265, 5, 110, 0, 0, 265, 42, 1, 0, 0, 0, 266, 267, 5, 43, 0, 0, 267, 268, 5, 61, 0, 0, 268, 44, 1, 0, 0, 0, 269, 270, 5, 45, 0, 0, 270, 271, 5, 61, 0, 0, 271, 46, 1, 0, 0, 0, 272, 273, 5, 43, 0, 0, 273, 274, 5, 43, 0, 0, 274, 48, 1, 0, 0, 0, 275, 276, 5, 45, 0, 0, 276, 277, 5, 45, 0, 0, 277, 50, 1, 0, 0, 0, 278, 279, 5, 114, 0, 0, 279, 280, 5, 101, 0, 0, 280, 281, 5, 113, 0, 0, 281, 282, 5, 117, 0, 0, 282, 283, 5, 105, 0, 0, 283, 284, 5, 114, 0, 0, 284, 285, 5, 101, 0, 0, 285, 52, 1, 0, 0, 0, 286, 287, 5, 99, 0, 0, 287, 288, 5, 111, 0, 0, 288, 289, 5, 110, 0, 0, 289, 290, 5, 115, 0, 0, 290, 291, 5, 111, 0, 0, 291, 292, 5, 108, 0, 0, 292, 293, 5, 101, 0, 0, 293, 294, 5, 46, 0, 0, 294, 295, 5, 108, 0, 0, 295, 296, 5, 111, 0, 0, 296, 297, 5, 103, 0, 0, 297, 54, 1, 0, 0, 0, 298, 299, 5, 105, 0, 0, 299, 300, 5, 102, 0, 0, 300, 56, 1, 0, 0, 0, 301, 302, 5, 101, 0, 0, 302, 303, 5, 108, 0, 0, 303, 304, 5, 115, 0, 0, 304, 305, 5, 101, 0, 0, 305, 58, 1, 0, 0, 0, 306, 307, 5, 100, 0, 0, 307, 308, 5, 111, 0, 0, 308, 60, 1, 0, 0, 0, 309, 310, 5, 119, 0, 0, 310, 311, 5, 104, 0, 0, 311, 312, 5, 105, 0, 0, 312, 313, 5, 108, 0, 0, 313, 314, 5, 101, 0, 0, 314, 62, 1, 0, 0, 0, 315, 316, 5, 102, 0, 0, 316, 317, 5, 111, 0, 0, 317, 318, 5, 114, 0, 0, 318, 64, 1, 0, 0, 0, 319, 320, 5, 110, 0, 0, 320, 321, 5, 101, 0, 0, 321, 322, 5, 119, 0, 0, 322, 66, 1, 0, 0, 0, 323, 324, 5, 91, 0, 0, 324, 68, 1, 0, 0, 0, 325, 326, 5, 93, 0, 0, 326, 70, 1, 0, 0, 0, 327, 328, 5, 116, 0, 0, 328, 329, 5, 120, 0, 0, 329, 330, 5, 46, 0, 0, 330, 331, 5, 111, 0, 0, 331, 332, 5, 117, 0, 0, 332, 333, 5, 116, 0, 0, 333, 334, 5, 112, 0, 0, 334, 335, 5, 117, 0, 0, 335, 336, 5, 116, 0, 0, 336, 337, 5, 115, 0, 0, 337, 72, 1, 0, 0, 0, 338, 339, 5, 46, 0, 0, 339, 340, 5, 118, 0, 0, 340, 341, 5, 97, 0, 0, 341, 342, 5, 108, 0, 0, 342, 343, 5, 117, 0, 0, 343, 344, 5, 101, 0, 0, 344, 74, 1, 0, 0, 0, 345, 346, 5, 46, 0, 0, 346, 347, 5, 108, 0, 0, 347, 348, 5, 111, 0, 0, 348, 349, 5, 99, 0, 0, 349, 350, 5, 107, 0, 0, 350, 351, 5, 105, 0, 0, 351, 352, 5, 110, 0, 0, 352, 353, 5, 103, 0, 0, 353, 354, 5, 66, 0, 0, 354, 355, 5, 121, 0, 0, 355, 356, 5, 116, 0, 0, 356, 357, 5, 101, 0, 0, 357, 358, 5, 99, 0, 0, 358, 359, 5, 111, 0, 0, 359, 360, 5, 100, 0, 0, 360, 361, 5, 101, 0, 0, 361, 76, 1, 0, 0, 0, 362, 363, 5, 46, 0, 0, 363, 364, 5, 116, 0, 0, 364, 365, 5, 111, 0, 0, 365, 366, 5, 107, 0, 0, 366, 367, 5, 101, 0, 0, 367, 368, 5, 110, 0, 0, 368, 369, 5, 67, 0, 0, 369, 370, 5, 97, 0, 0, 370, 371, 5, 116, 0, 0, 371, 372, 5, 101, 0, 0, 372, 373, 5, 103, 0, 0, 373, 374, 5, 111, 0, 0, 374, 375, 5, 114, 0, 0, 375, 376, 5, 121, 0, 0, 376, 78, 1, 0, 0, 0, 377, 378, 5, 46, 0, 0, 378, 379, 5, 110, 0, 0, 379, 380, 5, 102, 0, 0, 380, 381, 5, 116, 0, 0, 381, 382, 5, 67, 0, 0, 382, 383, 5, 111, 0, 0, 383, 384, 5, 109, 0, 0, 384, 385, 5, 109, 0, 0, 385, 386, 5, 105, 0, 0, 386, 387, 5, 116, 0, 0, 387, 388, 5, 109, 0, 0, 388, 389, 5, 101, 0, 0, 389, 390, 5, 110, 0, 0, 390, 391, 5, 116, 0, 0, 391, 80, 1, 0, 0, 0, 392, 393, 5, 46, 0, 0, 393, 394, 5, 116, 0, 0, 394, 395, 5, 111, 0, 0, 395, 396, 5, 107, 0, 0, 396, 397, 5, 101, 0, 0, 397, 398, 5, 110, 0, 0, 398, 399, 5, 65, 0, 0, 399, 400, 5, 109, 0, 0, 400, 401, 5, 111, 0, 0, 401, 402, 5, 117, 0, 0, 402, 403, 5, 110, 0, 0, 403, 404, 5, 116, 0, 0, 404, 82, 1, 0, 0, 0, 405, 406, 5, 116, 0, 0, 406, 407, 5, 120, 0, 0, 407, 408, 5, 46, 0, 0, 408, 409, 5, 105, 0, 0, 409, 410, 5, 110, 0, 0, 410, 411, 5, 112, 0, 0, 411, 412, 5, 117, 0, 0, 412, 413, 5, 116, 0, 0, 413, 414, 5, 115, 0, 0, 414, 84, 1, 0, 0, 0, 415, 416, 5, 46, 0, 0, 416, 417, 5, 111, 0, 0, 417, 418, 5, 117, 0, 0, 418, 419, 5, 116, 0, 0, 419, 420, 5, 112, 0, 0, 420, 421, 5, 111, 0, 0, 421, 422, 5, 105, 0, 0, 422, 423, 5, 110, 0, 0, 423, 424, 5, 116, 0, 0, 424, 425, 5, 84, 0, 0, 425, 426, 5, 114, 0, 0, 426, 427, 5, 97, 0, 0, 427, 428, 5, 110, 0, 0, 428, 429, 5, 115, 0, 0, 429, 430, 5, 97, 0, 0, 430, 431, 5, 99, 0, 0, 431, 432, 5, 116, 0, 0, 432, 433, 5, 105, 0, 0, 433, 434, 5, 111, 0, 0, 434, 435, 5, 110, 0, 0, 435, 436, 5, 72, 0, 0, 436, 437, 5, 97, 0, 0, 437, 438, 5, 115, 0, 0, 438, 439, 5, 104, 0, 0, 439, 86, 1, 0, 0, 0, 440, 441, 5, 46, 0, 0, 441, 442, 5, 111, 0, 0, 442, 443, 5, 117, 0, 0, 443, 444, 5, 116, 0, 0, 444, 445, 5, 112, 0, 0, 445, 446, 5, 111, 0, 0, 446, 447, 5, 105, 0, 0, 447, 448, 5, 110, 0, 0, 448, 449, 5, 116, 0, 0, 449, 450, 5, 73, 0, 0, 450, 451, 5, 110, 0, 0, 451, 452, 5, 100, 0, 0, 452, 453, 5, 101, 0, 0, 453, 454, 5, 120, 0, 0, 454, 88, 1, 0, 0, 0, 455, 456, 5, 46, 0, 0, 456, 457, 5, 117, 0, 0, 457, 458, 5, 110, 0, 0, 458, 459, 5, 108, 0, 0, 459, 460, 5, 111, 0, 0, 460, 461, 5, 99, 0, 0, 461, 462, 5, 107, 0, 0, 462, 463, 5, 105, 0, 0, 463, 464, 5, 110, 0, 0, 464, 465, 5, 103, 0, 0, 465, 466, 5, 66, 0, 0, 466, 467, 5, 121, 0, 0, 467, 468, 5, 116, 0, 0, 468, 469, 5, 101, 0, 0, 469, 470, 5, 99, 0, 0, 470, 471, 5, 111, 0, 0, 471, 472, 5, 100, 0, 0, 472, 473, 5, 101, 0, 0, 473, 90, 1, 0, 0, 0, 474, 475, 5, 46, 0, 0, 475, 476, 5, 115, 0, 0, 476, 477, 5, 101, 0, 0, 477, 478, 5, 113, 0, 0, 478, 479, 5, 117, 0, 0, 479, 480, 5, 101, 0, 0, 480, 481, 5, 110, 0, 0, 481, 482, 5, 99, 0, 0, 482, 483, 5, 101, 0, 0, 483, 484, 5, 78, 0, 0, 484, 485, 5, 117, 0, 0, 485, 486, 5, 109, 0, 0, 486, 487, 5, 98, 0, 0, 487, 488, 5, 101, 0, 0, 488, 489, 5, 114, 0, 0, 489, 92, 1, 0, 0, 0, 490, 491, 5, 46, 0, 0, 491, 492, 5, 114, 0, 0, 492, 493, 5, 101, 0, 0, 493, 494, 5, 118, 0, 0, 494, 495, 5, 101, 0, 0, 495, 496, 5, 114, 0, 0, 496, 497, 5, 115, 0, 0, 497, 498, 5, 101, 0, 0, 498, 499, 5, 40, 0, 0, 499, 500, 5, 41, 0, 0, 500, 94, 1, 0, 0, 0, 501, 502, 5, 46, 0, 0, 502, 503, 5, 108, 0, 0, 503, 504, 5, 101, 0, 0, 504, 505, 5, 110, 0, 0, 505, 506, 5, 103, 0, 0, 506, 507, 5, 116, 0, 0, 507, 508, 5, 104, 0, 0, 508, 96, 1, 0, 0, 0, 509, 510, 5, 46, 0, 0, 510, 511, 5, 115, 0, 0, 511, 512, 5, 112, 0, 0, 512, 513, 5, 108, 0, 0, 513, 514, 5, 105, 0, 0, 514, 515, 5, 116, 0, 0, 515, 98, 1, 0, 0, 0, 516, 517, 5, 46, 0, 0, 517, 518, 5, 115, 0, 0, 518, 519, 5, 108, 0, 0, 519, 520, 5, 105, 0, 0, 520, 521, 5, 99, 0, 0, 521, 522, 5, 101, 0, 0, 522, 100, 1, 0, 0, 0, 523, 524, 5, 33, 0, 0, 524, 102, 1, 0, 0, 0, 525, 526, 5, 45, 0, 0, 526, 104, 1, 0, 0, 0, 527, 528, 5, 42, 0, 0, 528, 106, 1, 0, 0, 0, 529, 530, 5, 47, 0, 0, 530, 108, 1, 0, 0, 0, 531, 532, 5, 37, 0, 0, 532, 110, 1, 0, 0, 0, 533, 534, 5, 43, 0, 0, 534, 112, 1, 0, 0, 0, 535, 536, 5, 62, 0, 0, 536, 537, 5, 62, 0, 0, 537, 114, 1, 0, 0, 0, 538, 539, 5, 60, 0, 0, 539, 540, 5, 60, 0, 0, 540, 116, 1, 0, 0, 0, 541, 542, 5, 61, 0, 0, 542, 543, 5, 61, 0, 0, 543, 118, 1, 0, 0, 0, 544, 545, 5, 33, 0, 0, 545, 546, 5, 61, 0, 0, 546, 120, 1, 0, 0, 0, 547, 548, 5, 38, 0, 0, 548, 122, 1, 0, 0, 0, 549, 550, 5, 124, 0, 0, 550, 124, 1, 0, 0, 0, 551, 552, 5, 38, 0, 0, 552, 553, 5, 38, 0, 0, 553, 126, 1, 0, 0, 0, 554, 555, 5, 124, 0, 0, 555, 556, 5, 124, 0, 0, 556, 128, 1, 0, 0, 0, 557, 558, 5, 117, 0, 0, 558, 559, 5, 110, 0, 0, 559, 560, 5, 117, 0, 0, 560, 561, 5, 115, 0, 0, 561, 562, 5, 101, 0, 0, 562, 563, 5, 100, 0, 0, 563, 130, 1, 0, 0, 0, 564, 566, 7, 0, 0, 0, 565, 564, 1, 0, 0, 0, 566, 567, 1, 0, 0, 0, 567, 565, 1, 0, 0, 0, 567, 568, 1, 0, 0, 0, 568, 569, 1, 0, 0, 0, 569, 571, 5, 46, 0, 0, 570, 572, 7, 0, 0, 0, 571, 570, 1, 0, 0, 0, 572, 573, 1, 0, 0, 0, 573, 571, 1, 0, 0, 0, 573, 574, 1, 0, 0, 0, 574, 575, 1, 0, 0, 0, 575, 577, 5, 46, 0, 0, 576, 578, 7, 0, 0, 0, 577, 576, 1, 0, 0, 0, 578, 579, 1, 0, 0, 0, 579, 577, 1, 0, 0, 0, 579, 580, 1, 0, 0, 0, 580, 132, 1, 0, 0, 0, 581, 582, 5, 116, 0, 0, 582, 583, 5, 114, 0, 0, 583, 584, 5, 117, 0, 0, 584, 591, 5, 101, 0, 0, 585, 586, 5, 102, 0, 0, 586, 587, 5, 97, 0, 0, 587, 588, 5, 108, 0, 0, 588, 589, 5, 115, 0, 0, 589, 591, 5, 101, 0, 0, 590, 581, 1, 0, 0, 0, 590, 585, 1, 0, 0, 0, 591, 134, 1, 0, 0, 0, 592, 593, 5, 115, 0, 0, 593, 594, 5, 97, 0, 0, 594, 595, 5, 116, 0, 0, 595, 596, 5, 111, 0, 0, 596, 597, 5, 115, 0, 0, 597, 598, 5, 104, 0, 0, 598, 599, 5, 105, 0, 0, 599, 650, 5, 115, 0, 0, 600, 601, 5, 115, 0, 0, 601, 602, 5, 97, 0, 0, 602, 603, 5, 116, 0, 0, 603, 650, 5, 115, 0, 0, 604, 605, 5, 102, 0, 0, 605, 606, 5, 105, 0, 0, 606, 607, 5, 110, 0, 0, 607, 608, 5, 110, 0, 0, 608, 609, 5, 101, 0, 0, 609, 650, 5, 121, 0, 0, 610, 611, 5, 98, 0, 0, 611, 612, 5, 105, 0, 0, 612, 613, 5, 116, 0, 0, 613, 650, 5, 115, 0, 0, 614, 615, 5, 98, 0, 0, 615, 616, 5, 105, 0, 0, 616, 617, 5, 116, 0, 0, 617, 618, 5, 99, 0, 0, 618, 619, 5, 111, 0, 0, 619, 620, 5, 105, 0, 0, 620, 650, 5, 110, 0, 0, 621, 622, 5, 115, 0, 0, 622, 623, 5, 101, 0, 0, 623, 624, 5, 99, 0, 0, 624, 625, 5, 111, 0, 0, 625, 626, 5, 110, 0, 0, 626, 627, 5, 100, 0, 0, 627, 650, 5, 115, 0, 0, 628, 629, 5, 109, 0, 0, 629, 630, 5, 105, 0, 0, 630, 631, 5, 110, 0, 0, 631, 632, 5, 117, 0, 0, 632, 633, 5, 116, 0, 0, 633, 634, 5, 101, 0, 0, 634, 650, 5, 115, 0, 0, 635, 636, 5, 104, 0, 0, 636, 637, 5, 111, 0, 0, 637, 638, 5, 117, 0, 0, 638, 639, 5, 114, 0, 0, 639, 650, 5, 115, 0, 0, 640, 641, 5, 100, 0, 0, 641, 642, 5, 97, 0, 0, 642, 643, 5, 121, 0, 0, 643, 650, 5, 115, 0, 0, 644, 645, 5, 119, 0, 0, 645, 646, 5, 101, 0, 0, 646, 647, 5, 101, 0, 0, 647, 648, 5, 107, 0, 0, 648, 650, 5, 115, 0, 0, 649, 592, 1, 0, 0, 0, 649, 600, 1, 0, 0, 0, 649, 604, 1, 0, 0, 0, 649, 610, 1, 0, 0, 0, 649, 614, 1, 0, 0, 0, 649, 621, 1, 0, 0, 0, 649, 628, 1, 0, 0, 0, 649, 635, 1, 0, 0, 0, 649, 640, 1, 0, 0, 0, 649, 644, 1, 0, 0, 0, 650, 136, 1, 0, 0, 0, 651, 653, 5, 45, 0, 0, 652, 651, 1, 0, 0, 0, 652, 653, 1, 0, 0, 0, 653, 654, 1, 0, 0, 0, 654, 656, 3, 139, 69, 0, 655, 657, 3, 141, 70, 0, 656, 655, 1, 0, 0, 0, 656, 657, 1, 0, 0, 0, 657, 138, 1, 0, 0, 0, 658, 660, 7, 0, 0, 0, 659, 658, 1, 0, 0, 0, 660, 661, 1, 0, 0, 0, 661, 659, 1, 0, 0, 0, 661, 662, 1, 0, 0, 0, 662, 671, 1, 0, 0, 0, 663, 665, 5, 95, 0, 0, 664, 666, 7, 0, 0, 0, 665, 664, 1, 0, 0, 0, 666, 667, 1, 0, 0, 0, 667, 665, 1, 0, 0, 0, 667, 668, 1, 0, 0, 0, 668, 670, 1, 0, 0, 0, 669, 663, 1, 0, 0, 0, 670, 673, 1, 0, 0, 0, 671, 669, 1, 0, 0, 0, 671, 672, 1, 0, 0, 0, 672, 140, 1, 0, 0, 0, 673, 671, 1, 0, 0, 0, 674, 675, 7, 1, 0, 0, 675, 676, 3, 139, 69, 0, 676, 142, 1, 0, 0, 0, 677, 678, 5, 105, 0, 0, 678, 679, 5, 110, 0, 0, 679, 707, 5, 116, 0, 0, 680, 681, 5, 98, 0, 0, 681, 682, 5, 111, 0, 0, 682, 683, 5, 111, 0, 0, 683, 707, 5, 108, 0, 0, 684, 685, 5, 115, 0, 0, 685, 686, 5, 116, 0, 0, 686, 687, 5, 114, 0, 0, 687, 688, 5, 105, 0, 0, 688, 689, 5, 110, 0, 0, 689, 707, 5, 103, 0, 0, 690, 691, 5, 112, 0, 0, 691, 692, 5, 117, 0, 0, 692, 693, 5, 98, 0, 0, 693, 694, 5, 107, 0, 0, 694, 695, 5, 101, 0, 0, 695, 707, 5, 121, 0, 0, 696, 697, 5, 115, 0, 0, 697, 698, 5, 105, 0, 0, 698, 707, 5, 103, 0, 0, 699, 700, 5, 100, 0, 0, 700, 701, 5, 97, 0, 0, 701, 702, 5, 116, 0, 0, 702, 703, 5, 97, 0, 0, 703, 704, 5, 115, 0, 0, 704, 705, 5, 105, 0, 0, 705, 707, 5, 103, 0, 0, 706, 677, 1, 0, 0, 0, 706, 680, 1, 0, 0, 0, 706, 684, 1, 0, 0, 0, 706, 690, 1, 0, 0, 0, 706, 696, 1, 0, 0, 0, 706, 699, 1, 0, 0, 0, 707, 144, 1, 0, 0, 0, 708, 709, 5, 98, 0, 0, 709, 710, 5, 121, 0, 0, 710, 711, 5, 116, 0, 0, 711, 712, 5, 101, 0, 0, 712, 713, 5, 115, 0, 0, 713, 146, 1, 0, 0, 0, 714, 715, 5, 98, 0, 0, 715, 716, 5, 121, 0, 0, 716, 717, 5, 116, 0, 0, 717, 718, 5, 101, 0, 0, 718, 719, 5, 115, 0, 0, 719, 720, 1, 0, 0, 0, 720, 726, 3, 149, 74, 0, 721, 722, 5, 98, 0, 0, 722, 723, 5, 121, 0, 0, 723, 724, 5, 116, 0, 0, 724, 726, 5, 101, 0, 0, 725, 714, 1, 0, 0, 0, 725, 721, 1, 0, 0, 0, 726, 148, 1, 0, 0, 0, 727, 731, 7, 2, 0, 0, 728, 730, 7, 0, 0, 0, 729, 728, 1, 0, 0, 0, 730, 733, 1, 0, 0, 0, 731, 729, 1, 0, 0, 0, 731, 732, 1, 0, 0, 0, 732, 150, 1, 0, 0, 0, 733, 731, 1, 0, 0, 0, 734, 740, 5, 34, 0, 0, 735, 736, 5, 92, 0, 0, 736, 739, 5, 34, 0, 0, 737, 739, 8, 3, 0, 0, 738, 735, 1, 0, 0, 0, 738, 737, 1, 0, 0, 0, 739, 742, 1, 0, 0, 0, 740, 741, 1, 0, 0, 0, 740, 738, 1, 0, 0, 0, 741, 743, 1, 0, 0, 0, 742, 740, 1, 0, 0, 0, 743, 755, 5, 34, 0, 0, 744, 750, 5, 39, 0, 0, 745, 746, 5, 92, 0, 0, 746, 749, 5, 39, 0, 0, 747, 749, 8, 4, 0, 0, 748, 745, 1, 0, 0, 0, 748, 747, 1, 0, 0, 0, 749, 752, 1, 0, 0, 0, 750, 751, 1, 0, 0, 0, 750, 748, 1, 0, 0, 0, 751, 753, 1, 0, 0, 0, 752, 750, 1, 0, 0, 0, 753, 755, 5, 39, 0, 0, 754, 734, 1, 0, 0, 0, 754, 744, 1, 0, 0, 0, 755, 152, 1, 0, 0, 0, 756, 757, 5, 100, 0, 0, 757, 758, 5, 97, 0, 0, 758, 759, 5, 116, 0, 0, 759, 760, 5, 101, 0, 0, 760, 761, 5, 40, 0, 0, 761, 762, 1, 0, 0, 0, 762, 763, 3, 151, 75, 0, 763, 764, 5, 41, 0, 0, 764, 154, 1, 0, 0, 0, 765, 766, 5, 48, 0, 0, 766, 770, 7, 5, 0, 0, 767, 769, 7, 6, 0, 0, 768, 767, 1, 0, 0, 0, 769, 772, 1, 0, 0, 0, 770, 768, 1, 0, 0, 0, 770, 771, 1, 0, 0, 0, 771, 156, 1, 0, 0, 0, 772, 770, 1, 0, 0, 0, 773, 774, 5, 116, 0, 0, 774, 775, 5, 104, 0, 0, 775, 776, 5, 105, 0, 0, 776, 777, 5, 115, 0, 0, 777, 778, 5, 46, 0, 0, 778, 779, 5, 97, 0, 0, 779, 780, 5, 103, 0, 0, 780, 789, 5, 101, 0, 0, 781, 782, 5, 116, 0, 0, 782, 783, 5, 120, 0, 0, 783, 784, 5, 46, 0, 0, 784, 785, 5, 116, 0, 0, 785, 786, 5, 105, 0, 0, 786, 787, 5, 109, 0, 0, 787, 789, 5, 101, 0, 0, 788, 773, 1, 0, 0, 0, 788, 781, 1, 0, 0, 0, 789, 158, 1, 0, 0, 0, 790, 791, 5, 117, 0, 0, 791, 792, 5, 110, 0, 0, 792, 793, 5, 115, 0, 0, 793, 794, 5, 97, 0, 0, 794, 795, 5, 102, 0, 0, 795, 796, 5, 101, 0, 0, 796, 797, 5, 95, 0, 0, 797, 798, 5, 105, 0, 0, 798, 799, 5, 110, 0, 0, 799, 839, 5, 116, 0, 0, 800, 801, 5, 117, 0, 0, 801, 802, 5, 110, 0, 0, 802, 803, 5, 115, 0, 0, 803, 804, 5, 97, 0, 0, 804, 805, 5, 102, 0, 0, 805, 806, 5, 101, 0, 0, 806, 807, 5, 95, 0, 0, 807, 808, 5, 98, 0, 0, 808, 809, 5, 111, 0, 0, 809, 810, 5, 111, 0, 0, 810, 839, 5, 108, 0, 0, 811, 812, 5, 117, 0, 0, 812, 813, 5, 110, 0, 0, 813, 814, 5, 115, 0, 0, 814, 815, 5, 97, 0, 0, 815, 816, 5, 102, 0, 0, 816, 817, 5, 101, 0, 0, 817, 818, 5, 95, 0, 0, 818, 819, 5, 98, 0, 0, 819, 820, 5, 121, 0, 0, 820, 821, 5, 116, 0, 0, 821, 822, 5, 101, 0, 0, 822, 823, 5, 115, 0, 0, 823, 825, 1, 0, 0, 0, 824, 826, 3, 149, 74, 0, 825, 824, 1, 0, 0, 0, 825, 826, 1, 0, 0, 0, 826, 839, 1, 0, 0, 0, 827, 828, 5, 117, 0, 0, 828, 829, 5, 110, 0, 0, 829, 830, 5, 115, 0, 0, 830, 831, 5, 97, 0, 0, 831, 832, 5, 102, 0, 0, 832, 833, 5, 101, 0, 0, 833, 834, 5, 95, 0, 0, 834, 835, 5, 98, 0, 0, 835, 836, 5, 121, 0, 0, 836, 837, 5, 116, 0, 0, 837, 839, 5, 101, 0, 0, 838, 790, 1, 0, 0, 0, 838, 800, 1, 0, 0, 0, 838, 811, 1, 0, 0, 0, 838, 827, 1, 0, 0, 0, 839, 160, 1, 0, 0, 0, 840, 841, 5, 116, 0, 0, 841, 842, 5, 104, 0, 0, 842, 843, 5, 105, 0, 0, 843, 844, 5, 115, 0, 0, 844, 845, 5, 46, 0, 0, 845, 846, 5, 97, 0, 0, 846, 847, 5, 99, 0, 0, 847, 848, 5, 116, 0, 0, 848, 849, 5, 105, 0, 0, 849, 850, 5, 118, 0, 0, 850, 851, 5, 101, 0, 0, 851, 852, 5, 73, 0, 0, 852, 853, 5, 110, 0, 0, 853, 854, 5, 112, 0, 0, 854, 855, 5, 117, 0, 0, 855, 856, 5, 116, 0, 0, 856, 857, 5, 73, 0, 0, 857, 858, 5, 110, 0, 0, 858, 859, 5, 100, 0, 0, 859, 860, 5, 101, 0, 0, 860, 935, 5, 120, 0, 0, 861, 862, 5, 116, 0, 0, 862, 863, 5, 104, 0, 0, 863, 864, 5, 105, 0, 0, 864, 865, 5, 115, 0, 0, 865, 866, 5, 46, 0, 0, 866, 867, 5, 97, 0, 0, 867, 868, 5, 99, 0, 0, 868, 869, 5, 116, 0, 0, 869, 870, 5, 105, 0, 0, 870, 871, 5, 118, 0, 0, 871, 872, 5, 101, 0, 0, 872, 873, 5, 66, 0, 0, 873, 874, 5, 121, 0, 0, 874, 875, 5, 116, 0, 0, 875, 876, 5, 101, 0, 0, 876, 877, 5, 99, 0, 0, 877, 878, 5, 111, 0, 0, 878, 879, 5, 100, 0, 0, 879, 935, 5, 101, 0, 0, 880, 881, 5, 116, 0, 0, 881, 882, 5, 120, 0, 0, 882, 883, 5, 46, 0, 0, 883, 884, 5, 105, 0, 0, 884, 885, 5, 110, 0, 0, 885, 886, 5, 112, 0, 0, 886, 887, 5, 117, 0, 0, 887, 888, 5, 116, 0, 0, 888, 889, 5, 115, 0, 0, 889, 890, 5, 46, 0, 0, 890, 891, 5, 108, 0, 0, 891, 892, 5, 101, 0, 0, 892, 893, 5, 110, 0, 0, 893, 894, 5, 103, 0, 0, 894, 895, 5, 116, 0, 0, 895, 935, 5, 104, 0, 0, 896, 897, 5, 116, 0, 0, 897, 898, 5, 120, 0, 0, 898, 899, 5, 46, 0, 0, 899, 900, 5, 111, 0, 0, 900, 901, 5, 117, 0, 0, 901, 902, 5, 116, 0, 0, 902, 903, 5, 112, 0, 0, 903, 904, 5, 117, 0, 0, 904, 905, 5, 116, 0, 0, 905, 906, 5, 115, 0, 0, 906, 907, 5, 46, 0, 0, 907, 908, 5, 108, 0, 0, 908, 909, 5, 101, 0, 0, 909, 910, 5, 110, 0, 0, 910, 911, 5, 103, 0, 0, 911, 912, 5, 116, 0, 0, 912, 935, 5, 104, 0, 0, 913, 914, 5, 116, 0, 0, 914, 915, 5, 120, 0, 0, 915, 916, 5, 46, 0, 0, 916, 917, 5, 118, 0, 0, 917, 918, 5, 101, 0, 0, 918, 919, 5, 114, 0, 0, 919, 920, 5, 115, 0, 0, 920, 921, 5, 105, 0, 0, 921, 922, 5, 111, 0, 0, 922, 935, 5, 110, 0, 0, 923, 924, 5, 116, 0, 0, 924, 925, 5, 120, 0, 0, 925, 926, 5, 46, 0, 0, 926, 927, 5, 108, 0, 0, 927, 928, 5, 111, 0, 0, 928, 929, 5, 99, 0, 0, 929, 930, 5, 107, 0, 0, 930, 931, 5, 116, 0, 0, 931, 932, 5, 105, 0, 0, 932, 933, 5, 109, 0, 0, 933, 935, 5, 101, 0, 0, 934, 840, 1, 0, 0, 0, 934, 861, 1, 0, 0, 0, 934, 880, 1, 0, 0, 0, 934, 896, 1, 0, 0, 0, 934, 913, 1, 0, 0, 0, 934, 923, 1, 0, 0, 0, 935, 162, 1, 0, 0, 0, 936, 940, 7, 7, 0, 0, 937, 939, 7, 8, 0, 0, 938, 937, 1, 0, 0, 0, 939, 942, 1, 0, 0, 0, 940, 938, 1, 0, 0, 0, 940, 941, 1, 0, 0, 0, 941, 164, 1, 0, 0, 0, 942, 940, 1, 0, 0, 0, 943, 945, 7, 9, 0, 0, 944, 943, 1, 0, 0, 0, 945, 946, 1, 0, 0, 0, 946, 944, 1, 0, 0, 0, 946, 947, 1, 0, 0, 0, 947, 948, 1, 0, 0, 0, 948, 949, 6, 82, 0, 0, 949, 166, 1, 0, 0, 0, 950, 951, 5, 47, 0, 0, 951, 952, 5, 42, 0, 0, 952, 956, 1, 0, 0, 0, 953, 955, 9, 0, 0, 0, 954, 953, 1, 0, 0, 0, 955, 958, 1, 0, 0, 0, 956, 957, 1, 0, 0, 0, 956, 954, 1, 0, 0, 0, 957, 959, 1, 0, 0, 0, 958, 956, 1, 0, 0, 0, 959, 960, 5, 42, 0, 0, 960, 961, 5, 47, 0, 0, 961, 962, 1, 0, 0, 0, 962, 963, 6, 83, 1, 0, 963, 168, 1, 0, 0, 0, 964, 965, 5, 47, 0, 0, 965, 966, 5, 47, 0, 0, 966, 970, 1, 0, 0, 0, 967, 969, 8, 10, 0, 0, 968, 967, 1, 0, 0, 0, 969, 972, 1, 0, 0, 0, 970, 968, 1, 0, 0, 0, 970, 971, 1, 0, 0, 0, 971, 973, 1, 0, 0, 0, 972, 970, 1, 0, 0, 0, 973, 974, 6, 84, 1, 0, 974, 170, 1, 0, 0, 0, 28, 0, 567, 573, 579, 590, 649, 652, 656, 661, 667, 671, 706, 725, 731, 738, 740, 748, 750, 754, 770, 788, 825, 838, 934, 940, 946, 956, 970, 2, 6, 0, 0, 0, 1, 0] \ No newline at end of file diff --git a/packages/cashc/src/grammar/CashScriptLexer.tokens b/packages/cashc/src/grammar/CashScriptLexer.tokens index b9cfb61bd..1ca45fe0a 100644 --- a/packages/cashc/src/grammar/CashScriptLexer.tokens +++ b/packages/cashc/src/grammar/CashScriptLexer.tokens @@ -59,26 +59,30 @@ T__57=58 T__58=59 T__59=60 T__60=61 -VersionLiteral=62 -BooleanLiteral=63 -NumberUnit=64 -NumberLiteral=65 -NumberPart=66 -ExponentPart=67 -PrimitiveType=68 -UnboundedBytes=69 -BoundedBytes=70 -Bound=71 -StringLiteral=72 -DateLiteral=73 -HexLiteral=74 -TxVar=75 -UnsafeCast=76 -NullaryOp=77 -Identifier=78 -WHITESPACE=79 -COMMENT=80 -LINE_COMMENT=81 +T__61=62 +T__62=63 +T__63=64 +T__64=65 +VersionLiteral=66 +BooleanLiteral=67 +NumberUnit=68 +NumberLiteral=69 +NumberPart=70 +ExponentPart=71 +PrimitiveType=72 +UnboundedBytes=73 +BoundedBytes=74 +Bound=75 +StringLiteral=76 +DateLiteral=77 +HexLiteral=78 +TxVar=79 +UnsafeCast=80 +NullaryOp=81 +Identifier=82 +WHITESPACE=83 +COMMENT=84 +LINE_COMMENT=85 'pragma'=1 ';'=2 'cashscript'=3 @@ -89,55 +93,59 @@ LINE_COMMENT=81 '<'=8 '<='=9 '='=10 -'contract'=11 -'{'=12 -'}'=13 -'function'=14 -'('=15 -','=16 -')'=17 -'+='=18 -'-='=19 -'++'=20 -'--'=21 -'require'=22 -'console.log'=23 -'if'=24 -'else'=25 -'do'=26 -'while'=27 -'for'=28 -'new'=29 -'['=30 -']'=31 -'tx.outputs'=32 -'.value'=33 -'.lockingBytecode'=34 -'.tokenCategory'=35 -'.nftCommitment'=36 -'.tokenAmount'=37 -'tx.inputs'=38 -'.outpointTransactionHash'=39 -'.outpointIndex'=40 -'.unlockingBytecode'=41 -'.sequenceNumber'=42 -'.reverse()'=43 -'.length'=44 -'.split'=45 -'.slice'=46 -'!'=47 -'-'=48 -'*'=49 -'/'=50 -'%'=51 -'+'=52 -'>>'=53 -'<<'=54 -'=='=55 -'!='=56 -'&'=57 -'|'=58 -'&&'=59 -'||'=60 -'constant'=61 -'bytes'=69 +'import'=11 +'function'=12 +'returns'=13 +'('=14 +','=15 +')'=16 +'constant'=17 +'contract'=18 +'{'=19 +'}'=20 +'return'=21 +'+='=22 +'-='=23 +'++'=24 +'--'=25 +'require'=26 +'console.log'=27 +'if'=28 +'else'=29 +'do'=30 +'while'=31 +'for'=32 +'new'=33 +'['=34 +']'=35 +'tx.outputs'=36 +'.value'=37 +'.lockingBytecode'=38 +'.tokenCategory'=39 +'.nftCommitment'=40 +'.tokenAmount'=41 +'tx.inputs'=42 +'.outpointTransactionHash'=43 +'.outpointIndex'=44 +'.unlockingBytecode'=45 +'.sequenceNumber'=46 +'.reverse()'=47 +'.length'=48 +'.split'=49 +'.slice'=50 +'!'=51 +'-'=52 +'*'=53 +'/'=54 +'%'=55 +'+'=56 +'>>'=57 +'<<'=58 +'=='=59 +'!='=60 +'&'=61 +'|'=62 +'&&'=63 +'||'=64 +'unused'=65 +'bytes'=73 diff --git a/packages/cashc/src/grammar/CashScriptLexer.ts b/packages/cashc/src/grammar/CashScriptLexer.ts index c67a03b5f..e5634ac55 100644 --- a/packages/cashc/src/grammar/CashScriptLexer.ts +++ b/packages/cashc/src/grammar/CashScriptLexer.ts @@ -73,26 +73,30 @@ export default class CashScriptLexer extends Lexer { public static readonly T__58 = 59; public static readonly T__59 = 60; public static readonly T__60 = 61; - public static readonly VersionLiteral = 62; - public static readonly BooleanLiteral = 63; - public static readonly NumberUnit = 64; - public static readonly NumberLiteral = 65; - public static readonly NumberPart = 66; - public static readonly ExponentPart = 67; - public static readonly PrimitiveType = 68; - public static readonly UnboundedBytes = 69; - public static readonly BoundedBytes = 70; - public static readonly Bound = 71; - public static readonly StringLiteral = 72; - public static readonly DateLiteral = 73; - public static readonly HexLiteral = 74; - public static readonly TxVar = 75; - public static readonly UnsafeCast = 76; - public static readonly NullaryOp = 77; - public static readonly Identifier = 78; - public static readonly WHITESPACE = 79; - public static readonly COMMENT = 80; - public static readonly LINE_COMMENT = 81; + public static readonly T__61 = 62; + public static readonly T__62 = 63; + public static readonly T__63 = 64; + public static readonly T__64 = 65; + public static readonly VersionLiteral = 66; + public static readonly BooleanLiteral = 67; + public static readonly NumberUnit = 68; + public static readonly NumberLiteral = 69; + public static readonly NumberPart = 70; + public static readonly ExponentPart = 71; + public static readonly PrimitiveType = 72; + public static readonly UnboundedBytes = 73; + public static readonly BoundedBytes = 74; + public static readonly Bound = 75; + public static readonly StringLiteral = 76; + public static readonly DateLiteral = 77; + public static readonly HexLiteral = 78; + public static readonly TxVar = 79; + public static readonly UnsafeCast = 80; + public static readonly NullaryOp = 81; + public static readonly Identifier = 82; + public static readonly WHITESPACE = 83; + public static readonly COMMENT = 84; + public static readonly LINE_COMMENT = 85; public static readonly EOF = Token.EOF; public static readonly channelNames: string[] = [ "DEFAULT_TOKEN_CHANNEL", "HIDDEN" ]; @@ -101,13 +105,17 @@ export default class CashScriptLexer extends Lexer { "'^'", "'~'", "'>='", "'>'", "'<'", "'<='", - "'='", "'contract'", - "'{'", "'}'", + "'='", "'import'", "'function'", + "'returns'", "'('", "','", - "')'", "'+='", - "'-='", "'++'", - "'--'", "'require'", + "')'", "'constant'", + "'contract'", + "'{'", "'}'", + "'return'", + "'+='", "'-='", + "'++'", "'--'", + "'require'", "'console.log'", "'if'", "'else'", "'do'", "'while'", @@ -135,7 +143,7 @@ export default class CashScriptLexer extends Lexer { "'=='", "'!='", "'&'", "'|'", "'&&'", "'||'", - "'constant'", + "'unused'", null, null, null, null, null, null, @@ -171,6 +179,8 @@ export default class CashScriptLexer extends Lexer { null, null, null, null, null, null, + null, null, + null, null, "VersionLiteral", "BooleanLiteral", "NumberUnit", @@ -199,11 +209,11 @@ export default class CashScriptLexer extends Lexer { "T__33", "T__34", "T__35", "T__36", "T__37", "T__38", "T__39", "T__40", "T__41", "T__42", "T__43", "T__44", "T__45", "T__46", "T__47", "T__48", "T__49", "T__50", "T__51", "T__52", "T__53", "T__54", "T__55", "T__56", - "T__57", "T__58", "T__59", "T__60", "VersionLiteral", "BooleanLiteral", - "NumberUnit", "NumberLiteral", "NumberPart", "ExponentPart", "PrimitiveType", - "UnboundedBytes", "BoundedBytes", "Bound", "StringLiteral", "DateLiteral", - "HexLiteral", "TxVar", "UnsafeCast", "NullaryOp", "Identifier", "WHITESPACE", - "COMMENT", "LINE_COMMENT", + "T__57", "T__58", "T__59", "T__60", "T__61", "T__62", "T__63", "T__64", + "VersionLiteral", "BooleanLiteral", "NumberUnit", "NumberLiteral", "NumberPart", + "ExponentPart", "PrimitiveType", "UnboundedBytes", "BoundedBytes", "Bound", + "StringLiteral", "DateLiteral", "HexLiteral", "TxVar", "UnsafeCast", "NullaryOp", + "Identifier", "WHITESPACE", "COMMENT", "LINE_COMMENT", ]; @@ -224,7 +234,7 @@ export default class CashScriptLexer extends Lexer { public get modeNames(): string[] { return CashScriptLexer.modeNames; } - public static readonly _serializedATN: number[] = [4,0,81,938,6,-1,2,0, + public static readonly _serializedATN: number[] = [4,0,85,975,6,-1,2,0, 7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,2,7,7,7,2,8,7,8,2,9, 7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14,7,14,2,15,7,15,2,16,7, 16,2,17,7,17,2,18,7,18,2,19,7,19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23, @@ -235,304 +245,315 @@ export default class CashScriptLexer extends Lexer { 2,53,7,53,2,54,7,54,2,55,7,55,2,56,7,56,2,57,7,57,2,58,7,58,2,59,7,59,2, 60,7,60,2,61,7,61,2,62,7,62,2,63,7,63,2,64,7,64,2,65,7,65,2,66,7,66,2,67, 7,67,2,68,7,68,2,69,7,69,2,70,7,70,2,71,7,71,2,72,7,72,2,73,7,73,2,74,7, - 74,2,75,7,75,2,76,7,76,2,77,7,77,2,78,7,78,2,79,7,79,2,80,7,80,1,0,1,0, - 1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, - 1,3,1,3,1,4,1,4,1,5,1,5,1,5,1,6,1,6,1,7,1,7,1,8,1,8,1,8,1,9,1,9,1,10,1, - 10,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,11,1,11,1,12,1,12,1,13,1,13,1,13, - 1,13,1,13,1,13,1,13,1,13,1,13,1,14,1,14,1,15,1,15,1,16,1,16,1,17,1,17,1, - 17,1,18,1,18,1,18,1,19,1,19,1,19,1,20,1,20,1,20,1,21,1,21,1,21,1,21,1,21, - 1,21,1,21,1,21,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1, - 22,1,23,1,23,1,23,1,24,1,24,1,24,1,24,1,24,1,25,1,25,1,25,1,26,1,26,1,26, - 1,26,1,26,1,26,1,27,1,27,1,27,1,27,1,28,1,28,1,28,1,28,1,29,1,29,1,30,1, - 30,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,32,1,32,1,32, - 1,32,1,32,1,32,1,32,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1, - 33,1,33,1,33,1,33,1,33,1,33,1,33,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34, - 1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1, - 35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,36,1,36,1,36,1,36,1,36,1,36,1,36, - 1,36,1,36,1,36,1,36,1,36,1,36,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1, + 74,2,75,7,75,2,76,7,76,2,77,7,77,2,78,7,78,2,79,7,79,2,80,7,80,2,81,7,81, + 2,82,7,82,2,83,7,83,2,84,7,84,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,2,1, + 2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,3,1,3,1,4,1,4,1,5,1,5,1,5,1,6,1, + 6,1,7,1,7,1,8,1,8,1,8,1,9,1,9,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,11,1, + 11,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1,12,1,12,1,12,1,12,1,12,1,12,1,12, + 1,12,1,13,1,13,1,14,1,14,1,15,1,15,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1, + 16,1,16,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,18,1,18,1,19,1,19, + 1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,21,1,21,1,21,1,22,1,22,1,22,1,23,1, + 23,1,23,1,24,1,24,1,24,1,25,1,25,1,25,1,25,1,25,1,25,1,25,1,25,1,26,1,26, + 1,26,1,26,1,26,1,26,1,26,1,26,1,26,1,26,1,26,1,26,1,27,1,27,1,27,1,28,1, + 28,1,28,1,28,1,28,1,29,1,29,1,29,1,30,1,30,1,30,1,30,1,30,1,30,1,31,1,31, + 1,31,1,31,1,32,1,32,1,32,1,32,1,33,1,33,1,34,1,34,1,35,1,35,1,35,1,35,1, + 35,1,35,1,35,1,35,1,35,1,35,1,35,1,36,1,36,1,36,1,36,1,36,1,36,1,36,1,37, + 1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1, 37,1,37,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38, - 1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,39,1,39,1, - 39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,40,1,40, - 1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1, - 40,1,40,1,40,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41, - 1,41,1,41,1,41,1,41,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1, - 42,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1,44,1,44,1,44,1,44,1,44,1,44, - 1,44,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,46,1,46,1,47,1,47,1,48,1,48,1, - 49,1,49,1,50,1,50,1,51,1,51,1,52,1,52,1,52,1,53,1,53,1,53,1,54,1,54,1,54, - 1,55,1,55,1,55,1,56,1,56,1,57,1,57,1,58,1,58,1,58,1,59,1,59,1,59,1,60,1, - 60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,61,4,61,529,8,61,11,61,12,61,530, - 1,61,1,61,4,61,535,8,61,11,61,12,61,536,1,61,1,61,4,61,541,8,61,11,61,12, - 61,542,1,62,1,62,1,62,1,62,1,62,1,62,1,62,1,62,1,62,3,62,554,8,62,1,63, - 1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1, - 63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63, - 1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1, - 63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,3,63,613, - 8,63,1,64,3,64,616,8,64,1,64,1,64,3,64,620,8,64,1,65,4,65,623,8,65,11,65, - 12,65,624,1,65,1,65,4,65,629,8,65,11,65,12,65,630,5,65,633,8,65,10,65,12, - 65,636,9,65,1,66,1,66,1,66,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67, + 1,38,1,38,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1, + 39,1,39,1,39,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,40, + 1,40,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,42,1,42,1,42,1, + 42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42, + 1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1, + 43,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1,44,1,44,1,44,1,44,1,44,1,44,1,44, + 1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,45,1,45,1, + 45,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,46, + 1,46,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1,47,1,47,1,47,1,47,1, + 47,1,47,1,47,1,47,1,48,1,48,1,48,1,48,1,48,1,48,1,48,1,49,1,49,1,49,1,49, + 1,49,1,49,1,49,1,50,1,50,1,51,1,51,1,52,1,52,1,53,1,53,1,54,1,54,1,55,1, + 55,1,56,1,56,1,56,1,57,1,57,1,57,1,58,1,58,1,58,1,59,1,59,1,59,1,60,1,60, + 1,61,1,61,1,62,1,62,1,62,1,63,1,63,1,63,1,64,1,64,1,64,1,64,1,64,1,64,1, + 64,1,65,4,65,566,8,65,11,65,12,65,567,1,65,1,65,4,65,572,8,65,11,65,12, + 65,573,1,65,1,65,4,65,578,8,65,11,65,12,65,579,1,66,1,66,1,66,1,66,1,66, + 1,66,1,66,1,66,1,66,3,66,591,8,66,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1, + 67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67, 1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1, - 67,1,67,1,67,1,67,1,67,1,67,3,67,670,8,67,1,68,1,68,1,68,1,68,1,68,1,68, - 1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,3,69,689,8,69,1, - 70,1,70,5,70,693,8,70,10,70,12,70,696,9,70,1,71,1,71,1,71,1,71,5,71,702, - 8,71,10,71,12,71,705,9,71,1,71,1,71,1,71,1,71,1,71,5,71,712,8,71,10,71, - 12,71,715,9,71,1,71,3,71,718,8,71,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1, - 72,1,72,1,73,1,73,1,73,5,73,732,8,73,10,73,12,73,735,9,73,1,74,1,74,1,74, - 1,74,1,74,1,74,1,74,1,74,1,74,1,74,1,74,1,74,1,74,1,74,1,74,3,74,752,8, - 74,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1,75, - 1,75,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1, - 75,1,75,1,75,1,75,1,75,1,75,1,75,3,75,789,8,75,1,75,1,75,1,75,1,75,1,75, - 1,75,1,75,1,75,1,75,1,75,1,75,3,75,802,8,75,1,76,1,76,1,76,1,76,1,76,1, - 76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76, - 1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1, - 76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76, - 1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1, - 76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76, - 1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1, - 76,1,76,3,76,898,8,76,1,77,1,77,5,77,902,8,77,10,77,12,77,905,9,77,1,78, - 4,78,908,8,78,11,78,12,78,909,1,78,1,78,1,79,1,79,1,79,1,79,5,79,918,8, - 79,10,79,12,79,921,9,79,1,79,1,79,1,79,1,79,1,79,1,80,1,80,1,80,1,80,5, - 80,932,8,80,10,80,12,80,935,9,80,1,80,1,80,3,703,713,919,0,81,1,1,3,2,5, - 3,7,4,9,5,11,6,13,7,15,8,17,9,19,10,21,11,23,12,25,13,27,14,29,15,31,16, - 33,17,35,18,37,19,39,20,41,21,43,22,45,23,47,24,49,25,51,26,53,27,55,28, - 57,29,59,30,61,31,63,32,65,33,67,34,69,35,71,36,73,37,75,38,77,39,79,40, - 81,41,83,42,85,43,87,44,89,45,91,46,93,47,95,48,97,49,99,50,101,51,103, - 52,105,53,107,54,109,55,111,56,113,57,115,58,117,59,119,60,121,61,123,62, - 125,63,127,64,129,65,131,66,133,67,135,68,137,69,139,70,141,71,143,72,145, - 73,147,74,149,75,151,76,153,77,155,78,157,79,159,80,161,81,1,0,11,1,0,48, - 57,2,0,69,69,101,101,1,0,49,57,3,0,10,10,13,13,34,34,3,0,10,10,13,13,39, - 39,2,0,88,88,120,120,3,0,48,57,65,70,97,102,2,0,65,90,97,122,4,0,48,57, - 65,90,95,95,97,122,3,0,9,10,12,13,32,32,2,0,10,10,13,13,982,0,1,1,0,0,0, - 0,3,1,0,0,0,0,5,1,0,0,0,0,7,1,0,0,0,0,9,1,0,0,0,0,11,1,0,0,0,0,13,1,0,0, - 0,0,15,1,0,0,0,0,17,1,0,0,0,0,19,1,0,0,0,0,21,1,0,0,0,0,23,1,0,0,0,0,25, - 1,0,0,0,0,27,1,0,0,0,0,29,1,0,0,0,0,31,1,0,0,0,0,33,1,0,0,0,0,35,1,0,0, - 0,0,37,1,0,0,0,0,39,1,0,0,0,0,41,1,0,0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,47, - 1,0,0,0,0,49,1,0,0,0,0,51,1,0,0,0,0,53,1,0,0,0,0,55,1,0,0,0,0,57,1,0,0, - 0,0,59,1,0,0,0,0,61,1,0,0,0,0,63,1,0,0,0,0,65,1,0,0,0,0,67,1,0,0,0,0,69, - 1,0,0,0,0,71,1,0,0,0,0,73,1,0,0,0,0,75,1,0,0,0,0,77,1,0,0,0,0,79,1,0,0, - 0,0,81,1,0,0,0,0,83,1,0,0,0,0,85,1,0,0,0,0,87,1,0,0,0,0,89,1,0,0,0,0,91, - 1,0,0,0,0,93,1,0,0,0,0,95,1,0,0,0,0,97,1,0,0,0,0,99,1,0,0,0,0,101,1,0,0, - 0,0,103,1,0,0,0,0,105,1,0,0,0,0,107,1,0,0,0,0,109,1,0,0,0,0,111,1,0,0,0, - 0,113,1,0,0,0,0,115,1,0,0,0,0,117,1,0,0,0,0,119,1,0,0,0,0,121,1,0,0,0,0, - 123,1,0,0,0,0,125,1,0,0,0,0,127,1,0,0,0,0,129,1,0,0,0,0,131,1,0,0,0,0,133, - 1,0,0,0,0,135,1,0,0,0,0,137,1,0,0,0,0,139,1,0,0,0,0,141,1,0,0,0,0,143,1, - 0,0,0,0,145,1,0,0,0,0,147,1,0,0,0,0,149,1,0,0,0,0,151,1,0,0,0,0,153,1,0, - 0,0,0,155,1,0,0,0,0,157,1,0,0,0,0,159,1,0,0,0,0,161,1,0,0,0,1,163,1,0,0, - 0,3,170,1,0,0,0,5,172,1,0,0,0,7,183,1,0,0,0,9,185,1,0,0,0,11,187,1,0,0, - 0,13,190,1,0,0,0,15,192,1,0,0,0,17,194,1,0,0,0,19,197,1,0,0,0,21,199,1, - 0,0,0,23,208,1,0,0,0,25,210,1,0,0,0,27,212,1,0,0,0,29,221,1,0,0,0,31,223, - 1,0,0,0,33,225,1,0,0,0,35,227,1,0,0,0,37,230,1,0,0,0,39,233,1,0,0,0,41, - 236,1,0,0,0,43,239,1,0,0,0,45,247,1,0,0,0,47,259,1,0,0,0,49,262,1,0,0,0, - 51,267,1,0,0,0,53,270,1,0,0,0,55,276,1,0,0,0,57,280,1,0,0,0,59,284,1,0, - 0,0,61,286,1,0,0,0,63,288,1,0,0,0,65,299,1,0,0,0,67,306,1,0,0,0,69,323, - 1,0,0,0,71,338,1,0,0,0,73,353,1,0,0,0,75,366,1,0,0,0,77,376,1,0,0,0,79, - 401,1,0,0,0,81,416,1,0,0,0,83,435,1,0,0,0,85,451,1,0,0,0,87,462,1,0,0,0, - 89,470,1,0,0,0,91,477,1,0,0,0,93,484,1,0,0,0,95,486,1,0,0,0,97,488,1,0, - 0,0,99,490,1,0,0,0,101,492,1,0,0,0,103,494,1,0,0,0,105,496,1,0,0,0,107, - 499,1,0,0,0,109,502,1,0,0,0,111,505,1,0,0,0,113,508,1,0,0,0,115,510,1,0, - 0,0,117,512,1,0,0,0,119,515,1,0,0,0,121,518,1,0,0,0,123,528,1,0,0,0,125, - 553,1,0,0,0,127,612,1,0,0,0,129,615,1,0,0,0,131,622,1,0,0,0,133,637,1,0, - 0,0,135,669,1,0,0,0,137,671,1,0,0,0,139,688,1,0,0,0,141,690,1,0,0,0,143, - 717,1,0,0,0,145,719,1,0,0,0,147,728,1,0,0,0,149,751,1,0,0,0,151,801,1,0, - 0,0,153,897,1,0,0,0,155,899,1,0,0,0,157,907,1,0,0,0,159,913,1,0,0,0,161, - 927,1,0,0,0,163,164,5,112,0,0,164,165,5,114,0,0,165,166,5,97,0,0,166,167, - 5,103,0,0,167,168,5,109,0,0,168,169,5,97,0,0,169,2,1,0,0,0,170,171,5,59, - 0,0,171,4,1,0,0,0,172,173,5,99,0,0,173,174,5,97,0,0,174,175,5,115,0,0,175, - 176,5,104,0,0,176,177,5,115,0,0,177,178,5,99,0,0,178,179,5,114,0,0,179, - 180,5,105,0,0,180,181,5,112,0,0,181,182,5,116,0,0,182,6,1,0,0,0,183,184, - 5,94,0,0,184,8,1,0,0,0,185,186,5,126,0,0,186,10,1,0,0,0,187,188,5,62,0, - 0,188,189,5,61,0,0,189,12,1,0,0,0,190,191,5,62,0,0,191,14,1,0,0,0,192,193, - 5,60,0,0,193,16,1,0,0,0,194,195,5,60,0,0,195,196,5,61,0,0,196,18,1,0,0, - 0,197,198,5,61,0,0,198,20,1,0,0,0,199,200,5,99,0,0,200,201,5,111,0,0,201, - 202,5,110,0,0,202,203,5,116,0,0,203,204,5,114,0,0,204,205,5,97,0,0,205, - 206,5,99,0,0,206,207,5,116,0,0,207,22,1,0,0,0,208,209,5,123,0,0,209,24, - 1,0,0,0,210,211,5,125,0,0,211,26,1,0,0,0,212,213,5,102,0,0,213,214,5,117, - 0,0,214,215,5,110,0,0,215,216,5,99,0,0,216,217,5,116,0,0,217,218,5,105, - 0,0,218,219,5,111,0,0,219,220,5,110,0,0,220,28,1,0,0,0,221,222,5,40,0,0, - 222,30,1,0,0,0,223,224,5,44,0,0,224,32,1,0,0,0,225,226,5,41,0,0,226,34, - 1,0,0,0,227,228,5,43,0,0,228,229,5,61,0,0,229,36,1,0,0,0,230,231,5,45,0, - 0,231,232,5,61,0,0,232,38,1,0,0,0,233,234,5,43,0,0,234,235,5,43,0,0,235, - 40,1,0,0,0,236,237,5,45,0,0,237,238,5,45,0,0,238,42,1,0,0,0,239,240,5,114, - 0,0,240,241,5,101,0,0,241,242,5,113,0,0,242,243,5,117,0,0,243,244,5,105, - 0,0,244,245,5,114,0,0,245,246,5,101,0,0,246,44,1,0,0,0,247,248,5,99,0,0, - 248,249,5,111,0,0,249,250,5,110,0,0,250,251,5,115,0,0,251,252,5,111,0,0, - 252,253,5,108,0,0,253,254,5,101,0,0,254,255,5,46,0,0,255,256,5,108,0,0, - 256,257,5,111,0,0,257,258,5,103,0,0,258,46,1,0,0,0,259,260,5,105,0,0,260, - 261,5,102,0,0,261,48,1,0,0,0,262,263,5,101,0,0,263,264,5,108,0,0,264,265, - 5,115,0,0,265,266,5,101,0,0,266,50,1,0,0,0,267,268,5,100,0,0,268,269,5, - 111,0,0,269,52,1,0,0,0,270,271,5,119,0,0,271,272,5,104,0,0,272,273,5,105, - 0,0,273,274,5,108,0,0,274,275,5,101,0,0,275,54,1,0,0,0,276,277,5,102,0, - 0,277,278,5,111,0,0,278,279,5,114,0,0,279,56,1,0,0,0,280,281,5,110,0,0, - 281,282,5,101,0,0,282,283,5,119,0,0,283,58,1,0,0,0,284,285,5,91,0,0,285, - 60,1,0,0,0,286,287,5,93,0,0,287,62,1,0,0,0,288,289,5,116,0,0,289,290,5, - 120,0,0,290,291,5,46,0,0,291,292,5,111,0,0,292,293,5,117,0,0,293,294,5, - 116,0,0,294,295,5,112,0,0,295,296,5,117,0,0,296,297,5,116,0,0,297,298,5, - 115,0,0,298,64,1,0,0,0,299,300,5,46,0,0,300,301,5,118,0,0,301,302,5,97, - 0,0,302,303,5,108,0,0,303,304,5,117,0,0,304,305,5,101,0,0,305,66,1,0,0, - 0,306,307,5,46,0,0,307,308,5,108,0,0,308,309,5,111,0,0,309,310,5,99,0,0, - 310,311,5,107,0,0,311,312,5,105,0,0,312,313,5,110,0,0,313,314,5,103,0,0, - 314,315,5,66,0,0,315,316,5,121,0,0,316,317,5,116,0,0,317,318,5,101,0,0, - 318,319,5,99,0,0,319,320,5,111,0,0,320,321,5,100,0,0,321,322,5,101,0,0, - 322,68,1,0,0,0,323,324,5,46,0,0,324,325,5,116,0,0,325,326,5,111,0,0,326, - 327,5,107,0,0,327,328,5,101,0,0,328,329,5,110,0,0,329,330,5,67,0,0,330, - 331,5,97,0,0,331,332,5,116,0,0,332,333,5,101,0,0,333,334,5,103,0,0,334, - 335,5,111,0,0,335,336,5,114,0,0,336,337,5,121,0,0,337,70,1,0,0,0,338,339, - 5,46,0,0,339,340,5,110,0,0,340,341,5,102,0,0,341,342,5,116,0,0,342,343, - 5,67,0,0,343,344,5,111,0,0,344,345,5,109,0,0,345,346,5,109,0,0,346,347, - 5,105,0,0,347,348,5,116,0,0,348,349,5,109,0,0,349,350,5,101,0,0,350,351, - 5,110,0,0,351,352,5,116,0,0,352,72,1,0,0,0,353,354,5,46,0,0,354,355,5,116, - 0,0,355,356,5,111,0,0,356,357,5,107,0,0,357,358,5,101,0,0,358,359,5,110, - 0,0,359,360,5,65,0,0,360,361,5,109,0,0,361,362,5,111,0,0,362,363,5,117, - 0,0,363,364,5,110,0,0,364,365,5,116,0,0,365,74,1,0,0,0,366,367,5,116,0, - 0,367,368,5,120,0,0,368,369,5,46,0,0,369,370,5,105,0,0,370,371,5,110,0, - 0,371,372,5,112,0,0,372,373,5,117,0,0,373,374,5,116,0,0,374,375,5,115,0, - 0,375,76,1,0,0,0,376,377,5,46,0,0,377,378,5,111,0,0,378,379,5,117,0,0,379, - 380,5,116,0,0,380,381,5,112,0,0,381,382,5,111,0,0,382,383,5,105,0,0,383, - 384,5,110,0,0,384,385,5,116,0,0,385,386,5,84,0,0,386,387,5,114,0,0,387, - 388,5,97,0,0,388,389,5,110,0,0,389,390,5,115,0,0,390,391,5,97,0,0,391,392, - 5,99,0,0,392,393,5,116,0,0,393,394,5,105,0,0,394,395,5,111,0,0,395,396, - 5,110,0,0,396,397,5,72,0,0,397,398,5,97,0,0,398,399,5,115,0,0,399,400,5, - 104,0,0,400,78,1,0,0,0,401,402,5,46,0,0,402,403,5,111,0,0,403,404,5,117, - 0,0,404,405,5,116,0,0,405,406,5,112,0,0,406,407,5,111,0,0,407,408,5,105, - 0,0,408,409,5,110,0,0,409,410,5,116,0,0,410,411,5,73,0,0,411,412,5,110, - 0,0,412,413,5,100,0,0,413,414,5,101,0,0,414,415,5,120,0,0,415,80,1,0,0, - 0,416,417,5,46,0,0,417,418,5,117,0,0,418,419,5,110,0,0,419,420,5,108,0, - 0,420,421,5,111,0,0,421,422,5,99,0,0,422,423,5,107,0,0,423,424,5,105,0, - 0,424,425,5,110,0,0,425,426,5,103,0,0,426,427,5,66,0,0,427,428,5,121,0, - 0,428,429,5,116,0,0,429,430,5,101,0,0,430,431,5,99,0,0,431,432,5,111,0, - 0,432,433,5,100,0,0,433,434,5,101,0,0,434,82,1,0,0,0,435,436,5,46,0,0,436, - 437,5,115,0,0,437,438,5,101,0,0,438,439,5,113,0,0,439,440,5,117,0,0,440, - 441,5,101,0,0,441,442,5,110,0,0,442,443,5,99,0,0,443,444,5,101,0,0,444, - 445,5,78,0,0,445,446,5,117,0,0,446,447,5,109,0,0,447,448,5,98,0,0,448,449, - 5,101,0,0,449,450,5,114,0,0,450,84,1,0,0,0,451,452,5,46,0,0,452,453,5,114, - 0,0,453,454,5,101,0,0,454,455,5,118,0,0,455,456,5,101,0,0,456,457,5,114, - 0,0,457,458,5,115,0,0,458,459,5,101,0,0,459,460,5,40,0,0,460,461,5,41,0, - 0,461,86,1,0,0,0,462,463,5,46,0,0,463,464,5,108,0,0,464,465,5,101,0,0,465, - 466,5,110,0,0,466,467,5,103,0,0,467,468,5,116,0,0,468,469,5,104,0,0,469, - 88,1,0,0,0,470,471,5,46,0,0,471,472,5,115,0,0,472,473,5,112,0,0,473,474, - 5,108,0,0,474,475,5,105,0,0,475,476,5,116,0,0,476,90,1,0,0,0,477,478,5, - 46,0,0,478,479,5,115,0,0,479,480,5,108,0,0,480,481,5,105,0,0,481,482,5, - 99,0,0,482,483,5,101,0,0,483,92,1,0,0,0,484,485,5,33,0,0,485,94,1,0,0,0, - 486,487,5,45,0,0,487,96,1,0,0,0,488,489,5,42,0,0,489,98,1,0,0,0,490,491, - 5,47,0,0,491,100,1,0,0,0,492,493,5,37,0,0,493,102,1,0,0,0,494,495,5,43, - 0,0,495,104,1,0,0,0,496,497,5,62,0,0,497,498,5,62,0,0,498,106,1,0,0,0,499, - 500,5,60,0,0,500,501,5,60,0,0,501,108,1,0,0,0,502,503,5,61,0,0,503,504, - 5,61,0,0,504,110,1,0,0,0,505,506,5,33,0,0,506,507,5,61,0,0,507,112,1,0, - 0,0,508,509,5,38,0,0,509,114,1,0,0,0,510,511,5,124,0,0,511,116,1,0,0,0, - 512,513,5,38,0,0,513,514,5,38,0,0,514,118,1,0,0,0,515,516,5,124,0,0,516, - 517,5,124,0,0,517,120,1,0,0,0,518,519,5,99,0,0,519,520,5,111,0,0,520,521, - 5,110,0,0,521,522,5,115,0,0,522,523,5,116,0,0,523,524,5,97,0,0,524,525, - 5,110,0,0,525,526,5,116,0,0,526,122,1,0,0,0,527,529,7,0,0,0,528,527,1,0, - 0,0,529,530,1,0,0,0,530,528,1,0,0,0,530,531,1,0,0,0,531,532,1,0,0,0,532, - 534,5,46,0,0,533,535,7,0,0,0,534,533,1,0,0,0,535,536,1,0,0,0,536,534,1, - 0,0,0,536,537,1,0,0,0,537,538,1,0,0,0,538,540,5,46,0,0,539,541,7,0,0,0, - 540,539,1,0,0,0,541,542,1,0,0,0,542,540,1,0,0,0,542,543,1,0,0,0,543,124, - 1,0,0,0,544,545,5,116,0,0,545,546,5,114,0,0,546,547,5,117,0,0,547,554,5, - 101,0,0,548,549,5,102,0,0,549,550,5,97,0,0,550,551,5,108,0,0,551,552,5, - 115,0,0,552,554,5,101,0,0,553,544,1,0,0,0,553,548,1,0,0,0,554,126,1,0,0, - 0,555,556,5,115,0,0,556,557,5,97,0,0,557,558,5,116,0,0,558,559,5,111,0, - 0,559,560,5,115,0,0,560,561,5,104,0,0,561,562,5,105,0,0,562,613,5,115,0, - 0,563,564,5,115,0,0,564,565,5,97,0,0,565,566,5,116,0,0,566,613,5,115,0, - 0,567,568,5,102,0,0,568,569,5,105,0,0,569,570,5,110,0,0,570,571,5,110,0, - 0,571,572,5,101,0,0,572,613,5,121,0,0,573,574,5,98,0,0,574,575,5,105,0, - 0,575,576,5,116,0,0,576,613,5,115,0,0,577,578,5,98,0,0,578,579,5,105,0, - 0,579,580,5,116,0,0,580,581,5,99,0,0,581,582,5,111,0,0,582,583,5,105,0, - 0,583,613,5,110,0,0,584,585,5,115,0,0,585,586,5,101,0,0,586,587,5,99,0, - 0,587,588,5,111,0,0,588,589,5,110,0,0,589,590,5,100,0,0,590,613,5,115,0, - 0,591,592,5,109,0,0,592,593,5,105,0,0,593,594,5,110,0,0,594,595,5,117,0, - 0,595,596,5,116,0,0,596,597,5,101,0,0,597,613,5,115,0,0,598,599,5,104,0, - 0,599,600,5,111,0,0,600,601,5,117,0,0,601,602,5,114,0,0,602,613,5,115,0, - 0,603,604,5,100,0,0,604,605,5,97,0,0,605,606,5,121,0,0,606,613,5,115,0, - 0,607,608,5,119,0,0,608,609,5,101,0,0,609,610,5,101,0,0,610,611,5,107,0, - 0,611,613,5,115,0,0,612,555,1,0,0,0,612,563,1,0,0,0,612,567,1,0,0,0,612, - 573,1,0,0,0,612,577,1,0,0,0,612,584,1,0,0,0,612,591,1,0,0,0,612,598,1,0, - 0,0,612,603,1,0,0,0,612,607,1,0,0,0,613,128,1,0,0,0,614,616,5,45,0,0,615, - 614,1,0,0,0,615,616,1,0,0,0,616,617,1,0,0,0,617,619,3,131,65,0,618,620, - 3,133,66,0,619,618,1,0,0,0,619,620,1,0,0,0,620,130,1,0,0,0,621,623,7,0, - 0,0,622,621,1,0,0,0,623,624,1,0,0,0,624,622,1,0,0,0,624,625,1,0,0,0,625, - 634,1,0,0,0,626,628,5,95,0,0,627,629,7,0,0,0,628,627,1,0,0,0,629,630,1, - 0,0,0,630,628,1,0,0,0,630,631,1,0,0,0,631,633,1,0,0,0,632,626,1,0,0,0,633, - 636,1,0,0,0,634,632,1,0,0,0,634,635,1,0,0,0,635,132,1,0,0,0,636,634,1,0, - 0,0,637,638,7,1,0,0,638,639,3,131,65,0,639,134,1,0,0,0,640,641,5,105,0, - 0,641,642,5,110,0,0,642,670,5,116,0,0,643,644,5,98,0,0,644,645,5,111,0, - 0,645,646,5,111,0,0,646,670,5,108,0,0,647,648,5,115,0,0,648,649,5,116,0, - 0,649,650,5,114,0,0,650,651,5,105,0,0,651,652,5,110,0,0,652,670,5,103,0, - 0,653,654,5,112,0,0,654,655,5,117,0,0,655,656,5,98,0,0,656,657,5,107,0, - 0,657,658,5,101,0,0,658,670,5,121,0,0,659,660,5,115,0,0,660,661,5,105,0, - 0,661,670,5,103,0,0,662,663,5,100,0,0,663,664,5,97,0,0,664,665,5,116,0, - 0,665,666,5,97,0,0,666,667,5,115,0,0,667,668,5,105,0,0,668,670,5,103,0, - 0,669,640,1,0,0,0,669,643,1,0,0,0,669,647,1,0,0,0,669,653,1,0,0,0,669,659, - 1,0,0,0,669,662,1,0,0,0,670,136,1,0,0,0,671,672,5,98,0,0,672,673,5,121, - 0,0,673,674,5,116,0,0,674,675,5,101,0,0,675,676,5,115,0,0,676,138,1,0,0, - 0,677,678,5,98,0,0,678,679,5,121,0,0,679,680,5,116,0,0,680,681,5,101,0, - 0,681,682,5,115,0,0,682,683,1,0,0,0,683,689,3,141,70,0,684,685,5,98,0,0, - 685,686,5,121,0,0,686,687,5,116,0,0,687,689,5,101,0,0,688,677,1,0,0,0,688, - 684,1,0,0,0,689,140,1,0,0,0,690,694,7,2,0,0,691,693,7,0,0,0,692,691,1,0, - 0,0,693,696,1,0,0,0,694,692,1,0,0,0,694,695,1,0,0,0,695,142,1,0,0,0,696, - 694,1,0,0,0,697,703,5,34,0,0,698,699,5,92,0,0,699,702,5,34,0,0,700,702, - 8,3,0,0,701,698,1,0,0,0,701,700,1,0,0,0,702,705,1,0,0,0,703,704,1,0,0,0, - 703,701,1,0,0,0,704,706,1,0,0,0,705,703,1,0,0,0,706,718,5,34,0,0,707,713, - 5,39,0,0,708,709,5,92,0,0,709,712,5,39,0,0,710,712,8,4,0,0,711,708,1,0, - 0,0,711,710,1,0,0,0,712,715,1,0,0,0,713,714,1,0,0,0,713,711,1,0,0,0,714, - 716,1,0,0,0,715,713,1,0,0,0,716,718,5,39,0,0,717,697,1,0,0,0,717,707,1, - 0,0,0,718,144,1,0,0,0,719,720,5,100,0,0,720,721,5,97,0,0,721,722,5,116, - 0,0,722,723,5,101,0,0,723,724,5,40,0,0,724,725,1,0,0,0,725,726,3,143,71, - 0,726,727,5,41,0,0,727,146,1,0,0,0,728,729,5,48,0,0,729,733,7,5,0,0,730, - 732,7,6,0,0,731,730,1,0,0,0,732,735,1,0,0,0,733,731,1,0,0,0,733,734,1,0, - 0,0,734,148,1,0,0,0,735,733,1,0,0,0,736,737,5,116,0,0,737,738,5,104,0,0, - 738,739,5,105,0,0,739,740,5,115,0,0,740,741,5,46,0,0,741,742,5,97,0,0,742, - 743,5,103,0,0,743,752,5,101,0,0,744,745,5,116,0,0,745,746,5,120,0,0,746, - 747,5,46,0,0,747,748,5,116,0,0,748,749,5,105,0,0,749,750,5,109,0,0,750, - 752,5,101,0,0,751,736,1,0,0,0,751,744,1,0,0,0,752,150,1,0,0,0,753,754,5, - 117,0,0,754,755,5,110,0,0,755,756,5,115,0,0,756,757,5,97,0,0,757,758,5, - 102,0,0,758,759,5,101,0,0,759,760,5,95,0,0,760,761,5,105,0,0,761,762,5, - 110,0,0,762,802,5,116,0,0,763,764,5,117,0,0,764,765,5,110,0,0,765,766,5, - 115,0,0,766,767,5,97,0,0,767,768,5,102,0,0,768,769,5,101,0,0,769,770,5, - 95,0,0,770,771,5,98,0,0,771,772,5,111,0,0,772,773,5,111,0,0,773,802,5,108, - 0,0,774,775,5,117,0,0,775,776,5,110,0,0,776,777,5,115,0,0,777,778,5,97, - 0,0,778,779,5,102,0,0,779,780,5,101,0,0,780,781,5,95,0,0,781,782,5,98,0, - 0,782,783,5,121,0,0,783,784,5,116,0,0,784,785,5,101,0,0,785,786,5,115,0, - 0,786,788,1,0,0,0,787,789,3,141,70,0,788,787,1,0,0,0,788,789,1,0,0,0,789, - 802,1,0,0,0,790,791,5,117,0,0,791,792,5,110,0,0,792,793,5,115,0,0,793,794, - 5,97,0,0,794,795,5,102,0,0,795,796,5,101,0,0,796,797,5,95,0,0,797,798,5, - 98,0,0,798,799,5,121,0,0,799,800,5,116,0,0,800,802,5,101,0,0,801,753,1, - 0,0,0,801,763,1,0,0,0,801,774,1,0,0,0,801,790,1,0,0,0,802,152,1,0,0,0,803, - 804,5,116,0,0,804,805,5,104,0,0,805,806,5,105,0,0,806,807,5,115,0,0,807, - 808,5,46,0,0,808,809,5,97,0,0,809,810,5,99,0,0,810,811,5,116,0,0,811,812, - 5,105,0,0,812,813,5,118,0,0,813,814,5,101,0,0,814,815,5,73,0,0,815,816, - 5,110,0,0,816,817,5,112,0,0,817,818,5,117,0,0,818,819,5,116,0,0,819,820, - 5,73,0,0,820,821,5,110,0,0,821,822,5,100,0,0,822,823,5,101,0,0,823,898, - 5,120,0,0,824,825,5,116,0,0,825,826,5,104,0,0,826,827,5,105,0,0,827,828, - 5,115,0,0,828,829,5,46,0,0,829,830,5,97,0,0,830,831,5,99,0,0,831,832,5, - 116,0,0,832,833,5,105,0,0,833,834,5,118,0,0,834,835,5,101,0,0,835,836,5, - 66,0,0,836,837,5,121,0,0,837,838,5,116,0,0,838,839,5,101,0,0,839,840,5, - 99,0,0,840,841,5,111,0,0,841,842,5,100,0,0,842,898,5,101,0,0,843,844,5, - 116,0,0,844,845,5,120,0,0,845,846,5,46,0,0,846,847,5,105,0,0,847,848,5, - 110,0,0,848,849,5,112,0,0,849,850,5,117,0,0,850,851,5,116,0,0,851,852,5, - 115,0,0,852,853,5,46,0,0,853,854,5,108,0,0,854,855,5,101,0,0,855,856,5, - 110,0,0,856,857,5,103,0,0,857,858,5,116,0,0,858,898,5,104,0,0,859,860,5, - 116,0,0,860,861,5,120,0,0,861,862,5,46,0,0,862,863,5,111,0,0,863,864,5, - 117,0,0,864,865,5,116,0,0,865,866,5,112,0,0,866,867,5,117,0,0,867,868,5, - 116,0,0,868,869,5,115,0,0,869,870,5,46,0,0,870,871,5,108,0,0,871,872,5, - 101,0,0,872,873,5,110,0,0,873,874,5,103,0,0,874,875,5,116,0,0,875,898,5, - 104,0,0,876,877,5,116,0,0,877,878,5,120,0,0,878,879,5,46,0,0,879,880,5, - 118,0,0,880,881,5,101,0,0,881,882,5,114,0,0,882,883,5,115,0,0,883,884,5, - 105,0,0,884,885,5,111,0,0,885,898,5,110,0,0,886,887,5,116,0,0,887,888,5, - 120,0,0,888,889,5,46,0,0,889,890,5,108,0,0,890,891,5,111,0,0,891,892,5, - 99,0,0,892,893,5,107,0,0,893,894,5,116,0,0,894,895,5,105,0,0,895,896,5, - 109,0,0,896,898,5,101,0,0,897,803,1,0,0,0,897,824,1,0,0,0,897,843,1,0,0, - 0,897,859,1,0,0,0,897,876,1,0,0,0,897,886,1,0,0,0,898,154,1,0,0,0,899,903, - 7,7,0,0,900,902,7,8,0,0,901,900,1,0,0,0,902,905,1,0,0,0,903,901,1,0,0,0, - 903,904,1,0,0,0,904,156,1,0,0,0,905,903,1,0,0,0,906,908,7,9,0,0,907,906, - 1,0,0,0,908,909,1,0,0,0,909,907,1,0,0,0,909,910,1,0,0,0,910,911,1,0,0,0, - 911,912,6,78,0,0,912,158,1,0,0,0,913,914,5,47,0,0,914,915,5,42,0,0,915, - 919,1,0,0,0,916,918,9,0,0,0,917,916,1,0,0,0,918,921,1,0,0,0,919,920,1,0, - 0,0,919,917,1,0,0,0,920,922,1,0,0,0,921,919,1,0,0,0,922,923,5,42,0,0,923, - 924,5,47,0,0,924,925,1,0,0,0,925,926,6,79,1,0,926,160,1,0,0,0,927,928,5, - 47,0,0,928,929,5,47,0,0,929,933,1,0,0,0,930,932,8,10,0,0,931,930,1,0,0, - 0,932,935,1,0,0,0,933,931,1,0,0,0,933,934,1,0,0,0,934,936,1,0,0,0,935,933, - 1,0,0,0,936,937,6,80,1,0,937,162,1,0,0,0,28,0,530,536,542,553,612,615,619, - 624,630,634,669,688,694,701,703,711,713,717,733,751,788,801,897,903,909, - 919,933,2,6,0,0,0,1,0]; + 67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67, + 1,67,1,67,1,67,1,67,1,67,1,67,3,67,650,8,67,1,68,3,68,653,8,68,1,68,1,68, + 3,68,657,8,68,1,69,4,69,660,8,69,11,69,12,69,661,1,69,1,69,4,69,666,8,69, + 11,69,12,69,667,5,69,670,8,69,10,69,12,69,673,9,69,1,70,1,70,1,70,1,71, + 1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1, + 71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,3,71, + 707,8,71,1,72,1,72,1,72,1,72,1,72,1,72,1,73,1,73,1,73,1,73,1,73,1,73,1, + 73,1,73,1,73,1,73,1,73,3,73,726,8,73,1,74,1,74,5,74,730,8,74,10,74,12,74, + 733,9,74,1,75,1,75,1,75,1,75,5,75,739,8,75,10,75,12,75,742,9,75,1,75,1, + 75,1,75,1,75,1,75,5,75,749,8,75,10,75,12,75,752,9,75,1,75,3,75,755,8,75, + 1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,77,1,77,1,77,5,77,769,8, + 77,10,77,12,77,772,9,77,1,78,1,78,1,78,1,78,1,78,1,78,1,78,1,78,1,78,1, + 78,1,78,1,78,1,78,1,78,1,78,3,78,789,8,78,1,79,1,79,1,79,1,79,1,79,1,79, + 1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1, + 79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79, + 3,79,826,8,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,3, + 79,839,8,79,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80, + 1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1, + 80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80, + 1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1, + 80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80, + 1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1, + 80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,3,80,935,8,80,1,81,1,81, + 5,81,939,8,81,10,81,12,81,942,9,81,1,82,4,82,945,8,82,11,82,12,82,946,1, + 82,1,82,1,83,1,83,1,83,1,83,5,83,955,8,83,10,83,12,83,958,9,83,1,83,1,83, + 1,83,1,83,1,83,1,84,1,84,1,84,1,84,5,84,969,8,84,10,84,12,84,972,9,84,1, + 84,1,84,3,740,750,956,0,85,1,1,3,2,5,3,7,4,9,5,11,6,13,7,15,8,17,9,19,10, + 21,11,23,12,25,13,27,14,29,15,31,16,33,17,35,18,37,19,39,20,41,21,43,22, + 45,23,47,24,49,25,51,26,53,27,55,28,57,29,59,30,61,31,63,32,65,33,67,34, + 69,35,71,36,73,37,75,38,77,39,79,40,81,41,83,42,85,43,87,44,89,45,91,46, + 93,47,95,48,97,49,99,50,101,51,103,52,105,53,107,54,109,55,111,56,113,57, + 115,58,117,59,119,60,121,61,123,62,125,63,127,64,129,65,131,66,133,67,135, + 68,137,69,139,70,141,71,143,72,145,73,147,74,149,75,151,76,153,77,155,78, + 157,79,159,80,161,81,163,82,165,83,167,84,169,85,1,0,11,1,0,48,57,2,0,69, + 69,101,101,1,0,49,57,3,0,10,10,13,13,34,34,3,0,10,10,13,13,39,39,2,0,88, + 88,120,120,3,0,48,57,65,70,97,102,2,0,65,90,97,122,4,0,48,57,65,90,95,95, + 97,122,3,0,9,10,12,13,32,32,2,0,10,10,13,13,1019,0,1,1,0,0,0,0,3,1,0,0, + 0,0,5,1,0,0,0,0,7,1,0,0,0,0,9,1,0,0,0,0,11,1,0,0,0,0,13,1,0,0,0,0,15,1, + 0,0,0,0,17,1,0,0,0,0,19,1,0,0,0,0,21,1,0,0,0,0,23,1,0,0,0,0,25,1,0,0,0, + 0,27,1,0,0,0,0,29,1,0,0,0,0,31,1,0,0,0,0,33,1,0,0,0,0,35,1,0,0,0,0,37,1, + 0,0,0,0,39,1,0,0,0,0,41,1,0,0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,47,1,0,0,0, + 0,49,1,0,0,0,0,51,1,0,0,0,0,53,1,0,0,0,0,55,1,0,0,0,0,57,1,0,0,0,0,59,1, + 0,0,0,0,61,1,0,0,0,0,63,1,0,0,0,0,65,1,0,0,0,0,67,1,0,0,0,0,69,1,0,0,0, + 0,71,1,0,0,0,0,73,1,0,0,0,0,75,1,0,0,0,0,77,1,0,0,0,0,79,1,0,0,0,0,81,1, + 0,0,0,0,83,1,0,0,0,0,85,1,0,0,0,0,87,1,0,0,0,0,89,1,0,0,0,0,91,1,0,0,0, + 0,93,1,0,0,0,0,95,1,0,0,0,0,97,1,0,0,0,0,99,1,0,0,0,0,101,1,0,0,0,0,103, + 1,0,0,0,0,105,1,0,0,0,0,107,1,0,0,0,0,109,1,0,0,0,0,111,1,0,0,0,0,113,1, + 0,0,0,0,115,1,0,0,0,0,117,1,0,0,0,0,119,1,0,0,0,0,121,1,0,0,0,0,123,1,0, + 0,0,0,125,1,0,0,0,0,127,1,0,0,0,0,129,1,0,0,0,0,131,1,0,0,0,0,133,1,0,0, + 0,0,135,1,0,0,0,0,137,1,0,0,0,0,139,1,0,0,0,0,141,1,0,0,0,0,143,1,0,0,0, + 0,145,1,0,0,0,0,147,1,0,0,0,0,149,1,0,0,0,0,151,1,0,0,0,0,153,1,0,0,0,0, + 155,1,0,0,0,0,157,1,0,0,0,0,159,1,0,0,0,0,161,1,0,0,0,0,163,1,0,0,0,0,165, + 1,0,0,0,0,167,1,0,0,0,0,169,1,0,0,0,1,171,1,0,0,0,3,178,1,0,0,0,5,180,1, + 0,0,0,7,191,1,0,0,0,9,193,1,0,0,0,11,195,1,0,0,0,13,198,1,0,0,0,15,200, + 1,0,0,0,17,202,1,0,0,0,19,205,1,0,0,0,21,207,1,0,0,0,23,214,1,0,0,0,25, + 223,1,0,0,0,27,231,1,0,0,0,29,233,1,0,0,0,31,235,1,0,0,0,33,237,1,0,0,0, + 35,246,1,0,0,0,37,255,1,0,0,0,39,257,1,0,0,0,41,259,1,0,0,0,43,266,1,0, + 0,0,45,269,1,0,0,0,47,272,1,0,0,0,49,275,1,0,0,0,51,278,1,0,0,0,53,286, + 1,0,0,0,55,298,1,0,0,0,57,301,1,0,0,0,59,306,1,0,0,0,61,309,1,0,0,0,63, + 315,1,0,0,0,65,319,1,0,0,0,67,323,1,0,0,0,69,325,1,0,0,0,71,327,1,0,0,0, + 73,338,1,0,0,0,75,345,1,0,0,0,77,362,1,0,0,0,79,377,1,0,0,0,81,392,1,0, + 0,0,83,405,1,0,0,0,85,415,1,0,0,0,87,440,1,0,0,0,89,455,1,0,0,0,91,474, + 1,0,0,0,93,490,1,0,0,0,95,501,1,0,0,0,97,509,1,0,0,0,99,516,1,0,0,0,101, + 523,1,0,0,0,103,525,1,0,0,0,105,527,1,0,0,0,107,529,1,0,0,0,109,531,1,0, + 0,0,111,533,1,0,0,0,113,535,1,0,0,0,115,538,1,0,0,0,117,541,1,0,0,0,119, + 544,1,0,0,0,121,547,1,0,0,0,123,549,1,0,0,0,125,551,1,0,0,0,127,554,1,0, + 0,0,129,557,1,0,0,0,131,565,1,0,0,0,133,590,1,0,0,0,135,649,1,0,0,0,137, + 652,1,0,0,0,139,659,1,0,0,0,141,674,1,0,0,0,143,706,1,0,0,0,145,708,1,0, + 0,0,147,725,1,0,0,0,149,727,1,0,0,0,151,754,1,0,0,0,153,756,1,0,0,0,155, + 765,1,0,0,0,157,788,1,0,0,0,159,838,1,0,0,0,161,934,1,0,0,0,163,936,1,0, + 0,0,165,944,1,0,0,0,167,950,1,0,0,0,169,964,1,0,0,0,171,172,5,112,0,0,172, + 173,5,114,0,0,173,174,5,97,0,0,174,175,5,103,0,0,175,176,5,109,0,0,176, + 177,5,97,0,0,177,2,1,0,0,0,178,179,5,59,0,0,179,4,1,0,0,0,180,181,5,99, + 0,0,181,182,5,97,0,0,182,183,5,115,0,0,183,184,5,104,0,0,184,185,5,115, + 0,0,185,186,5,99,0,0,186,187,5,114,0,0,187,188,5,105,0,0,188,189,5,112, + 0,0,189,190,5,116,0,0,190,6,1,0,0,0,191,192,5,94,0,0,192,8,1,0,0,0,193, + 194,5,126,0,0,194,10,1,0,0,0,195,196,5,62,0,0,196,197,5,61,0,0,197,12,1, + 0,0,0,198,199,5,62,0,0,199,14,1,0,0,0,200,201,5,60,0,0,201,16,1,0,0,0,202, + 203,5,60,0,0,203,204,5,61,0,0,204,18,1,0,0,0,205,206,5,61,0,0,206,20,1, + 0,0,0,207,208,5,105,0,0,208,209,5,109,0,0,209,210,5,112,0,0,210,211,5,111, + 0,0,211,212,5,114,0,0,212,213,5,116,0,0,213,22,1,0,0,0,214,215,5,102,0, + 0,215,216,5,117,0,0,216,217,5,110,0,0,217,218,5,99,0,0,218,219,5,116,0, + 0,219,220,5,105,0,0,220,221,5,111,0,0,221,222,5,110,0,0,222,24,1,0,0,0, + 223,224,5,114,0,0,224,225,5,101,0,0,225,226,5,116,0,0,226,227,5,117,0,0, + 227,228,5,114,0,0,228,229,5,110,0,0,229,230,5,115,0,0,230,26,1,0,0,0,231, + 232,5,40,0,0,232,28,1,0,0,0,233,234,5,44,0,0,234,30,1,0,0,0,235,236,5,41, + 0,0,236,32,1,0,0,0,237,238,5,99,0,0,238,239,5,111,0,0,239,240,5,110,0,0, + 240,241,5,115,0,0,241,242,5,116,0,0,242,243,5,97,0,0,243,244,5,110,0,0, + 244,245,5,116,0,0,245,34,1,0,0,0,246,247,5,99,0,0,247,248,5,111,0,0,248, + 249,5,110,0,0,249,250,5,116,0,0,250,251,5,114,0,0,251,252,5,97,0,0,252, + 253,5,99,0,0,253,254,5,116,0,0,254,36,1,0,0,0,255,256,5,123,0,0,256,38, + 1,0,0,0,257,258,5,125,0,0,258,40,1,0,0,0,259,260,5,114,0,0,260,261,5,101, + 0,0,261,262,5,116,0,0,262,263,5,117,0,0,263,264,5,114,0,0,264,265,5,110, + 0,0,265,42,1,0,0,0,266,267,5,43,0,0,267,268,5,61,0,0,268,44,1,0,0,0,269, + 270,5,45,0,0,270,271,5,61,0,0,271,46,1,0,0,0,272,273,5,43,0,0,273,274,5, + 43,0,0,274,48,1,0,0,0,275,276,5,45,0,0,276,277,5,45,0,0,277,50,1,0,0,0, + 278,279,5,114,0,0,279,280,5,101,0,0,280,281,5,113,0,0,281,282,5,117,0,0, + 282,283,5,105,0,0,283,284,5,114,0,0,284,285,5,101,0,0,285,52,1,0,0,0,286, + 287,5,99,0,0,287,288,5,111,0,0,288,289,5,110,0,0,289,290,5,115,0,0,290, + 291,5,111,0,0,291,292,5,108,0,0,292,293,5,101,0,0,293,294,5,46,0,0,294, + 295,5,108,0,0,295,296,5,111,0,0,296,297,5,103,0,0,297,54,1,0,0,0,298,299, + 5,105,0,0,299,300,5,102,0,0,300,56,1,0,0,0,301,302,5,101,0,0,302,303,5, + 108,0,0,303,304,5,115,0,0,304,305,5,101,0,0,305,58,1,0,0,0,306,307,5,100, + 0,0,307,308,5,111,0,0,308,60,1,0,0,0,309,310,5,119,0,0,310,311,5,104,0, + 0,311,312,5,105,0,0,312,313,5,108,0,0,313,314,5,101,0,0,314,62,1,0,0,0, + 315,316,5,102,0,0,316,317,5,111,0,0,317,318,5,114,0,0,318,64,1,0,0,0,319, + 320,5,110,0,0,320,321,5,101,0,0,321,322,5,119,0,0,322,66,1,0,0,0,323,324, + 5,91,0,0,324,68,1,0,0,0,325,326,5,93,0,0,326,70,1,0,0,0,327,328,5,116,0, + 0,328,329,5,120,0,0,329,330,5,46,0,0,330,331,5,111,0,0,331,332,5,117,0, + 0,332,333,5,116,0,0,333,334,5,112,0,0,334,335,5,117,0,0,335,336,5,116,0, + 0,336,337,5,115,0,0,337,72,1,0,0,0,338,339,5,46,0,0,339,340,5,118,0,0,340, + 341,5,97,0,0,341,342,5,108,0,0,342,343,5,117,0,0,343,344,5,101,0,0,344, + 74,1,0,0,0,345,346,5,46,0,0,346,347,5,108,0,0,347,348,5,111,0,0,348,349, + 5,99,0,0,349,350,5,107,0,0,350,351,5,105,0,0,351,352,5,110,0,0,352,353, + 5,103,0,0,353,354,5,66,0,0,354,355,5,121,0,0,355,356,5,116,0,0,356,357, + 5,101,0,0,357,358,5,99,0,0,358,359,5,111,0,0,359,360,5,100,0,0,360,361, + 5,101,0,0,361,76,1,0,0,0,362,363,5,46,0,0,363,364,5,116,0,0,364,365,5,111, + 0,0,365,366,5,107,0,0,366,367,5,101,0,0,367,368,5,110,0,0,368,369,5,67, + 0,0,369,370,5,97,0,0,370,371,5,116,0,0,371,372,5,101,0,0,372,373,5,103, + 0,0,373,374,5,111,0,0,374,375,5,114,0,0,375,376,5,121,0,0,376,78,1,0,0, + 0,377,378,5,46,0,0,378,379,5,110,0,0,379,380,5,102,0,0,380,381,5,116,0, + 0,381,382,5,67,0,0,382,383,5,111,0,0,383,384,5,109,0,0,384,385,5,109,0, + 0,385,386,5,105,0,0,386,387,5,116,0,0,387,388,5,109,0,0,388,389,5,101,0, + 0,389,390,5,110,0,0,390,391,5,116,0,0,391,80,1,0,0,0,392,393,5,46,0,0,393, + 394,5,116,0,0,394,395,5,111,0,0,395,396,5,107,0,0,396,397,5,101,0,0,397, + 398,5,110,0,0,398,399,5,65,0,0,399,400,5,109,0,0,400,401,5,111,0,0,401, + 402,5,117,0,0,402,403,5,110,0,0,403,404,5,116,0,0,404,82,1,0,0,0,405,406, + 5,116,0,0,406,407,5,120,0,0,407,408,5,46,0,0,408,409,5,105,0,0,409,410, + 5,110,0,0,410,411,5,112,0,0,411,412,5,117,0,0,412,413,5,116,0,0,413,414, + 5,115,0,0,414,84,1,0,0,0,415,416,5,46,0,0,416,417,5,111,0,0,417,418,5,117, + 0,0,418,419,5,116,0,0,419,420,5,112,0,0,420,421,5,111,0,0,421,422,5,105, + 0,0,422,423,5,110,0,0,423,424,5,116,0,0,424,425,5,84,0,0,425,426,5,114, + 0,0,426,427,5,97,0,0,427,428,5,110,0,0,428,429,5,115,0,0,429,430,5,97,0, + 0,430,431,5,99,0,0,431,432,5,116,0,0,432,433,5,105,0,0,433,434,5,111,0, + 0,434,435,5,110,0,0,435,436,5,72,0,0,436,437,5,97,0,0,437,438,5,115,0,0, + 438,439,5,104,0,0,439,86,1,0,0,0,440,441,5,46,0,0,441,442,5,111,0,0,442, + 443,5,117,0,0,443,444,5,116,0,0,444,445,5,112,0,0,445,446,5,111,0,0,446, + 447,5,105,0,0,447,448,5,110,0,0,448,449,5,116,0,0,449,450,5,73,0,0,450, + 451,5,110,0,0,451,452,5,100,0,0,452,453,5,101,0,0,453,454,5,120,0,0,454, + 88,1,0,0,0,455,456,5,46,0,0,456,457,5,117,0,0,457,458,5,110,0,0,458,459, + 5,108,0,0,459,460,5,111,0,0,460,461,5,99,0,0,461,462,5,107,0,0,462,463, + 5,105,0,0,463,464,5,110,0,0,464,465,5,103,0,0,465,466,5,66,0,0,466,467, + 5,121,0,0,467,468,5,116,0,0,468,469,5,101,0,0,469,470,5,99,0,0,470,471, + 5,111,0,0,471,472,5,100,0,0,472,473,5,101,0,0,473,90,1,0,0,0,474,475,5, + 46,0,0,475,476,5,115,0,0,476,477,5,101,0,0,477,478,5,113,0,0,478,479,5, + 117,0,0,479,480,5,101,0,0,480,481,5,110,0,0,481,482,5,99,0,0,482,483,5, + 101,0,0,483,484,5,78,0,0,484,485,5,117,0,0,485,486,5,109,0,0,486,487,5, + 98,0,0,487,488,5,101,0,0,488,489,5,114,0,0,489,92,1,0,0,0,490,491,5,46, + 0,0,491,492,5,114,0,0,492,493,5,101,0,0,493,494,5,118,0,0,494,495,5,101, + 0,0,495,496,5,114,0,0,496,497,5,115,0,0,497,498,5,101,0,0,498,499,5,40, + 0,0,499,500,5,41,0,0,500,94,1,0,0,0,501,502,5,46,0,0,502,503,5,108,0,0, + 503,504,5,101,0,0,504,505,5,110,0,0,505,506,5,103,0,0,506,507,5,116,0,0, + 507,508,5,104,0,0,508,96,1,0,0,0,509,510,5,46,0,0,510,511,5,115,0,0,511, + 512,5,112,0,0,512,513,5,108,0,0,513,514,5,105,0,0,514,515,5,116,0,0,515, + 98,1,0,0,0,516,517,5,46,0,0,517,518,5,115,0,0,518,519,5,108,0,0,519,520, + 5,105,0,0,520,521,5,99,0,0,521,522,5,101,0,0,522,100,1,0,0,0,523,524,5, + 33,0,0,524,102,1,0,0,0,525,526,5,45,0,0,526,104,1,0,0,0,527,528,5,42,0, + 0,528,106,1,0,0,0,529,530,5,47,0,0,530,108,1,0,0,0,531,532,5,37,0,0,532, + 110,1,0,0,0,533,534,5,43,0,0,534,112,1,0,0,0,535,536,5,62,0,0,536,537,5, + 62,0,0,537,114,1,0,0,0,538,539,5,60,0,0,539,540,5,60,0,0,540,116,1,0,0, + 0,541,542,5,61,0,0,542,543,5,61,0,0,543,118,1,0,0,0,544,545,5,33,0,0,545, + 546,5,61,0,0,546,120,1,0,0,0,547,548,5,38,0,0,548,122,1,0,0,0,549,550,5, + 124,0,0,550,124,1,0,0,0,551,552,5,38,0,0,552,553,5,38,0,0,553,126,1,0,0, + 0,554,555,5,124,0,0,555,556,5,124,0,0,556,128,1,0,0,0,557,558,5,117,0,0, + 558,559,5,110,0,0,559,560,5,117,0,0,560,561,5,115,0,0,561,562,5,101,0,0, + 562,563,5,100,0,0,563,130,1,0,0,0,564,566,7,0,0,0,565,564,1,0,0,0,566,567, + 1,0,0,0,567,565,1,0,0,0,567,568,1,0,0,0,568,569,1,0,0,0,569,571,5,46,0, + 0,570,572,7,0,0,0,571,570,1,0,0,0,572,573,1,0,0,0,573,571,1,0,0,0,573,574, + 1,0,0,0,574,575,1,0,0,0,575,577,5,46,0,0,576,578,7,0,0,0,577,576,1,0,0, + 0,578,579,1,0,0,0,579,577,1,0,0,0,579,580,1,0,0,0,580,132,1,0,0,0,581,582, + 5,116,0,0,582,583,5,114,0,0,583,584,5,117,0,0,584,591,5,101,0,0,585,586, + 5,102,0,0,586,587,5,97,0,0,587,588,5,108,0,0,588,589,5,115,0,0,589,591, + 5,101,0,0,590,581,1,0,0,0,590,585,1,0,0,0,591,134,1,0,0,0,592,593,5,115, + 0,0,593,594,5,97,0,0,594,595,5,116,0,0,595,596,5,111,0,0,596,597,5,115, + 0,0,597,598,5,104,0,0,598,599,5,105,0,0,599,650,5,115,0,0,600,601,5,115, + 0,0,601,602,5,97,0,0,602,603,5,116,0,0,603,650,5,115,0,0,604,605,5,102, + 0,0,605,606,5,105,0,0,606,607,5,110,0,0,607,608,5,110,0,0,608,609,5,101, + 0,0,609,650,5,121,0,0,610,611,5,98,0,0,611,612,5,105,0,0,612,613,5,116, + 0,0,613,650,5,115,0,0,614,615,5,98,0,0,615,616,5,105,0,0,616,617,5,116, + 0,0,617,618,5,99,0,0,618,619,5,111,0,0,619,620,5,105,0,0,620,650,5,110, + 0,0,621,622,5,115,0,0,622,623,5,101,0,0,623,624,5,99,0,0,624,625,5,111, + 0,0,625,626,5,110,0,0,626,627,5,100,0,0,627,650,5,115,0,0,628,629,5,109, + 0,0,629,630,5,105,0,0,630,631,5,110,0,0,631,632,5,117,0,0,632,633,5,116, + 0,0,633,634,5,101,0,0,634,650,5,115,0,0,635,636,5,104,0,0,636,637,5,111, + 0,0,637,638,5,117,0,0,638,639,5,114,0,0,639,650,5,115,0,0,640,641,5,100, + 0,0,641,642,5,97,0,0,642,643,5,121,0,0,643,650,5,115,0,0,644,645,5,119, + 0,0,645,646,5,101,0,0,646,647,5,101,0,0,647,648,5,107,0,0,648,650,5,115, + 0,0,649,592,1,0,0,0,649,600,1,0,0,0,649,604,1,0,0,0,649,610,1,0,0,0,649, + 614,1,0,0,0,649,621,1,0,0,0,649,628,1,0,0,0,649,635,1,0,0,0,649,640,1,0, + 0,0,649,644,1,0,0,0,650,136,1,0,0,0,651,653,5,45,0,0,652,651,1,0,0,0,652, + 653,1,0,0,0,653,654,1,0,0,0,654,656,3,139,69,0,655,657,3,141,70,0,656,655, + 1,0,0,0,656,657,1,0,0,0,657,138,1,0,0,0,658,660,7,0,0,0,659,658,1,0,0,0, + 660,661,1,0,0,0,661,659,1,0,0,0,661,662,1,0,0,0,662,671,1,0,0,0,663,665, + 5,95,0,0,664,666,7,0,0,0,665,664,1,0,0,0,666,667,1,0,0,0,667,665,1,0,0, + 0,667,668,1,0,0,0,668,670,1,0,0,0,669,663,1,0,0,0,670,673,1,0,0,0,671,669, + 1,0,0,0,671,672,1,0,0,0,672,140,1,0,0,0,673,671,1,0,0,0,674,675,7,1,0,0, + 675,676,3,139,69,0,676,142,1,0,0,0,677,678,5,105,0,0,678,679,5,110,0,0, + 679,707,5,116,0,0,680,681,5,98,0,0,681,682,5,111,0,0,682,683,5,111,0,0, + 683,707,5,108,0,0,684,685,5,115,0,0,685,686,5,116,0,0,686,687,5,114,0,0, + 687,688,5,105,0,0,688,689,5,110,0,0,689,707,5,103,0,0,690,691,5,112,0,0, + 691,692,5,117,0,0,692,693,5,98,0,0,693,694,5,107,0,0,694,695,5,101,0,0, + 695,707,5,121,0,0,696,697,5,115,0,0,697,698,5,105,0,0,698,707,5,103,0,0, + 699,700,5,100,0,0,700,701,5,97,0,0,701,702,5,116,0,0,702,703,5,97,0,0,703, + 704,5,115,0,0,704,705,5,105,0,0,705,707,5,103,0,0,706,677,1,0,0,0,706,680, + 1,0,0,0,706,684,1,0,0,0,706,690,1,0,0,0,706,696,1,0,0,0,706,699,1,0,0,0, + 707,144,1,0,0,0,708,709,5,98,0,0,709,710,5,121,0,0,710,711,5,116,0,0,711, + 712,5,101,0,0,712,713,5,115,0,0,713,146,1,0,0,0,714,715,5,98,0,0,715,716, + 5,121,0,0,716,717,5,116,0,0,717,718,5,101,0,0,718,719,5,115,0,0,719,720, + 1,0,0,0,720,726,3,149,74,0,721,722,5,98,0,0,722,723,5,121,0,0,723,724,5, + 116,0,0,724,726,5,101,0,0,725,714,1,0,0,0,725,721,1,0,0,0,726,148,1,0,0, + 0,727,731,7,2,0,0,728,730,7,0,0,0,729,728,1,0,0,0,730,733,1,0,0,0,731,729, + 1,0,0,0,731,732,1,0,0,0,732,150,1,0,0,0,733,731,1,0,0,0,734,740,5,34,0, + 0,735,736,5,92,0,0,736,739,5,34,0,0,737,739,8,3,0,0,738,735,1,0,0,0,738, + 737,1,0,0,0,739,742,1,0,0,0,740,741,1,0,0,0,740,738,1,0,0,0,741,743,1,0, + 0,0,742,740,1,0,0,0,743,755,5,34,0,0,744,750,5,39,0,0,745,746,5,92,0,0, + 746,749,5,39,0,0,747,749,8,4,0,0,748,745,1,0,0,0,748,747,1,0,0,0,749,752, + 1,0,0,0,750,751,1,0,0,0,750,748,1,0,0,0,751,753,1,0,0,0,752,750,1,0,0,0, + 753,755,5,39,0,0,754,734,1,0,0,0,754,744,1,0,0,0,755,152,1,0,0,0,756,757, + 5,100,0,0,757,758,5,97,0,0,758,759,5,116,0,0,759,760,5,101,0,0,760,761, + 5,40,0,0,761,762,1,0,0,0,762,763,3,151,75,0,763,764,5,41,0,0,764,154,1, + 0,0,0,765,766,5,48,0,0,766,770,7,5,0,0,767,769,7,6,0,0,768,767,1,0,0,0, + 769,772,1,0,0,0,770,768,1,0,0,0,770,771,1,0,0,0,771,156,1,0,0,0,772,770, + 1,0,0,0,773,774,5,116,0,0,774,775,5,104,0,0,775,776,5,105,0,0,776,777,5, + 115,0,0,777,778,5,46,0,0,778,779,5,97,0,0,779,780,5,103,0,0,780,789,5,101, + 0,0,781,782,5,116,0,0,782,783,5,120,0,0,783,784,5,46,0,0,784,785,5,116, + 0,0,785,786,5,105,0,0,786,787,5,109,0,0,787,789,5,101,0,0,788,773,1,0,0, + 0,788,781,1,0,0,0,789,158,1,0,0,0,790,791,5,117,0,0,791,792,5,110,0,0,792, + 793,5,115,0,0,793,794,5,97,0,0,794,795,5,102,0,0,795,796,5,101,0,0,796, + 797,5,95,0,0,797,798,5,105,0,0,798,799,5,110,0,0,799,839,5,116,0,0,800, + 801,5,117,0,0,801,802,5,110,0,0,802,803,5,115,0,0,803,804,5,97,0,0,804, + 805,5,102,0,0,805,806,5,101,0,0,806,807,5,95,0,0,807,808,5,98,0,0,808,809, + 5,111,0,0,809,810,5,111,0,0,810,839,5,108,0,0,811,812,5,117,0,0,812,813, + 5,110,0,0,813,814,5,115,0,0,814,815,5,97,0,0,815,816,5,102,0,0,816,817, + 5,101,0,0,817,818,5,95,0,0,818,819,5,98,0,0,819,820,5,121,0,0,820,821,5, + 116,0,0,821,822,5,101,0,0,822,823,5,115,0,0,823,825,1,0,0,0,824,826,3,149, + 74,0,825,824,1,0,0,0,825,826,1,0,0,0,826,839,1,0,0,0,827,828,5,117,0,0, + 828,829,5,110,0,0,829,830,5,115,0,0,830,831,5,97,0,0,831,832,5,102,0,0, + 832,833,5,101,0,0,833,834,5,95,0,0,834,835,5,98,0,0,835,836,5,121,0,0,836, + 837,5,116,0,0,837,839,5,101,0,0,838,790,1,0,0,0,838,800,1,0,0,0,838,811, + 1,0,0,0,838,827,1,0,0,0,839,160,1,0,0,0,840,841,5,116,0,0,841,842,5,104, + 0,0,842,843,5,105,0,0,843,844,5,115,0,0,844,845,5,46,0,0,845,846,5,97,0, + 0,846,847,5,99,0,0,847,848,5,116,0,0,848,849,5,105,0,0,849,850,5,118,0, + 0,850,851,5,101,0,0,851,852,5,73,0,0,852,853,5,110,0,0,853,854,5,112,0, + 0,854,855,5,117,0,0,855,856,5,116,0,0,856,857,5,73,0,0,857,858,5,110,0, + 0,858,859,5,100,0,0,859,860,5,101,0,0,860,935,5,120,0,0,861,862,5,116,0, + 0,862,863,5,104,0,0,863,864,5,105,0,0,864,865,5,115,0,0,865,866,5,46,0, + 0,866,867,5,97,0,0,867,868,5,99,0,0,868,869,5,116,0,0,869,870,5,105,0,0, + 870,871,5,118,0,0,871,872,5,101,0,0,872,873,5,66,0,0,873,874,5,121,0,0, + 874,875,5,116,0,0,875,876,5,101,0,0,876,877,5,99,0,0,877,878,5,111,0,0, + 878,879,5,100,0,0,879,935,5,101,0,0,880,881,5,116,0,0,881,882,5,120,0,0, + 882,883,5,46,0,0,883,884,5,105,0,0,884,885,5,110,0,0,885,886,5,112,0,0, + 886,887,5,117,0,0,887,888,5,116,0,0,888,889,5,115,0,0,889,890,5,46,0,0, + 890,891,5,108,0,0,891,892,5,101,0,0,892,893,5,110,0,0,893,894,5,103,0,0, + 894,895,5,116,0,0,895,935,5,104,0,0,896,897,5,116,0,0,897,898,5,120,0,0, + 898,899,5,46,0,0,899,900,5,111,0,0,900,901,5,117,0,0,901,902,5,116,0,0, + 902,903,5,112,0,0,903,904,5,117,0,0,904,905,5,116,0,0,905,906,5,115,0,0, + 906,907,5,46,0,0,907,908,5,108,0,0,908,909,5,101,0,0,909,910,5,110,0,0, + 910,911,5,103,0,0,911,912,5,116,0,0,912,935,5,104,0,0,913,914,5,116,0,0, + 914,915,5,120,0,0,915,916,5,46,0,0,916,917,5,118,0,0,917,918,5,101,0,0, + 918,919,5,114,0,0,919,920,5,115,0,0,920,921,5,105,0,0,921,922,5,111,0,0, + 922,935,5,110,0,0,923,924,5,116,0,0,924,925,5,120,0,0,925,926,5,46,0,0, + 926,927,5,108,0,0,927,928,5,111,0,0,928,929,5,99,0,0,929,930,5,107,0,0, + 930,931,5,116,0,0,931,932,5,105,0,0,932,933,5,109,0,0,933,935,5,101,0,0, + 934,840,1,0,0,0,934,861,1,0,0,0,934,880,1,0,0,0,934,896,1,0,0,0,934,913, + 1,0,0,0,934,923,1,0,0,0,935,162,1,0,0,0,936,940,7,7,0,0,937,939,7,8,0,0, + 938,937,1,0,0,0,939,942,1,0,0,0,940,938,1,0,0,0,940,941,1,0,0,0,941,164, + 1,0,0,0,942,940,1,0,0,0,943,945,7,9,0,0,944,943,1,0,0,0,945,946,1,0,0,0, + 946,944,1,0,0,0,946,947,1,0,0,0,947,948,1,0,0,0,948,949,6,82,0,0,949,166, + 1,0,0,0,950,951,5,47,0,0,951,952,5,42,0,0,952,956,1,0,0,0,953,955,9,0,0, + 0,954,953,1,0,0,0,955,958,1,0,0,0,956,957,1,0,0,0,956,954,1,0,0,0,957,959, + 1,0,0,0,958,956,1,0,0,0,959,960,5,42,0,0,960,961,5,47,0,0,961,962,1,0,0, + 0,962,963,6,83,1,0,963,168,1,0,0,0,964,965,5,47,0,0,965,966,5,47,0,0,966, + 970,1,0,0,0,967,969,8,10,0,0,968,967,1,0,0,0,969,972,1,0,0,0,970,968,1, + 0,0,0,970,971,1,0,0,0,971,973,1,0,0,0,972,970,1,0,0,0,973,974,6,84,1,0, + 974,170,1,0,0,0,28,0,567,573,579,590,649,652,656,661,667,671,706,725,731, + 738,740,748,750,754,770,788,825,838,934,940,946,956,970,2,6,0,0,0,1,0]; private static __ATN: ATN; public static get _ATN(): ATN { diff --git a/packages/cashc/src/grammar/CashScriptParser.ts b/packages/cashc/src/grammar/CashScriptParser.ts index 8d8c718dd..fae77537c 100644 --- a/packages/cashc/src/grammar/CashScriptParser.ts +++ b/packages/cashc/src/grammar/CashScriptParser.ts @@ -79,26 +79,30 @@ export default class CashScriptParser extends Parser { public static readonly T__58 = 59; public static readonly T__59 = 60; public static readonly T__60 = 61; - public static readonly VersionLiteral = 62; - public static readonly BooleanLiteral = 63; - public static readonly NumberUnit = 64; - public static readonly NumberLiteral = 65; - public static readonly NumberPart = 66; - public static readonly ExponentPart = 67; - public static readonly PrimitiveType = 68; - public static readonly UnboundedBytes = 69; - public static readonly BoundedBytes = 70; - public static readonly Bound = 71; - public static readonly StringLiteral = 72; - public static readonly DateLiteral = 73; - public static readonly HexLiteral = 74; - public static readonly TxVar = 75; - public static readonly UnsafeCast = 76; - public static readonly NullaryOp = 77; - public static readonly Identifier = 78; - public static readonly WHITESPACE = 79; - public static readonly COMMENT = 80; - public static readonly LINE_COMMENT = 81; + public static readonly T__61 = 62; + public static readonly T__62 = 63; + public static readonly T__63 = 64; + public static readonly T__64 = 65; + public static readonly VersionLiteral = 66; + public static readonly BooleanLiteral = 67; + public static readonly NumberUnit = 68; + public static readonly NumberLiteral = 69; + public static readonly NumberPart = 70; + public static readonly ExponentPart = 71; + public static readonly PrimitiveType = 72; + public static readonly UnboundedBytes = 73; + public static readonly BoundedBytes = 74; + public static readonly Bound = 75; + public static readonly StringLiteral = 76; + public static readonly DateLiteral = 77; + public static readonly HexLiteral = 78; + public static readonly TxVar = 79; + public static readonly UnsafeCast = 80; + public static readonly NullaryOp = 81; + public static readonly Identifier = 82; + public static readonly WHITESPACE = 83; + public static readonly COMMENT = 84; + public static readonly LINE_COMMENT = 85; public static readonly EOF = Token.EOF; public static readonly RULE_sourceFile = 0; public static readonly RULE_pragmaDirective = 1; @@ -106,50 +110,61 @@ export default class CashScriptParser extends Parser { public static readonly RULE_pragmaValue = 3; public static readonly RULE_versionConstraint = 4; public static readonly RULE_versionOperator = 5; - public static readonly RULE_contractDefinition = 6; - public static readonly RULE_functionDefinition = 7; - public static readonly RULE_functionBody = 8; - public static readonly RULE_parameterList = 9; - public static readonly RULE_parameter = 10; - public static readonly RULE_block = 11; - public static readonly RULE_statement = 12; - public static readonly RULE_nonControlStatement = 13; - public static readonly RULE_controlStatement = 14; - public static readonly RULE_variableDefinition = 15; - public static readonly RULE_tupleAssignment = 16; - public static readonly RULE_assignStatement = 17; - public static readonly RULE_timeOpStatement = 18; - public static readonly RULE_requireStatement = 19; - public static readonly RULE_consoleStatement = 20; - public static readonly RULE_ifStatement = 21; - public static readonly RULE_loopStatement = 22; - public static readonly RULE_doWhileStatement = 23; - public static readonly RULE_whileStatement = 24; - public static readonly RULE_forStatement = 25; - public static readonly RULE_forInit = 26; - public static readonly RULE_requireMessage = 27; - public static readonly RULE_consoleParameter = 28; - public static readonly RULE_consoleParameterList = 29; - public static readonly RULE_functionCall = 30; - public static readonly RULE_expressionList = 31; - public static readonly RULE_expression = 32; - public static readonly RULE_modifier = 33; - public static readonly RULE_literal = 34; - public static readonly RULE_numberLiteral = 35; - public static readonly RULE_typeName = 36; - public static readonly RULE_typeCast = 37; + public static readonly RULE_importDirective = 6; + public static readonly RULE_topLevelDefinition = 7; + public static readonly RULE_globalFunctionDefinition = 8; + public static readonly RULE_constantDefinition = 9; + public static readonly RULE_contractDefinition = 10; + public static readonly RULE_contractFunctionDefinition = 11; + public static readonly RULE_functionBody = 12; + public static readonly RULE_parameterList = 13; + public static readonly RULE_parameter = 14; + public static readonly RULE_block = 15; + public static readonly RULE_statement = 16; + public static readonly RULE_nonControlStatement = 17; + public static readonly RULE_functionCallStatement = 18; + public static readonly RULE_returnStatement = 19; + public static readonly RULE_controlStatement = 20; + public static readonly RULE_variableDefinition = 21; + public static readonly RULE_tupleAssignment = 22; + public static readonly RULE_tupleTarget = 23; + public static readonly RULE_assignStatement = 24; + public static readonly RULE_timeOpStatement = 25; + public static readonly RULE_requireStatement = 26; + public static readonly RULE_consoleStatement = 27; + public static readonly RULE_ifStatement = 28; + public static readonly RULE_loopStatement = 29; + public static readonly RULE_doWhileStatement = 30; + public static readonly RULE_whileStatement = 31; + public static readonly RULE_forStatement = 32; + public static readonly RULE_forInit = 33; + public static readonly RULE_requireMessage = 34; + public static readonly RULE_consoleParameter = 35; + public static readonly RULE_consoleParameterList = 36; + public static readonly RULE_functionCall = 37; + public static readonly RULE_expressionList = 38; + public static readonly RULE_expression = 39; + public static readonly RULE_modifier = 40; + public static readonly RULE_literal = 41; + public static readonly RULE_numberLiteral = 42; + public static readonly RULE_typeName = 43; + public static readonly RULE_typeCast = 44; public static readonly literalNames: (string | null)[] = [ null, "'pragma'", "';'", "'cashscript'", "'^'", "'~'", "'>='", "'>'", "'<'", "'<='", - "'='", "'contract'", - "'{'", "'}'", + "'='", "'import'", "'function'", + "'returns'", "'('", "','", - "')'", "'+='", - "'-='", "'++'", - "'--'", "'require'", + "')'", "'constant'", + "'contract'", + "'{'", "'}'", + "'return'", + "'+='", "'-='", + "'++'", "'--'", + "'require'", "'console.log'", "'if'", "'else'", "'do'", "'while'", @@ -177,7 +192,7 @@ export default class CashScriptParser extends Parser { "'=='", "'!='", "'&'", "'|'", "'&&'", "'||'", - "'constant'", + "'unused'", null, null, null, null, null, null, @@ -213,6 +228,8 @@ export default class CashScriptParser extends Parser { null, null, null, null, null, null, + null, null, + null, null, "VersionLiteral", "BooleanLiteral", "NumberUnit", @@ -234,14 +251,15 @@ export default class CashScriptParser extends Parser { // tslint:disable:no-trailing-whitespace public static readonly ruleNames: string[] = [ "sourceFile", "pragmaDirective", "pragmaName", "pragmaValue", "versionConstraint", - "versionOperator", "contractDefinition", "functionDefinition", "functionBody", - "parameterList", "parameter", "block", "statement", "nonControlStatement", - "controlStatement", "variableDefinition", "tupleAssignment", "assignStatement", - "timeOpStatement", "requireStatement", "consoleStatement", "ifStatement", - "loopStatement", "doWhileStatement", "whileStatement", "forStatement", - "forInit", "requireMessage", "consoleParameter", "consoleParameterList", - "functionCall", "expressionList", "expression", "modifier", "literal", - "numberLiteral", "typeName", "typeCast", + "versionOperator", "importDirective", "topLevelDefinition", "globalFunctionDefinition", + "constantDefinition", "contractDefinition", "contractFunctionDefinition", + "functionBody", "parameterList", "parameter", "block", "statement", "nonControlStatement", + "functionCallStatement", "returnStatement", "controlStatement", "variableDefinition", + "tupleAssignment", "tupleTarget", "assignStatement", "timeOpStatement", + "requireStatement", "consoleStatement", "ifStatement", "loopStatement", + "doWhileStatement", "whileStatement", "forStatement", "forInit", "requireMessage", + "consoleParameter", "consoleParameterList", "functionCall", "expressionList", + "expression", "modifier", "literal", "numberLiteral", "typeName", "typeCast", ]; public get grammarFileName(): string { return "CashScript.g4"; } public get literalNames(): (string | null)[] { return CashScriptParser.literalNames; } @@ -265,23 +283,49 @@ export default class CashScriptParser extends Parser { try { this.enterOuterAlt(localctx, 1); { - this.state = 79; + this.state = 93; this._errHandler.sync(this); _la = this._input.LA(1); while (_la===1) { { { - this.state = 76; + this.state = 90; this.pragmaDirective(); } } - this.state = 81; + this.state = 95; + this._errHandler.sync(this); + _la = this._input.LA(1); + } + this.state = 99; + this._errHandler.sync(this); + _la = this._input.LA(1); + while (_la===11) { + { + { + this.state = 96; + this.importDirective(); + } + } + this.state = 101; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 82; - this.contractDefinition(); - this.state = 83; + this.state = 105; + this._errHandler.sync(this); + _la = this._input.LA(1); + while (_la===12 || _la===18 || ((((_la - 72)) & ~0x1F) === 0 && ((1 << (_la - 72)) & 7) !== 0)) { + { + { + this.state = 102; + this.topLevelDefinition(); + } + } + this.state = 107; + this._errHandler.sync(this); + _la = this._input.LA(1); + } + this.state = 108; this.match(CashScriptParser.EOF); } } @@ -306,13 +350,13 @@ export default class CashScriptParser extends Parser { try { this.enterOuterAlt(localctx, 1); { - this.state = 85; + this.state = 110; this.match(CashScriptParser.T__0); - this.state = 86; + this.state = 111; this.pragmaName(); - this.state = 87; + this.state = 112; this.pragmaValue(); - this.state = 88; + this.state = 113; this.match(CashScriptParser.T__1); } } @@ -337,7 +381,7 @@ export default class CashScriptParser extends Parser { try { this.enterOuterAlt(localctx, 1); { - this.state = 90; + this.state = 115; this.match(CashScriptParser.T__2); } } @@ -363,14 +407,14 @@ export default class CashScriptParser extends Parser { try { this.enterOuterAlt(localctx, 1); { - this.state = 92; + this.state = 117; this.versionConstraint(); - this.state = 94; + this.state = 119; this._errHandler.sync(this); _la = this._input.LA(1); - if ((((_la) & ~0x1F) === 0 && ((1 << _la) & 2032) !== 0) || _la===62) { + if ((((_la) & ~0x1F) === 0 && ((1 << _la) & 2032) !== 0) || _la===66) { { - this.state = 93; + this.state = 118; this.versionConstraint(); } } @@ -399,17 +443,17 @@ export default class CashScriptParser extends Parser { try { this.enterOuterAlt(localctx, 1); { - this.state = 97; + this.state = 122; this._errHandler.sync(this); _la = this._input.LA(1); if ((((_la) & ~0x1F) === 0 && ((1 << _la) & 2032) !== 0)) { { - this.state = 96; + this.state = 121; this.versionOperator(); } } - this.state = 99; + this.state = 124; this.match(CashScriptParser.VersionLiteral); } } @@ -435,7 +479,7 @@ export default class CashScriptParser extends Parser { try { this.enterOuterAlt(localctx, 1); { - this.state = 101; + this.state = 126; _la = this._input.LA(1); if(!((((_la) & ~0x1F) === 0 && ((1 << _la) & 2032) !== 0))) { this._errHandler.recoverInline(this); @@ -461,37 +505,214 @@ export default class CashScriptParser extends Parser { return localctx; } // @RuleVersion(0) + public importDirective(): ImportDirectiveContext { + let localctx: ImportDirectiveContext = new ImportDirectiveContext(this, this._ctx, this.state); + this.enterRule(localctx, 12, CashScriptParser.RULE_importDirective); + try { + this.enterOuterAlt(localctx, 1); + { + this.state = 128; + this.match(CashScriptParser.T__10); + this.state = 129; + this.match(CashScriptParser.StringLiteral); + this.state = 130; + this.match(CashScriptParser.T__1); + } + } + catch (re) { + if (re instanceof RecognitionException) { + localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return localctx; + } + // @RuleVersion(0) + public topLevelDefinition(): TopLevelDefinitionContext { + let localctx: TopLevelDefinitionContext = new TopLevelDefinitionContext(this, this._ctx, this.state); + this.enterRule(localctx, 14, CashScriptParser.RULE_topLevelDefinition); + try { + this.state = 135; + this._errHandler.sync(this); + switch (this._input.LA(1)) { + case 12: + this.enterOuterAlt(localctx, 1); + { + this.state = 132; + this.globalFunctionDefinition(); + } + break; + case 72: + case 73: + case 74: + this.enterOuterAlt(localctx, 2); + { + this.state = 133; + this.constantDefinition(); + } + break; + case 18: + this.enterOuterAlt(localctx, 3); + { + this.state = 134; + this.contractDefinition(); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (re) { + if (re instanceof RecognitionException) { + localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return localctx; + } + // @RuleVersion(0) + public globalFunctionDefinition(): GlobalFunctionDefinitionContext { + let localctx: GlobalFunctionDefinitionContext = new GlobalFunctionDefinitionContext(this, this._ctx, this.state); + this.enterRule(localctx, 16, CashScriptParser.RULE_globalFunctionDefinition); + let _la: number; + try { + this.enterOuterAlt(localctx, 1); + { + this.state = 137; + this.match(CashScriptParser.T__11); + this.state = 138; + this.match(CashScriptParser.Identifier); + this.state = 139; + this.parameterList(); + this.state = 152; + this._errHandler.sync(this); + _la = this._input.LA(1); + if (_la===13) { + { + this.state = 140; + this.match(CashScriptParser.T__12); + this.state = 141; + this.match(CashScriptParser.T__13); + this.state = 142; + this.typeName(); + this.state = 147; + this._errHandler.sync(this); + _la = this._input.LA(1); + while (_la===15) { + { + { + this.state = 143; + this.match(CashScriptParser.T__14); + this.state = 144; + this.typeName(); + } + } + this.state = 149; + this._errHandler.sync(this); + _la = this._input.LA(1); + } + this.state = 150; + this.match(CashScriptParser.T__15); + } + } + + this.state = 154; + this.functionBody(); + } + } + catch (re) { + if (re instanceof RecognitionException) { + localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return localctx; + } + // @RuleVersion(0) + public constantDefinition(): ConstantDefinitionContext { + let localctx: ConstantDefinitionContext = new ConstantDefinitionContext(this, this._ctx, this.state); + this.enterRule(localctx, 18, CashScriptParser.RULE_constantDefinition); + try { + this.enterOuterAlt(localctx, 1); + { + this.state = 156; + this.typeName(); + this.state = 157; + this.match(CashScriptParser.T__16); + this.state = 158; + this.match(CashScriptParser.Identifier); + this.state = 159; + this.match(CashScriptParser.T__9); + this.state = 160; + this.expression(0); + this.state = 161; + this.match(CashScriptParser.T__1); + } + } + catch (re) { + if (re instanceof RecognitionException) { + localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return localctx; + } + // @RuleVersion(0) public contractDefinition(): ContractDefinitionContext { let localctx: ContractDefinitionContext = new ContractDefinitionContext(this, this._ctx, this.state); - this.enterRule(localctx, 12, CashScriptParser.RULE_contractDefinition); + this.enterRule(localctx, 20, CashScriptParser.RULE_contractDefinition); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 103; - this.match(CashScriptParser.T__10); - this.state = 104; + this.state = 163; + this.match(CashScriptParser.T__17); + this.state = 164; this.match(CashScriptParser.Identifier); - this.state = 105; + this.state = 165; this.parameterList(); - this.state = 106; - this.match(CashScriptParser.T__11); - this.state = 110; + this.state = 166; + this.match(CashScriptParser.T__18); + this.state = 170; this._errHandler.sync(this); _la = this._input.LA(1); - while (_la===14) { + while (_la===12) { { { - this.state = 107; - this.functionDefinition(); + this.state = 167; + this.contractFunctionDefinition(); } } - this.state = 112; + this.state = 172; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 113; - this.match(CashScriptParser.T__12); + this.state = 173; + this.match(CashScriptParser.T__19); } } catch (re) { @@ -509,19 +730,19 @@ export default class CashScriptParser extends Parser { return localctx; } // @RuleVersion(0) - public functionDefinition(): FunctionDefinitionContext { - let localctx: FunctionDefinitionContext = new FunctionDefinitionContext(this, this._ctx, this.state); - this.enterRule(localctx, 14, CashScriptParser.RULE_functionDefinition); + public contractFunctionDefinition(): ContractFunctionDefinitionContext { + let localctx: ContractFunctionDefinitionContext = new ContractFunctionDefinitionContext(this, this._ctx, this.state); + this.enterRule(localctx, 22, CashScriptParser.RULE_contractFunctionDefinition); try { this.enterOuterAlt(localctx, 1); { - this.state = 115; - this.match(CashScriptParser.T__13); - this.state = 116; + this.state = 175; + this.match(CashScriptParser.T__11); + this.state = 176; this.match(CashScriptParser.Identifier); - this.state = 117; + this.state = 177; this.parameterList(); - this.state = 118; + this.state = 178; this.functionBody(); } } @@ -542,29 +763,29 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public functionBody(): FunctionBodyContext { let localctx: FunctionBodyContext = new FunctionBodyContext(this, this._ctx, this.state); - this.enterRule(localctx, 16, CashScriptParser.RULE_functionBody); + this.enterRule(localctx, 24, CashScriptParser.RULE_functionBody); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 120; - this.match(CashScriptParser.T__11); - this.state = 124; + this.state = 180; + this.match(CashScriptParser.T__18); + this.state = 184; this._errHandler.sync(this); _la = this._input.LA(1); - while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 499122176) !== 0) || ((((_la - 68)) & ~0x1F) === 0 && ((1 << (_la - 68)) & 1031) !== 0)) { + while (((((_la - 14)) & ~0x1F) === 0 && ((1 << (_la - 14)) & 487553) !== 0) || ((((_la - 72)) & ~0x1F) === 0 && ((1 << (_la - 72)) & 1031) !== 0)) { { { - this.state = 121; + this.state = 181; this.statement(); } } - this.state = 126; + this.state = 186; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 127; - this.match(CashScriptParser.T__12); + this.state = 187; + this.match(CashScriptParser.T__19); } } catch (re) { @@ -584,54 +805,54 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public parameterList(): ParameterListContext { let localctx: ParameterListContext = new ParameterListContext(this, this._ctx, this.state); - this.enterRule(localctx, 18, CashScriptParser.RULE_parameterList); + this.enterRule(localctx, 26, CashScriptParser.RULE_parameterList); let _la: number; try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 129; - this.match(CashScriptParser.T__14); - this.state = 141; + this.state = 189; + this.match(CashScriptParser.T__13); + this.state = 201; this._errHandler.sync(this); _la = this._input.LA(1); - if (((((_la - 68)) & ~0x1F) === 0 && ((1 << (_la - 68)) & 7) !== 0)) { + if (((((_la - 72)) & ~0x1F) === 0 && ((1 << (_la - 72)) & 7) !== 0)) { { - this.state = 130; + this.state = 190; this.parameter(); - this.state = 135; + this.state = 195; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 5, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 10, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 131; - this.match(CashScriptParser.T__15); - this.state = 132; + this.state = 191; + this.match(CashScriptParser.T__14); + this.state = 192; this.parameter(); } } } - this.state = 137; + this.state = 197; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 5, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 10, this._ctx); } - this.state = 139; + this.state = 199; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===16) { + if (_la===15) { { - this.state = 138; - this.match(CashScriptParser.T__15); + this.state = 198; + this.match(CashScriptParser.T__14); } } } } - this.state = 143; - this.match(CashScriptParser.T__16); + this.state = 203; + this.match(CashScriptParser.T__15); } } catch (re) { @@ -651,13 +872,28 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public parameter(): ParameterContext { let localctx: ParameterContext = new ParameterContext(this, this._ctx, this.state); - this.enterRule(localctx, 20, CashScriptParser.RULE_parameter); + this.enterRule(localctx, 28, CashScriptParser.RULE_parameter); + let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 145; + this.state = 205; this.typeName(); - this.state = 146; + this.state = 209; + this._errHandler.sync(this); + _la = this._input.LA(1); + while (_la===17 || _la===65) { + { + { + this.state = 206; + this.modifier(); + } + } + this.state = 211; + this._errHandler.sync(this); + _la = this._input.LA(1); + } + this.state = 212; this.match(CashScriptParser.Identifier); } } @@ -678,48 +914,50 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public block(): BlockContext { let localctx: BlockContext = new BlockContext(this, this._ctx, this.state); - this.enterRule(localctx, 22, CashScriptParser.RULE_block); + this.enterRule(localctx, 30, CashScriptParser.RULE_block); let _la: number; try { - this.state = 157; + this.state = 223; this._errHandler.sync(this); switch (this._input.LA(1)) { - case 12: + case 19: this.enterOuterAlt(localctx, 1); { - this.state = 148; - this.match(CashScriptParser.T__11); - this.state = 152; + this.state = 214; + this.match(CashScriptParser.T__18); + this.state = 218; this._errHandler.sync(this); _la = this._input.LA(1); - while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 499122176) !== 0) || ((((_la - 68)) & ~0x1F) === 0 && ((1 << (_la - 68)) & 1031) !== 0)) { + while (((((_la - 14)) & ~0x1F) === 0 && ((1 << (_la - 14)) & 487553) !== 0) || ((((_la - 72)) & ~0x1F) === 0 && ((1 << (_la - 72)) & 1031) !== 0)) { { { - this.state = 149; + this.state = 215; this.statement(); } } - this.state = 154; + this.state = 220; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 155; - this.match(CashScriptParser.T__12); + this.state = 221; + this.match(CashScriptParser.T__19); } break; - case 22: - case 23: - case 24: + case 14: + case 21: case 26: case 27: case 28: - case 68: - case 69: - case 70: - case 78: + case 30: + case 31: + case 32: + case 72: + case 73: + case 74: + case 82: this.enterOuterAlt(localctx, 2); { - this.state = 156; + this.state = 222; this.statement(); } break; @@ -744,32 +982,34 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public statement(): StatementContext { let localctx: StatementContext = new StatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 24, CashScriptParser.RULE_statement); + this.enterRule(localctx, 32, CashScriptParser.RULE_statement); try { - this.state = 163; + this.state = 229; this._errHandler.sync(this); switch (this._input.LA(1)) { - case 24: - case 26: - case 27: case 28: + case 30: + case 31: + case 32: this.enterOuterAlt(localctx, 1); { - this.state = 159; + this.state = 225; this.controlStatement(); } break; - case 22: - case 23: - case 68: - case 69: - case 70: - case 78: + case 14: + case 21: + case 26: + case 27: + case 72: + case 73: + case 74: + case 82: this.enterOuterAlt(localctx, 2); { - this.state = 160; + this.state = 226; this.nonControlStatement(); - this.state = 161; + this.state = 227; this.match(CashScriptParser.T__1); } break; @@ -794,53 +1034,136 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public nonControlStatement(): NonControlStatementContext { let localctx: NonControlStatementContext = new NonControlStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 26, CashScriptParser.RULE_nonControlStatement); + this.enterRule(localctx, 34, CashScriptParser.RULE_nonControlStatement); try { - this.state = 171; + this.state = 239; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 11, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 17, this._ctx) ) { case 1: this.enterOuterAlt(localctx, 1); { - this.state = 165; + this.state = 231; this.variableDefinition(); } break; case 2: this.enterOuterAlt(localctx, 2); { - this.state = 166; + this.state = 232; this.tupleAssignment(); } break; case 3: this.enterOuterAlt(localctx, 3); { - this.state = 167; + this.state = 233; this.assignStatement(); } break; case 4: this.enterOuterAlt(localctx, 4); { - this.state = 168; + this.state = 234; this.timeOpStatement(); } break; case 5: this.enterOuterAlt(localctx, 5); { - this.state = 169; + this.state = 235; this.requireStatement(); } break; case 6: this.enterOuterAlt(localctx, 6); { - this.state = 170; + this.state = 236; + this.functionCallStatement(); + } + break; + case 7: + this.enterOuterAlt(localctx, 7); + { + this.state = 237; this.consoleStatement(); } break; + case 8: + this.enterOuterAlt(localctx, 8); + { + this.state = 238; + this.returnStatement(); + } + break; + } + } + catch (re) { + if (re instanceof RecognitionException) { + localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return localctx; + } + // @RuleVersion(0) + public functionCallStatement(): FunctionCallStatementContext { + let localctx: FunctionCallStatementContext = new FunctionCallStatementContext(this, this._ctx, this.state); + this.enterRule(localctx, 36, CashScriptParser.RULE_functionCallStatement); + try { + this.enterOuterAlt(localctx, 1); + { + this.state = 241; + this.functionCall(); + } + } + catch (re) { + if (re instanceof RecognitionException) { + localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return localctx; + } + // @RuleVersion(0) + public returnStatement(): ReturnStatementContext { + let localctx: ReturnStatementContext = new ReturnStatementContext(this, this._ctx, this.state); + this.enterRule(localctx, 38, CashScriptParser.RULE_returnStatement); + let _la: number; + try { + this.enterOuterAlt(localctx, 1); + { + this.state = 243; + this.match(CashScriptParser.T__20); + this.state = 244; + this.expression(0); + this.state = 249; + this._errHandler.sync(this); + _la = this._input.LA(1); + while (_la===15) { + { + { + this.state = 245; + this.match(CashScriptParser.T__14); + this.state = 246; + this.expression(0); + } + } + this.state = 251; + this._errHandler.sync(this); + _la = this._input.LA(1); + } } } catch (re) { @@ -860,24 +1183,24 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public controlStatement(): ControlStatementContext { let localctx: ControlStatementContext = new ControlStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 28, CashScriptParser.RULE_controlStatement); + this.enterRule(localctx, 40, CashScriptParser.RULE_controlStatement); try { - this.state = 175; + this.state = 254; this._errHandler.sync(this); switch (this._input.LA(1)) { - case 24: + case 28: this.enterOuterAlt(localctx, 1); { - this.state = 173; + this.state = 252; this.ifStatement(); } break; - case 26: - case 27: - case 28: + case 30: + case 31: + case 32: this.enterOuterAlt(localctx, 2); { - this.state = 174; + this.state = 253; this.loopStatement(); } break; @@ -902,32 +1225,32 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public variableDefinition(): VariableDefinitionContext { let localctx: VariableDefinitionContext = new VariableDefinitionContext(this, this._ctx, this.state); - this.enterRule(localctx, 30, CashScriptParser.RULE_variableDefinition); + this.enterRule(localctx, 42, CashScriptParser.RULE_variableDefinition); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 177; + this.state = 256; this.typeName(); - this.state = 181; + this.state = 260; this._errHandler.sync(this); _la = this._input.LA(1); - while (_la===61) { + while (_la===17 || _la===65) { { { - this.state = 178; + this.state = 257; this.modifier(); } } - this.state = 183; + this.state = 262; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 184; + this.state = 263; this.match(CashScriptParser.Identifier); - this.state = 185; + this.state = 264; this.match(CashScriptParser.T__9); - this.state = 186; + this.state = 265; this.expression(0); } } @@ -948,24 +1271,134 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public tupleAssignment(): TupleAssignmentContext { let localctx: TupleAssignmentContext = new TupleAssignmentContext(this, this._ctx, this.state); - this.enterRule(localctx, 32, CashScriptParser.RULE_tupleAssignment); + this.enterRule(localctx, 44, CashScriptParser.RULE_tupleAssignment); + let _la: number; try { - this.enterOuterAlt(localctx, 1); - { - this.state = 188; - this.typeName(); - this.state = 189; - this.match(CashScriptParser.Identifier); - this.state = 190; - this.match(CashScriptParser.T__15); - this.state = 191; - this.typeName(); - this.state = 192; - this.match(CashScriptParser.Identifier); - this.state = 193; - this.match(CashScriptParser.T__9); - this.state = 194; - this.expression(0); + this.state = 289; + this._errHandler.sync(this); + switch (this._input.LA(1)) { + case 72: + case 73: + case 74: + case 82: + this.enterOuterAlt(localctx, 1); + { + this.state = 267; + this.tupleTarget(); + this.state = 270; + this._errHandler.sync(this); + _la = this._input.LA(1); + do { + { + { + this.state = 268; + this.match(CashScriptParser.T__14); + this.state = 269; + this.tupleTarget(); + } + } + this.state = 272; + this._errHandler.sync(this); + _la = this._input.LA(1); + } while (_la===15); + this.state = 274; + this.match(CashScriptParser.T__9); + this.state = 275; + this.expression(0); + } + break; + case 14: + this.enterOuterAlt(localctx, 2); + { + this.state = 277; + this.match(CashScriptParser.T__13); + this.state = 278; + this.tupleTarget(); + this.state = 281; + this._errHandler.sync(this); + _la = this._input.LA(1); + do { + { + { + this.state = 279; + this.match(CashScriptParser.T__14); + this.state = 280; + this.tupleTarget(); + } + } + this.state = 283; + this._errHandler.sync(this); + _la = this._input.LA(1); + } while (_la===15); + this.state = 285; + this.match(CashScriptParser.T__15); + this.state = 286; + this.match(CashScriptParser.T__9); + this.state = 287; + this.expression(0); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (re) { + if (re instanceof RecognitionException) { + localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return localctx; + } + // @RuleVersion(0) + public tupleTarget(): TupleTargetContext { + let localctx: TupleTargetContext = new TupleTargetContext(this, this._ctx, this.state); + this.enterRule(localctx, 46, CashScriptParser.RULE_tupleTarget); + let _la: number; + try { + this.state = 301; + this._errHandler.sync(this); + switch (this._input.LA(1)) { + case 72: + case 73: + case 74: + this.enterOuterAlt(localctx, 1); + { + this.state = 291; + this.typeName(); + this.state = 295; + this._errHandler.sync(this); + _la = this._input.LA(1); + while (_la===17 || _la===65) { + { + { + this.state = 292; + this.modifier(); + } + } + this.state = 297; + this._errHandler.sync(this); + _la = this._input.LA(1); + } + this.state = 298; + this.match(CashScriptParser.Identifier); + } + break; + case 82: + this.enterOuterAlt(localctx, 2); + { + this.state = 300; + this.match(CashScriptParser.Identifier); + } + break; + default: + throw new NoViableAltException(this); } } catch (re) { @@ -985,40 +1418,40 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public assignStatement(): AssignStatementContext { let localctx: AssignStatementContext = new AssignStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 34, CashScriptParser.RULE_assignStatement); + this.enterRule(localctx, 48, CashScriptParser.RULE_assignStatement); let _la: number; try { - this.state = 201; + this.state = 308; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 14, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 26, this._ctx) ) { case 1: this.enterOuterAlt(localctx, 1); { - this.state = 196; + this.state = 303; this.match(CashScriptParser.Identifier); - this.state = 197; + this.state = 304; localctx._op = this._input.LT(1); _la = this._input.LA(1); - if(!((((_la) & ~0x1F) === 0 && ((1 << _la) & 787456) !== 0))) { + if(!((((_la) & ~0x1F) === 0 && ((1 << _la) & 12583936) !== 0))) { localctx._op = this._errHandler.recoverInline(this); } else { this._errHandler.reportMatch(this); this.consume(); } - this.state = 198; + this.state = 305; this.expression(0); } break; case 2: this.enterOuterAlt(localctx, 2); { - this.state = 199; + this.state = 306; this.match(CashScriptParser.Identifier); - this.state = 200; + this.state = 307; localctx._op = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===20 || _la===21)) { + if(!(_la===24 || _la===25)) { localctx._op = this._errHandler.recoverInline(this); } else { @@ -1046,35 +1479,35 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public timeOpStatement(): TimeOpStatementContext { let localctx: TimeOpStatementContext = new TimeOpStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 36, CashScriptParser.RULE_timeOpStatement); + this.enterRule(localctx, 50, CashScriptParser.RULE_timeOpStatement); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 203; - this.match(CashScriptParser.T__21); - this.state = 204; - this.match(CashScriptParser.T__14); - this.state = 205; + this.state = 310; + this.match(CashScriptParser.T__25); + this.state = 311; + this.match(CashScriptParser.T__13); + this.state = 312; this.match(CashScriptParser.TxVar); - this.state = 206; + this.state = 313; this.match(CashScriptParser.T__5); - this.state = 207; + this.state = 314; this.expression(0); - this.state = 210; + this.state = 317; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===16) { + if (_la===15) { { - this.state = 208; - this.match(CashScriptParser.T__15); - this.state = 209; + this.state = 315; + this.match(CashScriptParser.T__14); + this.state = 316; this.requireMessage(); } } - this.state = 212; - this.match(CashScriptParser.T__16); + this.state = 319; + this.match(CashScriptParser.T__15); } } catch (re) { @@ -1094,31 +1527,31 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public requireStatement(): RequireStatementContext { let localctx: RequireStatementContext = new RequireStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 38, CashScriptParser.RULE_requireStatement); + this.enterRule(localctx, 52, CashScriptParser.RULE_requireStatement); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 214; - this.match(CashScriptParser.T__21); - this.state = 215; - this.match(CashScriptParser.T__14); - this.state = 216; + this.state = 321; + this.match(CashScriptParser.T__25); + this.state = 322; + this.match(CashScriptParser.T__13); + this.state = 323; this.expression(0); - this.state = 219; + this.state = 326; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===16) { + if (_la===15) { { - this.state = 217; - this.match(CashScriptParser.T__15); - this.state = 218; + this.state = 324; + this.match(CashScriptParser.T__14); + this.state = 325; this.requireMessage(); } } - this.state = 221; - this.match(CashScriptParser.T__16); + this.state = 328; + this.match(CashScriptParser.T__15); } } catch (re) { @@ -1138,13 +1571,13 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public consoleStatement(): ConsoleStatementContext { let localctx: ConsoleStatementContext = new ConsoleStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 40, CashScriptParser.RULE_consoleStatement); + this.enterRule(localctx, 54, CashScriptParser.RULE_consoleStatement); try { this.enterOuterAlt(localctx, 1); { - this.state = 223; - this.match(CashScriptParser.T__22); - this.state = 224; + this.state = 330; + this.match(CashScriptParser.T__26); + this.state = 331; this.consoleParameterList(); } } @@ -1165,28 +1598,28 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public ifStatement(): IfStatementContext { let localctx: IfStatementContext = new IfStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 42, CashScriptParser.RULE_ifStatement); + this.enterRule(localctx, 56, CashScriptParser.RULE_ifStatement); try { this.enterOuterAlt(localctx, 1); { - this.state = 226; - this.match(CashScriptParser.T__23); - this.state = 227; - this.match(CashScriptParser.T__14); - this.state = 228; + this.state = 333; + this.match(CashScriptParser.T__27); + this.state = 334; + this.match(CashScriptParser.T__13); + this.state = 335; this.expression(0); - this.state = 229; - this.match(CashScriptParser.T__16); - this.state = 230; + this.state = 336; + this.match(CashScriptParser.T__15); + this.state = 337; localctx._ifBlock = this.block(); - this.state = 233; + this.state = 340; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 17, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 29, this._ctx) ) { case 1: { - this.state = 231; - this.match(CashScriptParser.T__24); - this.state = 232; + this.state = 338; + this.match(CashScriptParser.T__28); + this.state = 339; localctx._elseBlock = this.block(); } break; @@ -1210,29 +1643,29 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public loopStatement(): LoopStatementContext { let localctx: LoopStatementContext = new LoopStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 44, CashScriptParser.RULE_loopStatement); + this.enterRule(localctx, 58, CashScriptParser.RULE_loopStatement); try { - this.state = 238; + this.state = 345; this._errHandler.sync(this); switch (this._input.LA(1)) { - case 26: + case 30: this.enterOuterAlt(localctx, 1); { - this.state = 235; + this.state = 342; this.doWhileStatement(); } break; - case 27: + case 31: this.enterOuterAlt(localctx, 2); { - this.state = 236; + this.state = 343; this.whileStatement(); } break; - case 28: + case 32: this.enterOuterAlt(localctx, 3); { - this.state = 237; + this.state = 344; this.forStatement(); } break; @@ -1257,23 +1690,23 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public doWhileStatement(): DoWhileStatementContext { let localctx: DoWhileStatementContext = new DoWhileStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 46, CashScriptParser.RULE_doWhileStatement); + this.enterRule(localctx, 60, CashScriptParser.RULE_doWhileStatement); try { this.enterOuterAlt(localctx, 1); { - this.state = 240; - this.match(CashScriptParser.T__25); - this.state = 241; + this.state = 347; + this.match(CashScriptParser.T__29); + this.state = 348; this.block(); - this.state = 242; - this.match(CashScriptParser.T__26); - this.state = 243; - this.match(CashScriptParser.T__14); - this.state = 244; + this.state = 349; + this.match(CashScriptParser.T__30); + this.state = 350; + this.match(CashScriptParser.T__13); + this.state = 351; this.expression(0); - this.state = 245; - this.match(CashScriptParser.T__16); - this.state = 246; + this.state = 352; + this.match(CashScriptParser.T__15); + this.state = 353; this.match(CashScriptParser.T__1); } } @@ -1294,19 +1727,19 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public whileStatement(): WhileStatementContext { let localctx: WhileStatementContext = new WhileStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 48, CashScriptParser.RULE_whileStatement); + this.enterRule(localctx, 62, CashScriptParser.RULE_whileStatement); try { this.enterOuterAlt(localctx, 1); { - this.state = 248; - this.match(CashScriptParser.T__26); - this.state = 249; - this.match(CashScriptParser.T__14); - this.state = 250; + this.state = 355; + this.match(CashScriptParser.T__30); + this.state = 356; + this.match(CashScriptParser.T__13); + this.state = 357; this.expression(0); - this.state = 251; - this.match(CashScriptParser.T__16); - this.state = 252; + this.state = 358; + this.match(CashScriptParser.T__15); + this.state = 359; this.block(); } } @@ -1327,27 +1760,27 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public forStatement(): ForStatementContext { let localctx: ForStatementContext = new ForStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 50, CashScriptParser.RULE_forStatement); + this.enterRule(localctx, 64, CashScriptParser.RULE_forStatement); try { this.enterOuterAlt(localctx, 1); { - this.state = 254; - this.match(CashScriptParser.T__27); - this.state = 255; - this.match(CashScriptParser.T__14); - this.state = 256; + this.state = 361; + this.match(CashScriptParser.T__31); + this.state = 362; + this.match(CashScriptParser.T__13); + this.state = 363; this.forInit(); - this.state = 257; + this.state = 364; this.match(CashScriptParser.T__1); - this.state = 258; + this.state = 365; this.expression(0); - this.state = 259; + this.state = 366; this.match(CashScriptParser.T__1); - this.state = 260; + this.state = 367; this.assignStatement(); - this.state = 261; - this.match(CashScriptParser.T__16); - this.state = 262; + this.state = 368; + this.match(CashScriptParser.T__15); + this.state = 369; this.block(); } } @@ -1368,24 +1801,24 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public forInit(): ForInitContext { let localctx: ForInitContext = new ForInitContext(this, this._ctx, this.state); - this.enterRule(localctx, 52, CashScriptParser.RULE_forInit); + this.enterRule(localctx, 66, CashScriptParser.RULE_forInit); try { - this.state = 266; + this.state = 373; this._errHandler.sync(this); switch (this._input.LA(1)) { - case 68: - case 69: - case 70: + case 72: + case 73: + case 74: this.enterOuterAlt(localctx, 1); { - this.state = 264; + this.state = 371; this.variableDefinition(); } break; - case 78: + case 82: this.enterOuterAlt(localctx, 2); { - this.state = 265; + this.state = 372; this.assignStatement(); } break; @@ -1410,11 +1843,11 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public requireMessage(): RequireMessageContext { let localctx: RequireMessageContext = new RequireMessageContext(this, this._ctx, this.state); - this.enterRule(localctx, 54, CashScriptParser.RULE_requireMessage); + this.enterRule(localctx, 68, CashScriptParser.RULE_requireMessage); try { this.enterOuterAlt(localctx, 1); { - this.state = 268; + this.state = 375; this.match(CashScriptParser.StringLiteral); } } @@ -1435,26 +1868,26 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public consoleParameter(): ConsoleParameterContext { let localctx: ConsoleParameterContext = new ConsoleParameterContext(this, this._ctx, this.state); - this.enterRule(localctx, 56, CashScriptParser.RULE_consoleParameter); + this.enterRule(localctx, 70, CashScriptParser.RULE_consoleParameter); try { - this.state = 272; + this.state = 379; this._errHandler.sync(this); switch (this._input.LA(1)) { - case 78: + case 82: this.enterOuterAlt(localctx, 1); { - this.state = 270; + this.state = 377; this.match(CashScriptParser.Identifier); } break; - case 63: - case 65: - case 72: - case 73: - case 74: + case 67: + case 69: + case 76: + case 77: + case 78: this.enterOuterAlt(localctx, 2); { - this.state = 271; + this.state = 378; this.literal(); } break; @@ -1479,54 +1912,54 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public consoleParameterList(): ConsoleParameterListContext { let localctx: ConsoleParameterListContext = new ConsoleParameterListContext(this, this._ctx, this.state); - this.enterRule(localctx, 58, CashScriptParser.RULE_consoleParameterList); + this.enterRule(localctx, 72, CashScriptParser.RULE_consoleParameterList); let _la: number; try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 274; - this.match(CashScriptParser.T__14); - this.state = 286; + this.state = 381; + this.match(CashScriptParser.T__13); + this.state = 393; this._errHandler.sync(this); _la = this._input.LA(1); - if (((((_la - 63)) & ~0x1F) === 0 && ((1 << (_la - 63)) & 36357) !== 0)) { + if (((((_la - 67)) & ~0x1F) === 0 && ((1 << (_la - 67)) & 36357) !== 0)) { { - this.state = 275; + this.state = 382; this.consoleParameter(); - this.state = 280; + this.state = 387; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 21, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 33, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 276; - this.match(CashScriptParser.T__15); - this.state = 277; + this.state = 383; + this.match(CashScriptParser.T__14); + this.state = 384; this.consoleParameter(); } } } - this.state = 282; + this.state = 389; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 21, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 33, this._ctx); } - this.state = 284; + this.state = 391; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===16) { + if (_la===15) { { - this.state = 283; - this.match(CashScriptParser.T__15); + this.state = 390; + this.match(CashScriptParser.T__14); } } } } - this.state = 288; - this.match(CashScriptParser.T__16); + this.state = 395; + this.match(CashScriptParser.T__15); } } catch (re) { @@ -1546,13 +1979,13 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public functionCall(): FunctionCallContext { let localctx: FunctionCallContext = new FunctionCallContext(this, this._ctx, this.state); - this.enterRule(localctx, 60, CashScriptParser.RULE_functionCall); + this.enterRule(localctx, 74, CashScriptParser.RULE_functionCall); try { this.enterOuterAlt(localctx, 1); { - this.state = 290; + this.state = 397; this.match(CashScriptParser.Identifier); - this.state = 291; + this.state = 398; this.expressionList(); } } @@ -1573,54 +2006,54 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public expressionList(): ExpressionListContext { let localctx: ExpressionListContext = new ExpressionListContext(this, this._ctx, this.state); - this.enterRule(localctx, 62, CashScriptParser.RULE_expressionList); + this.enterRule(localctx, 76, CashScriptParser.RULE_expressionList); let _la: number; try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 293; - this.match(CashScriptParser.T__14); - this.state = 305; + this.state = 400; + this.match(CashScriptParser.T__13); + this.state = 412; this._errHandler.sync(this); _la = this._input.LA(1); - if ((((_la) & ~0x1F) === 0 && ((1 << _la) & 1610645536) !== 0) || ((((_la - 32)) & ~0x1F) === 0 && ((1 << (_la - 32)) & 2147582017) !== 0) || ((((_la - 65)) & ~0x1F) === 0 && ((1 << (_la - 65)) & 15257) !== 0)) { + if (_la===5 || _la===14 || ((((_la - 33)) & ~0x1F) === 0 && ((1 << (_la - 33)) & 786955) !== 0) || ((((_la - 67)) & ~0x1F) === 0 && ((1 << (_la - 67)) & 61029) !== 0)) { { - this.state = 294; + this.state = 401; this.expression(0); - this.state = 299; + this.state = 406; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 24, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 36, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 295; - this.match(CashScriptParser.T__15); - this.state = 296; + this.state = 402; + this.match(CashScriptParser.T__14); + this.state = 403; this.expression(0); } } } - this.state = 301; + this.state = 408; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 24, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 36, this._ctx); } - this.state = 303; + this.state = 410; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===16) { + if (_la===15) { { - this.state = 302; - this.match(CashScriptParser.T__15); + this.state = 409; + this.match(CashScriptParser.T__14); } } } } - this.state = 307; - this.match(CashScriptParser.T__16); + this.state = 414; + this.match(CashScriptParser.T__15); } } catch (re) { @@ -1650,28 +2083,28 @@ export default class CashScriptParser extends Parser { let _parentState: number = this.state; let localctx: ExpressionContext = new ExpressionContext(this, this._ctx, _parentState); let _prevctx: ExpressionContext = localctx; - let _startState: number = 64; - this.enterRecursionRule(localctx, 64, CashScriptParser.RULE_expression, _p); + let _startState: number = 78; + this.enterRecursionRule(localctx, 78, CashScriptParser.RULE_expression, _p); let _la: number; try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 358; + this.state = 465; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 31, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 43, this._ctx) ) { case 1: { localctx = new ParenthesisedContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 310; - this.match(CashScriptParser.T__14); - this.state = 311; + this.state = 417; + this.match(CashScriptParser.T__13); + this.state = 418; this.expression(0); - this.state = 312; - this.match(CashScriptParser.T__16); + this.state = 419; + this.match(CashScriptParser.T__15); } break; case 2: @@ -1679,24 +2112,24 @@ export default class CashScriptParser extends Parser { localctx = new CastContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 314; + this.state = 421; this.typeCast(); - this.state = 315; - this.match(CashScriptParser.T__14); - this.state = 316; + this.state = 422; + this.match(CashScriptParser.T__13); + this.state = 423; (localctx as CastContext)._castable = this.expression(0); - this.state = 318; + this.state = 425; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===16) { + if (_la===15) { { - this.state = 317; - this.match(CashScriptParser.T__15); + this.state = 424; + this.match(CashScriptParser.T__14); } } - this.state = 320; - this.match(CashScriptParser.T__16); + this.state = 427; + this.match(CashScriptParser.T__15); } break; case 3: @@ -1704,7 +2137,7 @@ export default class CashScriptParser extends Parser { localctx = new FunctionCallExpressionContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 322; + this.state = 429; this.functionCall(); } break; @@ -1713,11 +2146,11 @@ export default class CashScriptParser extends Parser { localctx = new InstantiationContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 323; - this.match(CashScriptParser.T__28); - this.state = 324; + this.state = 430; + this.match(CashScriptParser.T__32); + this.state = 431; this.match(CashScriptParser.Identifier); - this.state = 325; + this.state = 432; this.expressionList(); } break; @@ -1726,18 +2159,18 @@ export default class CashScriptParser extends Parser { localctx = new UnaryIntrospectionOpContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 326; - (localctx as UnaryIntrospectionOpContext)._scope = this.match(CashScriptParser.T__31); - this.state = 327; - this.match(CashScriptParser.T__29); - this.state = 328; + this.state = 433; + (localctx as UnaryIntrospectionOpContext)._scope = this.match(CashScriptParser.T__35); + this.state = 434; + this.match(CashScriptParser.T__33); + this.state = 435; this.expression(0); - this.state = 329; - this.match(CashScriptParser.T__30); - this.state = 330; + this.state = 436; + this.match(CashScriptParser.T__34); + this.state = 437; (localctx as UnaryIntrospectionOpContext)._op = this._input.LT(1); _la = this._input.LA(1); - if(!(((((_la - 33)) & ~0x1F) === 0 && ((1 << (_la - 33)) & 31) !== 0))) { + if(!(((((_la - 37)) & ~0x1F) === 0 && ((1 << (_la - 37)) & 31) !== 0))) { (localctx as UnaryIntrospectionOpContext)._op = this._errHandler.recoverInline(this); } else { @@ -1751,18 +2184,18 @@ export default class CashScriptParser extends Parser { localctx = new UnaryIntrospectionOpContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 332; - (localctx as UnaryIntrospectionOpContext)._scope = this.match(CashScriptParser.T__37); - this.state = 333; - this.match(CashScriptParser.T__29); - this.state = 334; + this.state = 439; + (localctx as UnaryIntrospectionOpContext)._scope = this.match(CashScriptParser.T__41); + this.state = 440; + this.match(CashScriptParser.T__33); + this.state = 441; this.expression(0); - this.state = 335; - this.match(CashScriptParser.T__30); - this.state = 336; + this.state = 442; + this.match(CashScriptParser.T__34); + this.state = 443; (localctx as UnaryIntrospectionOpContext)._op = this._input.LT(1); _la = this._input.LA(1); - if(!(((((_la - 33)) & ~0x1F) === 0 && ((1 << (_la - 33)) & 991) !== 0))) { + if(!(((((_la - 37)) & ~0x1F) === 0 && ((1 << (_la - 37)) & 991) !== 0))) { (localctx as UnaryIntrospectionOpContext)._op = this._errHandler.recoverInline(this); } else { @@ -1776,17 +2209,17 @@ export default class CashScriptParser extends Parser { localctx = new UnaryOpContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 338; + this.state = 445; (localctx as UnaryOpContext)._op = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===5 || _la===47 || _la===48)) { + if(!(_la===5 || _la===51 || _la===52)) { (localctx as UnaryOpContext)._op = this._errHandler.recoverInline(this); } else { this._errHandler.reportMatch(this); this.consume(); } - this.state = 339; + this.state = 446; this.expression(15); } break; @@ -1795,48 +2228,48 @@ export default class CashScriptParser extends Parser { localctx = new ArrayContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 340; - this.match(CashScriptParser.T__29); - this.state = 352; + this.state = 447; + this.match(CashScriptParser.T__33); + this.state = 459; this._errHandler.sync(this); _la = this._input.LA(1); - if ((((_la) & ~0x1F) === 0 && ((1 << _la) & 1610645536) !== 0) || ((((_la - 32)) & ~0x1F) === 0 && ((1 << (_la - 32)) & 2147582017) !== 0) || ((((_la - 65)) & ~0x1F) === 0 && ((1 << (_la - 65)) & 15257) !== 0)) { + if (_la===5 || _la===14 || ((((_la - 33)) & ~0x1F) === 0 && ((1 << (_la - 33)) & 786955) !== 0) || ((((_la - 67)) & ~0x1F) === 0 && ((1 << (_la - 67)) & 61029) !== 0)) { { - this.state = 341; + this.state = 448; this.expression(0); - this.state = 346; + this.state = 453; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 28, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 40, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 342; - this.match(CashScriptParser.T__15); - this.state = 343; + this.state = 449; + this.match(CashScriptParser.T__14); + this.state = 450; this.expression(0); } } } - this.state = 348; + this.state = 455; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 28, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 40, this._ctx); } - this.state = 350; + this.state = 457; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===16) { + if (_la===15) { { - this.state = 349; - this.match(CashScriptParser.T__15); + this.state = 456; + this.match(CashScriptParser.T__14); } } } } - this.state = 354; - this.match(CashScriptParser.T__30); + this.state = 461; + this.match(CashScriptParser.T__34); } break; case 9: @@ -1844,7 +2277,7 @@ export default class CashScriptParser extends Parser { localctx = new NullaryOpContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 355; + this.state = 462; this.match(CashScriptParser.NullaryOp); } break; @@ -1853,7 +2286,7 @@ export default class CashScriptParser extends Parser { localctx = new IdentifierContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 356; + this.state = 463; this.match(CashScriptParser.Identifier); } break; @@ -1862,15 +2295,15 @@ export default class CashScriptParser extends Parser { localctx = new LiteralExpressionContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 357; + this.state = 464; this.literal(); } break; } this._ctx.stop = this._input.LT(-1); - this.state = 412; + this.state = 519; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 33, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 45, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { if (this._parseListeners != null) { @@ -1878,29 +2311,29 @@ export default class CashScriptParser extends Parser { } _prevctx = localctx; { - this.state = 410; + this.state = 517; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 32, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 44, this._ctx) ) { case 1: { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 360; + this.state = 467; if (!(this.precpred(this._ctx, 14))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 14)"); } - this.state = 361; + this.state = 468; (localctx as BinaryOpContext)._op = this._input.LT(1); _la = this._input.LA(1); - if(!(((((_la - 49)) & ~0x1F) === 0 && ((1 << (_la - 49)) & 7) !== 0))) { + if(!(((((_la - 53)) & ~0x1F) === 0 && ((1 << (_la - 53)) & 7) !== 0))) { (localctx as BinaryOpContext)._op = this._errHandler.recoverInline(this); } else { this._errHandler.reportMatch(this); this.consume(); } - this.state = 362; + this.state = 469; (localctx as BinaryOpContext)._right = this.expression(15); } break; @@ -1909,21 +2342,21 @@ export default class CashScriptParser extends Parser { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 363; + this.state = 470; if (!(this.precpred(this._ctx, 13))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 13)"); } - this.state = 364; + this.state = 471; (localctx as BinaryOpContext)._op = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===48 || _la===52)) { + if(!(_la===52 || _la===56)) { (localctx as BinaryOpContext)._op = this._errHandler.recoverInline(this); } else { this._errHandler.reportMatch(this); this.consume(); } - this.state = 365; + this.state = 472; (localctx as BinaryOpContext)._right = this.expression(14); } break; @@ -1932,21 +2365,21 @@ export default class CashScriptParser extends Parser { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 366; + this.state = 473; if (!(this.precpred(this._ctx, 12))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 12)"); } - this.state = 367; + this.state = 474; (localctx as BinaryOpContext)._op = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===53 || _la===54)) { + if(!(_la===57 || _la===58)) { (localctx as BinaryOpContext)._op = this._errHandler.recoverInline(this); } else { this._errHandler.reportMatch(this); this.consume(); } - this.state = 368; + this.state = 475; (localctx as BinaryOpContext)._right = this.expression(13); } break; @@ -1955,11 +2388,11 @@ export default class CashScriptParser extends Parser { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 369; + this.state = 476; if (!(this.precpred(this._ctx, 11))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 11)"); } - this.state = 370; + this.state = 477; (localctx as BinaryOpContext)._op = this._input.LT(1); _la = this._input.LA(1); if(!((((_la) & ~0x1F) === 0 && ((1 << _la) & 960) !== 0))) { @@ -1969,7 +2402,7 @@ export default class CashScriptParser extends Parser { this._errHandler.reportMatch(this); this.consume(); } - this.state = 371; + this.state = 478; (localctx as BinaryOpContext)._right = this.expression(12); } break; @@ -1978,21 +2411,21 @@ export default class CashScriptParser extends Parser { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 372; + this.state = 479; if (!(this.precpred(this._ctx, 10))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 10)"); } - this.state = 373; + this.state = 480; (localctx as BinaryOpContext)._op = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===55 || _la===56)) { + if(!(_la===59 || _la===60)) { (localctx as BinaryOpContext)._op = this._errHandler.recoverInline(this); } else { this._errHandler.reportMatch(this); this.consume(); } - this.state = 374; + this.state = 481; (localctx as BinaryOpContext)._right = this.expression(11); } break; @@ -2001,13 +2434,13 @@ export default class CashScriptParser extends Parser { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 375; + this.state = 482; if (!(this.precpred(this._ctx, 9))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 9)"); } - this.state = 376; - (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__56); - this.state = 377; + this.state = 483; + (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__60); + this.state = 484; (localctx as BinaryOpContext)._right = this.expression(10); } break; @@ -2016,13 +2449,13 @@ export default class CashScriptParser extends Parser { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 378; + this.state = 485; if (!(this.precpred(this._ctx, 8))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 8)"); } - this.state = 379; + this.state = 486; (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__3); - this.state = 380; + this.state = 487; (localctx as BinaryOpContext)._right = this.expression(9); } break; @@ -2031,13 +2464,13 @@ export default class CashScriptParser extends Parser { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 381; + this.state = 488; if (!(this.precpred(this._ctx, 7))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 7)"); } - this.state = 382; - (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__57); - this.state = 383; + this.state = 489; + (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__61); + this.state = 490; (localctx as BinaryOpContext)._right = this.expression(8); } break; @@ -2046,13 +2479,13 @@ export default class CashScriptParser extends Parser { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 384; + this.state = 491; if (!(this.precpred(this._ctx, 6))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 6)"); } - this.state = 385; - (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__58); - this.state = 386; + this.state = 492; + (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__62); + this.state = 493; (localctx as BinaryOpContext)._right = this.expression(7); } break; @@ -2061,13 +2494,13 @@ export default class CashScriptParser extends Parser { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 387; + this.state = 494; if (!(this.precpred(this._ctx, 5))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 5)"); } - this.state = 388; - (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__59); - this.state = 389; + this.state = 495; + (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__63); + this.state = 496; (localctx as BinaryOpContext)._right = this.expression(6); } break; @@ -2075,30 +2508,30 @@ export default class CashScriptParser extends Parser { { localctx = new TupleIndexOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 390; + this.state = 497; if (!(this.precpred(this._ctx, 21))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 21)"); } - this.state = 391; - this.match(CashScriptParser.T__29); - this.state = 392; + this.state = 498; + this.match(CashScriptParser.T__33); + this.state = 499; (localctx as TupleIndexOpContext)._index = this.match(CashScriptParser.NumberLiteral); - this.state = 393; - this.match(CashScriptParser.T__30); + this.state = 500; + this.match(CashScriptParser.T__34); } break; case 12: { localctx = new UnaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 394; + this.state = 501; if (!(this.precpred(this._ctx, 18))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 18)"); } - this.state = 395; + this.state = 502; (localctx as UnaryOpContext)._op = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===43 || _la===44)) { + if(!(_la===47 || _la===48)) { (localctx as UnaryOpContext)._op = this._errHandler.recoverInline(this); } else { @@ -2112,18 +2545,18 @@ export default class CashScriptParser extends Parser { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 396; + this.state = 503; if (!(this.precpred(this._ctx, 17))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 17)"); } - this.state = 397; - (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__44); - this.state = 398; - this.match(CashScriptParser.T__14); - this.state = 399; + this.state = 504; + (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__48); + this.state = 505; + this.match(CashScriptParser.T__13); + this.state = 506; (localctx as BinaryOpContext)._right = this.expression(0); - this.state = 400; - this.match(CashScriptParser.T__16); + this.state = 507; + this.match(CashScriptParser.T__15); } break; case 14: @@ -2131,30 +2564,30 @@ export default class CashScriptParser extends Parser { localctx = new SliceContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as SliceContext)._element = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 402; + this.state = 509; if (!(this.precpred(this._ctx, 16))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 16)"); } - this.state = 403; - this.match(CashScriptParser.T__45); - this.state = 404; - this.match(CashScriptParser.T__14); - this.state = 405; + this.state = 510; + this.match(CashScriptParser.T__49); + this.state = 511; + this.match(CashScriptParser.T__13); + this.state = 512; (localctx as SliceContext)._start = this.expression(0); - this.state = 406; - this.match(CashScriptParser.T__15); - this.state = 407; + this.state = 513; + this.match(CashScriptParser.T__14); + this.state = 514; (localctx as SliceContext)._end = this.expression(0); - this.state = 408; - this.match(CashScriptParser.T__16); + this.state = 515; + this.match(CashScriptParser.T__15); } break; } } } - this.state = 414; + this.state = 521; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 33, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 45, this._ctx); } } } @@ -2175,12 +2608,20 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public modifier(): ModifierContext { let localctx: ModifierContext = new ModifierContext(this, this._ctx, this.state); - this.enterRule(localctx, 66, CashScriptParser.RULE_modifier); + this.enterRule(localctx, 80, CashScriptParser.RULE_modifier); + let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 415; - this.match(CashScriptParser.T__60); + this.state = 522; + _la = this._input.LA(1); + if(!(_la===17 || _la===65)) { + this._errHandler.recoverInline(this); + } + else { + this._errHandler.reportMatch(this); + this.consume(); + } } } catch (re) { @@ -2200,43 +2641,43 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public literal(): LiteralContext { let localctx: LiteralContext = new LiteralContext(this, this._ctx, this.state); - this.enterRule(localctx, 68, CashScriptParser.RULE_literal); + this.enterRule(localctx, 82, CashScriptParser.RULE_literal); try { - this.state = 422; + this.state = 529; this._errHandler.sync(this); switch (this._input.LA(1)) { - case 63: + case 67: this.enterOuterAlt(localctx, 1); { - this.state = 417; + this.state = 524; this.match(CashScriptParser.BooleanLiteral); } break; - case 65: + case 69: this.enterOuterAlt(localctx, 2); { - this.state = 418; + this.state = 525; this.numberLiteral(); } break; - case 72: + case 76: this.enterOuterAlt(localctx, 3); { - this.state = 419; + this.state = 526; this.match(CashScriptParser.StringLiteral); } break; - case 73: + case 77: this.enterOuterAlt(localctx, 4); { - this.state = 420; + this.state = 527; this.match(CashScriptParser.DateLiteral); } break; - case 74: + case 78: this.enterOuterAlt(localctx, 5); { - this.state = 421; + this.state = 528; this.match(CashScriptParser.HexLiteral); } break; @@ -2261,18 +2702,18 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public numberLiteral(): NumberLiteralContext { let localctx: NumberLiteralContext = new NumberLiteralContext(this, this._ctx, this.state); - this.enterRule(localctx, 70, CashScriptParser.RULE_numberLiteral); + this.enterRule(localctx, 84, CashScriptParser.RULE_numberLiteral); try { this.enterOuterAlt(localctx, 1); { - this.state = 424; + this.state = 531; this.match(CashScriptParser.NumberLiteral); - this.state = 426; + this.state = 533; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 35, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 47, this._ctx) ) { case 1: { - this.state = 425; + this.state = 532; this.match(CashScriptParser.NumberUnit); } break; @@ -2296,14 +2737,14 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public typeName(): TypeNameContext { let localctx: TypeNameContext = new TypeNameContext(this, this._ctx, this.state); - this.enterRule(localctx, 72, CashScriptParser.RULE_typeName); + this.enterRule(localctx, 86, CashScriptParser.RULE_typeName); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 428; + this.state = 535; _la = this._input.LA(1); - if(!(((((_la - 68)) & ~0x1F) === 0 && ((1 << (_la - 68)) & 7) !== 0))) { + if(!(((((_la - 72)) & ~0x1F) === 0 && ((1 << (_la - 72)) & 7) !== 0))) { this._errHandler.recoverInline(this); } else { @@ -2329,14 +2770,14 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public typeCast(): TypeCastContext { let localctx: TypeCastContext = new TypeCastContext(this, this._ctx, this.state); - this.enterRule(localctx, 74, CashScriptParser.RULE_typeCast); + this.enterRule(localctx, 88, CashScriptParser.RULE_typeCast); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 430; + this.state = 537; _la = this._input.LA(1); - if(!(((((_la - 68)) & ~0x1F) === 0 && ((1 << (_la - 68)) & 259) !== 0))) { + if(!(((((_la - 72)) & ~0x1F) === 0 && ((1 << (_la - 72)) & 259) !== 0))) { this._errHandler.recoverInline(this); } else { @@ -2362,7 +2803,7 @@ export default class CashScriptParser extends Parser { public sempred(localctx: RuleContext, ruleIndex: number, predIndex: number): boolean { switch (ruleIndex) { - case 32: + case 39: return this.expression_sempred(localctx as ExpressionContext, predIndex); } return true; @@ -2401,149 +2842,185 @@ export default class CashScriptParser extends Parser { return true; } - public static readonly _serializedATN: number[] = [4,1,81,433,2,0,7,0,2, + public static readonly _serializedATN: number[] = [4,1,85,540,2,0,7,0,2, 1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,2,7,7,7,2,8,7,8,2,9,7,9,2, 10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14,7,14,2,15,7,15,2,16,7,16,2,17, 7,17,2,18,7,18,2,19,7,19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24,7, 24,2,25,7,25,2,26,7,26,2,27,7,27,2,28,7,28,2,29,7,29,2,30,7,30,2,31,7,31, - 2,32,7,32,2,33,7,33,2,34,7,34,2,35,7,35,2,36,7,36,2,37,7,37,1,0,5,0,78, - 8,0,10,0,12,0,81,9,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,2,1,2,1,3,1,3,3, - 3,95,8,3,1,4,3,4,98,8,4,1,4,1,4,1,5,1,5,1,6,1,6,1,6,1,6,1,6,5,6,109,8,6, - 10,6,12,6,112,9,6,1,6,1,6,1,7,1,7,1,7,1,7,1,7,1,8,1,8,5,8,123,8,8,10,8, - 12,8,126,9,8,1,8,1,8,1,9,1,9,1,9,1,9,5,9,134,8,9,10,9,12,9,137,9,9,1,9, - 3,9,140,8,9,3,9,142,8,9,1,9,1,9,1,10,1,10,1,10,1,11,1,11,5,11,151,8,11, - 10,11,12,11,154,9,11,1,11,1,11,3,11,158,8,11,1,12,1,12,1,12,1,12,3,12,164, - 8,12,1,13,1,13,1,13,1,13,1,13,1,13,3,13,172,8,13,1,14,1,14,3,14,176,8,14, - 1,15,1,15,5,15,180,8,15,10,15,12,15,183,9,15,1,15,1,15,1,15,1,15,1,16,1, - 16,1,16,1,16,1,16,1,16,1,16,1,16,1,17,1,17,1,17,1,17,1,17,3,17,202,8,17, - 1,18,1,18,1,18,1,18,1,18,1,18,1,18,3,18,211,8,18,1,18,1,18,1,19,1,19,1, - 19,1,19,1,19,3,19,220,8,19,1,19,1,19,1,20,1,20,1,20,1,21,1,21,1,21,1,21, - 1,21,1,21,1,21,3,21,234,8,21,1,22,1,22,1,22,3,22,239,8,22,1,23,1,23,1,23, - 1,23,1,23,1,23,1,23,1,23,1,24,1,24,1,24,1,24,1,24,1,24,1,25,1,25,1,25,1, - 25,1,25,1,25,1,25,1,25,1,25,1,25,1,26,1,26,3,26,267,8,26,1,27,1,27,1,28, - 1,28,3,28,273,8,28,1,29,1,29,1,29,1,29,5,29,279,8,29,10,29,12,29,282,9, - 29,1,29,3,29,285,8,29,3,29,287,8,29,1,29,1,29,1,30,1,30,1,30,1,31,1,31, - 1,31,1,31,5,31,298,8,31,10,31,12,31,301,9,31,1,31,3,31,304,8,31,3,31,306, - 8,31,1,31,1,31,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,3,32,319,8, - 32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32, - 1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,5,32,345,8,32,10,32,12, - 32,348,9,32,1,32,3,32,351,8,32,3,32,353,8,32,1,32,1,32,1,32,1,32,3,32,359, - 8,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1, - 32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32, - 1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1, - 32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,5,32,411,8,32,10,32,12,32,414,9,32, - 1,33,1,33,1,34,1,34,1,34,1,34,1,34,3,34,423,8,34,1,35,1,35,3,35,427,8,35, - 1,36,1,36,1,37,1,37,1,37,0,1,64,38,0,2,4,6,8,10,12,14,16,18,20,22,24,26, - 28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74, - 0,14,1,0,4,10,2,0,10,10,18,19,1,0,20,21,1,0,33,37,2,0,33,37,39,42,2,0,5, - 5,47,48,1,0,49,51,2,0,48,48,52,52,1,0,53,54,1,0,6,9,1,0,55,56,1,0,43,44, - 1,0,68,70,2,0,68,69,76,76,459,0,79,1,0,0,0,2,85,1,0,0,0,4,90,1,0,0,0,6, - 92,1,0,0,0,8,97,1,0,0,0,10,101,1,0,0,0,12,103,1,0,0,0,14,115,1,0,0,0,16, - 120,1,0,0,0,18,129,1,0,0,0,20,145,1,0,0,0,22,157,1,0,0,0,24,163,1,0,0,0, - 26,171,1,0,0,0,28,175,1,0,0,0,30,177,1,0,0,0,32,188,1,0,0,0,34,201,1,0, - 0,0,36,203,1,0,0,0,38,214,1,0,0,0,40,223,1,0,0,0,42,226,1,0,0,0,44,238, - 1,0,0,0,46,240,1,0,0,0,48,248,1,0,0,0,50,254,1,0,0,0,52,266,1,0,0,0,54, - 268,1,0,0,0,56,272,1,0,0,0,58,274,1,0,0,0,60,290,1,0,0,0,62,293,1,0,0,0, - 64,358,1,0,0,0,66,415,1,0,0,0,68,422,1,0,0,0,70,424,1,0,0,0,72,428,1,0, - 0,0,74,430,1,0,0,0,76,78,3,2,1,0,77,76,1,0,0,0,78,81,1,0,0,0,79,77,1,0, - 0,0,79,80,1,0,0,0,80,82,1,0,0,0,81,79,1,0,0,0,82,83,3,12,6,0,83,84,5,0, - 0,1,84,1,1,0,0,0,85,86,5,1,0,0,86,87,3,4,2,0,87,88,3,6,3,0,88,89,5,2,0, - 0,89,3,1,0,0,0,90,91,5,3,0,0,91,5,1,0,0,0,92,94,3,8,4,0,93,95,3,8,4,0,94, - 93,1,0,0,0,94,95,1,0,0,0,95,7,1,0,0,0,96,98,3,10,5,0,97,96,1,0,0,0,97,98, - 1,0,0,0,98,99,1,0,0,0,99,100,5,62,0,0,100,9,1,0,0,0,101,102,7,0,0,0,102, - 11,1,0,0,0,103,104,5,11,0,0,104,105,5,78,0,0,105,106,3,18,9,0,106,110,5, - 12,0,0,107,109,3,14,7,0,108,107,1,0,0,0,109,112,1,0,0,0,110,108,1,0,0,0, - 110,111,1,0,0,0,111,113,1,0,0,0,112,110,1,0,0,0,113,114,5,13,0,0,114,13, - 1,0,0,0,115,116,5,14,0,0,116,117,5,78,0,0,117,118,3,18,9,0,118,119,3,16, - 8,0,119,15,1,0,0,0,120,124,5,12,0,0,121,123,3,24,12,0,122,121,1,0,0,0,123, - 126,1,0,0,0,124,122,1,0,0,0,124,125,1,0,0,0,125,127,1,0,0,0,126,124,1,0, - 0,0,127,128,5,13,0,0,128,17,1,0,0,0,129,141,5,15,0,0,130,135,3,20,10,0, - 131,132,5,16,0,0,132,134,3,20,10,0,133,131,1,0,0,0,134,137,1,0,0,0,135, - 133,1,0,0,0,135,136,1,0,0,0,136,139,1,0,0,0,137,135,1,0,0,0,138,140,5,16, - 0,0,139,138,1,0,0,0,139,140,1,0,0,0,140,142,1,0,0,0,141,130,1,0,0,0,141, - 142,1,0,0,0,142,143,1,0,0,0,143,144,5,17,0,0,144,19,1,0,0,0,145,146,3,72, - 36,0,146,147,5,78,0,0,147,21,1,0,0,0,148,152,5,12,0,0,149,151,3,24,12,0, - 150,149,1,0,0,0,151,154,1,0,0,0,152,150,1,0,0,0,152,153,1,0,0,0,153,155, - 1,0,0,0,154,152,1,0,0,0,155,158,5,13,0,0,156,158,3,24,12,0,157,148,1,0, - 0,0,157,156,1,0,0,0,158,23,1,0,0,0,159,164,3,28,14,0,160,161,3,26,13,0, - 161,162,5,2,0,0,162,164,1,0,0,0,163,159,1,0,0,0,163,160,1,0,0,0,164,25, - 1,0,0,0,165,172,3,30,15,0,166,172,3,32,16,0,167,172,3,34,17,0,168,172,3, - 36,18,0,169,172,3,38,19,0,170,172,3,40,20,0,171,165,1,0,0,0,171,166,1,0, - 0,0,171,167,1,0,0,0,171,168,1,0,0,0,171,169,1,0,0,0,171,170,1,0,0,0,172, - 27,1,0,0,0,173,176,3,42,21,0,174,176,3,44,22,0,175,173,1,0,0,0,175,174, - 1,0,0,0,176,29,1,0,0,0,177,181,3,72,36,0,178,180,3,66,33,0,179,178,1,0, - 0,0,180,183,1,0,0,0,181,179,1,0,0,0,181,182,1,0,0,0,182,184,1,0,0,0,183, - 181,1,0,0,0,184,185,5,78,0,0,185,186,5,10,0,0,186,187,3,64,32,0,187,31, - 1,0,0,0,188,189,3,72,36,0,189,190,5,78,0,0,190,191,5,16,0,0,191,192,3,72, - 36,0,192,193,5,78,0,0,193,194,5,10,0,0,194,195,3,64,32,0,195,33,1,0,0,0, - 196,197,5,78,0,0,197,198,7,1,0,0,198,202,3,64,32,0,199,200,5,78,0,0,200, - 202,7,2,0,0,201,196,1,0,0,0,201,199,1,0,0,0,202,35,1,0,0,0,203,204,5,22, - 0,0,204,205,5,15,0,0,205,206,5,75,0,0,206,207,5,6,0,0,207,210,3,64,32,0, - 208,209,5,16,0,0,209,211,3,54,27,0,210,208,1,0,0,0,210,211,1,0,0,0,211, - 212,1,0,0,0,212,213,5,17,0,0,213,37,1,0,0,0,214,215,5,22,0,0,215,216,5, - 15,0,0,216,219,3,64,32,0,217,218,5,16,0,0,218,220,3,54,27,0,219,217,1,0, - 0,0,219,220,1,0,0,0,220,221,1,0,0,0,221,222,5,17,0,0,222,39,1,0,0,0,223, - 224,5,23,0,0,224,225,3,58,29,0,225,41,1,0,0,0,226,227,5,24,0,0,227,228, - 5,15,0,0,228,229,3,64,32,0,229,230,5,17,0,0,230,233,3,22,11,0,231,232,5, - 25,0,0,232,234,3,22,11,0,233,231,1,0,0,0,233,234,1,0,0,0,234,43,1,0,0,0, - 235,239,3,46,23,0,236,239,3,48,24,0,237,239,3,50,25,0,238,235,1,0,0,0,238, - 236,1,0,0,0,238,237,1,0,0,0,239,45,1,0,0,0,240,241,5,26,0,0,241,242,3,22, - 11,0,242,243,5,27,0,0,243,244,5,15,0,0,244,245,3,64,32,0,245,246,5,17,0, - 0,246,247,5,2,0,0,247,47,1,0,0,0,248,249,5,27,0,0,249,250,5,15,0,0,250, - 251,3,64,32,0,251,252,5,17,0,0,252,253,3,22,11,0,253,49,1,0,0,0,254,255, - 5,28,0,0,255,256,5,15,0,0,256,257,3,52,26,0,257,258,5,2,0,0,258,259,3,64, - 32,0,259,260,5,2,0,0,260,261,3,34,17,0,261,262,5,17,0,0,262,263,3,22,11, - 0,263,51,1,0,0,0,264,267,3,30,15,0,265,267,3,34,17,0,266,264,1,0,0,0,266, - 265,1,0,0,0,267,53,1,0,0,0,268,269,5,72,0,0,269,55,1,0,0,0,270,273,5,78, - 0,0,271,273,3,68,34,0,272,270,1,0,0,0,272,271,1,0,0,0,273,57,1,0,0,0,274, - 286,5,15,0,0,275,280,3,56,28,0,276,277,5,16,0,0,277,279,3,56,28,0,278,276, - 1,0,0,0,279,282,1,0,0,0,280,278,1,0,0,0,280,281,1,0,0,0,281,284,1,0,0,0, - 282,280,1,0,0,0,283,285,5,16,0,0,284,283,1,0,0,0,284,285,1,0,0,0,285,287, - 1,0,0,0,286,275,1,0,0,0,286,287,1,0,0,0,287,288,1,0,0,0,288,289,5,17,0, - 0,289,59,1,0,0,0,290,291,5,78,0,0,291,292,3,62,31,0,292,61,1,0,0,0,293, - 305,5,15,0,0,294,299,3,64,32,0,295,296,5,16,0,0,296,298,3,64,32,0,297,295, - 1,0,0,0,298,301,1,0,0,0,299,297,1,0,0,0,299,300,1,0,0,0,300,303,1,0,0,0, - 301,299,1,0,0,0,302,304,5,16,0,0,303,302,1,0,0,0,303,304,1,0,0,0,304,306, - 1,0,0,0,305,294,1,0,0,0,305,306,1,0,0,0,306,307,1,0,0,0,307,308,5,17,0, - 0,308,63,1,0,0,0,309,310,6,32,-1,0,310,311,5,15,0,0,311,312,3,64,32,0,312, - 313,5,17,0,0,313,359,1,0,0,0,314,315,3,74,37,0,315,316,5,15,0,0,316,318, - 3,64,32,0,317,319,5,16,0,0,318,317,1,0,0,0,318,319,1,0,0,0,319,320,1,0, - 0,0,320,321,5,17,0,0,321,359,1,0,0,0,322,359,3,60,30,0,323,324,5,29,0,0, - 324,325,5,78,0,0,325,359,3,62,31,0,326,327,5,32,0,0,327,328,5,30,0,0,328, - 329,3,64,32,0,329,330,5,31,0,0,330,331,7,3,0,0,331,359,1,0,0,0,332,333, - 5,38,0,0,333,334,5,30,0,0,334,335,3,64,32,0,335,336,5,31,0,0,336,337,7, - 4,0,0,337,359,1,0,0,0,338,339,7,5,0,0,339,359,3,64,32,15,340,352,5,30,0, - 0,341,346,3,64,32,0,342,343,5,16,0,0,343,345,3,64,32,0,344,342,1,0,0,0, - 345,348,1,0,0,0,346,344,1,0,0,0,346,347,1,0,0,0,347,350,1,0,0,0,348,346, - 1,0,0,0,349,351,5,16,0,0,350,349,1,0,0,0,350,351,1,0,0,0,351,353,1,0,0, - 0,352,341,1,0,0,0,352,353,1,0,0,0,353,354,1,0,0,0,354,359,5,31,0,0,355, - 359,5,77,0,0,356,359,5,78,0,0,357,359,3,68,34,0,358,309,1,0,0,0,358,314, - 1,0,0,0,358,322,1,0,0,0,358,323,1,0,0,0,358,326,1,0,0,0,358,332,1,0,0,0, - 358,338,1,0,0,0,358,340,1,0,0,0,358,355,1,0,0,0,358,356,1,0,0,0,358,357, - 1,0,0,0,359,412,1,0,0,0,360,361,10,14,0,0,361,362,7,6,0,0,362,411,3,64, - 32,15,363,364,10,13,0,0,364,365,7,7,0,0,365,411,3,64,32,14,366,367,10,12, - 0,0,367,368,7,8,0,0,368,411,3,64,32,13,369,370,10,11,0,0,370,371,7,9,0, - 0,371,411,3,64,32,12,372,373,10,10,0,0,373,374,7,10,0,0,374,411,3,64,32, - 11,375,376,10,9,0,0,376,377,5,57,0,0,377,411,3,64,32,10,378,379,10,8,0, - 0,379,380,5,4,0,0,380,411,3,64,32,9,381,382,10,7,0,0,382,383,5,58,0,0,383, - 411,3,64,32,8,384,385,10,6,0,0,385,386,5,59,0,0,386,411,3,64,32,7,387,388, - 10,5,0,0,388,389,5,60,0,0,389,411,3,64,32,6,390,391,10,21,0,0,391,392,5, - 30,0,0,392,393,5,65,0,0,393,411,5,31,0,0,394,395,10,18,0,0,395,411,7,11, - 0,0,396,397,10,17,0,0,397,398,5,45,0,0,398,399,5,15,0,0,399,400,3,64,32, - 0,400,401,5,17,0,0,401,411,1,0,0,0,402,403,10,16,0,0,403,404,5,46,0,0,404, - 405,5,15,0,0,405,406,3,64,32,0,406,407,5,16,0,0,407,408,3,64,32,0,408,409, - 5,17,0,0,409,411,1,0,0,0,410,360,1,0,0,0,410,363,1,0,0,0,410,366,1,0,0, - 0,410,369,1,0,0,0,410,372,1,0,0,0,410,375,1,0,0,0,410,378,1,0,0,0,410,381, - 1,0,0,0,410,384,1,0,0,0,410,387,1,0,0,0,410,390,1,0,0,0,410,394,1,0,0,0, - 410,396,1,0,0,0,410,402,1,0,0,0,411,414,1,0,0,0,412,410,1,0,0,0,412,413, - 1,0,0,0,413,65,1,0,0,0,414,412,1,0,0,0,415,416,5,61,0,0,416,67,1,0,0,0, - 417,423,5,63,0,0,418,423,3,70,35,0,419,423,5,72,0,0,420,423,5,73,0,0,421, - 423,5,74,0,0,422,417,1,0,0,0,422,418,1,0,0,0,422,419,1,0,0,0,422,420,1, - 0,0,0,422,421,1,0,0,0,423,69,1,0,0,0,424,426,5,65,0,0,425,427,5,64,0,0, - 426,425,1,0,0,0,426,427,1,0,0,0,427,71,1,0,0,0,428,429,7,12,0,0,429,73, - 1,0,0,0,430,431,7,13,0,0,431,75,1,0,0,0,36,79,94,97,110,124,135,139,141, - 152,157,163,171,175,181,201,210,219,233,238,266,272,280,284,286,299,303, - 305,318,346,350,352,358,410,412,422,426]; + 2,32,7,32,2,33,7,33,2,34,7,34,2,35,7,35,2,36,7,36,2,37,7,37,2,38,7,38,2, + 39,7,39,2,40,7,40,2,41,7,41,2,42,7,42,2,43,7,43,2,44,7,44,1,0,5,0,92,8, + 0,10,0,12,0,95,9,0,1,0,5,0,98,8,0,10,0,12,0,101,9,0,1,0,5,0,104,8,0,10, + 0,12,0,107,9,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,2,1,2,1,3,1,3,3,3,120,8,3, + 1,4,3,4,123,8,4,1,4,1,4,1,5,1,5,1,6,1,6,1,6,1,6,1,7,1,7,1,7,3,7,136,8,7, + 1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,5,8,146,8,8,10,8,12,8,149,9,8,1,8,1,8,3, + 8,153,8,8,1,8,1,8,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,10,1,10,1,10,1,10,1,10, + 5,10,169,8,10,10,10,12,10,172,9,10,1,10,1,10,1,11,1,11,1,11,1,11,1,11,1, + 12,1,12,5,12,183,8,12,10,12,12,12,186,9,12,1,12,1,12,1,13,1,13,1,13,1,13, + 5,13,194,8,13,10,13,12,13,197,9,13,1,13,3,13,200,8,13,3,13,202,8,13,1,13, + 1,13,1,14,1,14,5,14,208,8,14,10,14,12,14,211,9,14,1,14,1,14,1,15,1,15,5, + 15,217,8,15,10,15,12,15,220,9,15,1,15,1,15,3,15,224,8,15,1,16,1,16,1,16, + 1,16,3,16,230,8,16,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,17,3,17,240,8,17, + 1,18,1,18,1,19,1,19,1,19,1,19,5,19,248,8,19,10,19,12,19,251,9,19,1,20,1, + 20,3,20,255,8,20,1,21,1,21,5,21,259,8,21,10,21,12,21,262,9,21,1,21,1,21, + 1,21,1,21,1,22,1,22,1,22,4,22,271,8,22,11,22,12,22,272,1,22,1,22,1,22,1, + 22,1,22,1,22,1,22,4,22,282,8,22,11,22,12,22,283,1,22,1,22,1,22,1,22,3,22, + 290,8,22,1,23,1,23,5,23,294,8,23,10,23,12,23,297,9,23,1,23,1,23,1,23,3, + 23,302,8,23,1,24,1,24,1,24,1,24,1,24,3,24,309,8,24,1,25,1,25,1,25,1,25, + 1,25,1,25,1,25,3,25,318,8,25,1,25,1,25,1,26,1,26,1,26,1,26,1,26,3,26,327, + 8,26,1,26,1,26,1,27,1,27,1,27,1,28,1,28,1,28,1,28,1,28,1,28,1,28,3,28,341, + 8,28,1,29,1,29,1,29,3,29,346,8,29,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1, + 30,1,31,1,31,1,31,1,31,1,31,1,31,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32, + 1,32,1,32,1,33,1,33,3,33,374,8,33,1,34,1,34,1,35,1,35,3,35,380,8,35,1,36, + 1,36,1,36,1,36,5,36,386,8,36,10,36,12,36,389,9,36,1,36,3,36,392,8,36,3, + 36,394,8,36,1,36,1,36,1,37,1,37,1,37,1,38,1,38,1,38,1,38,5,38,405,8,38, + 10,38,12,38,408,9,38,1,38,3,38,411,8,38,3,38,413,8,38,1,38,1,38,1,39,1, + 39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,3,39,426,8,39,1,39,1,39,1,39,1,39, + 1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1, + 39,1,39,1,39,1,39,1,39,1,39,5,39,452,8,39,10,39,12,39,455,9,39,1,39,3,39, + 458,8,39,3,39,460,8,39,1,39,1,39,1,39,1,39,3,39,466,8,39,1,39,1,39,1,39, + 1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1, + 39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39, + 1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1, + 39,1,39,1,39,1,39,5,39,518,8,39,10,39,12,39,521,9,39,1,40,1,40,1,41,1,41, + 1,41,1,41,1,41,3,41,530,8,41,1,42,1,42,3,42,534,8,42,1,43,1,43,1,44,1,44, + 1,44,0,1,78,45,0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40, + 42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88, + 0,15,1,0,4,10,2,0,10,10,22,23,1,0,24,25,1,0,37,41,2,0,37,41,43,46,2,0,5, + 5,51,52,1,0,53,55,2,0,52,52,56,56,1,0,57,58,1,0,6,9,1,0,59,60,1,0,47,48, + 2,0,17,17,65,65,1,0,72,74,2,0,72,73,80,80,574,0,93,1,0,0,0,2,110,1,0,0, + 0,4,115,1,0,0,0,6,117,1,0,0,0,8,122,1,0,0,0,10,126,1,0,0,0,12,128,1,0,0, + 0,14,135,1,0,0,0,16,137,1,0,0,0,18,156,1,0,0,0,20,163,1,0,0,0,22,175,1, + 0,0,0,24,180,1,0,0,0,26,189,1,0,0,0,28,205,1,0,0,0,30,223,1,0,0,0,32,229, + 1,0,0,0,34,239,1,0,0,0,36,241,1,0,0,0,38,243,1,0,0,0,40,254,1,0,0,0,42, + 256,1,0,0,0,44,289,1,0,0,0,46,301,1,0,0,0,48,308,1,0,0,0,50,310,1,0,0,0, + 52,321,1,0,0,0,54,330,1,0,0,0,56,333,1,0,0,0,58,345,1,0,0,0,60,347,1,0, + 0,0,62,355,1,0,0,0,64,361,1,0,0,0,66,373,1,0,0,0,68,375,1,0,0,0,70,379, + 1,0,0,0,72,381,1,0,0,0,74,397,1,0,0,0,76,400,1,0,0,0,78,465,1,0,0,0,80, + 522,1,0,0,0,82,529,1,0,0,0,84,531,1,0,0,0,86,535,1,0,0,0,88,537,1,0,0,0, + 90,92,3,2,1,0,91,90,1,0,0,0,92,95,1,0,0,0,93,91,1,0,0,0,93,94,1,0,0,0,94, + 99,1,0,0,0,95,93,1,0,0,0,96,98,3,12,6,0,97,96,1,0,0,0,98,101,1,0,0,0,99, + 97,1,0,0,0,99,100,1,0,0,0,100,105,1,0,0,0,101,99,1,0,0,0,102,104,3,14,7, + 0,103,102,1,0,0,0,104,107,1,0,0,0,105,103,1,0,0,0,105,106,1,0,0,0,106,108, + 1,0,0,0,107,105,1,0,0,0,108,109,5,0,0,1,109,1,1,0,0,0,110,111,5,1,0,0,111, + 112,3,4,2,0,112,113,3,6,3,0,113,114,5,2,0,0,114,3,1,0,0,0,115,116,5,3,0, + 0,116,5,1,0,0,0,117,119,3,8,4,0,118,120,3,8,4,0,119,118,1,0,0,0,119,120, + 1,0,0,0,120,7,1,0,0,0,121,123,3,10,5,0,122,121,1,0,0,0,122,123,1,0,0,0, + 123,124,1,0,0,0,124,125,5,66,0,0,125,9,1,0,0,0,126,127,7,0,0,0,127,11,1, + 0,0,0,128,129,5,11,0,0,129,130,5,76,0,0,130,131,5,2,0,0,131,13,1,0,0,0, + 132,136,3,16,8,0,133,136,3,18,9,0,134,136,3,20,10,0,135,132,1,0,0,0,135, + 133,1,0,0,0,135,134,1,0,0,0,136,15,1,0,0,0,137,138,5,12,0,0,138,139,5,82, + 0,0,139,152,3,26,13,0,140,141,5,13,0,0,141,142,5,14,0,0,142,147,3,86,43, + 0,143,144,5,15,0,0,144,146,3,86,43,0,145,143,1,0,0,0,146,149,1,0,0,0,147, + 145,1,0,0,0,147,148,1,0,0,0,148,150,1,0,0,0,149,147,1,0,0,0,150,151,5,16, + 0,0,151,153,1,0,0,0,152,140,1,0,0,0,152,153,1,0,0,0,153,154,1,0,0,0,154, + 155,3,24,12,0,155,17,1,0,0,0,156,157,3,86,43,0,157,158,5,17,0,0,158,159, + 5,82,0,0,159,160,5,10,0,0,160,161,3,78,39,0,161,162,5,2,0,0,162,19,1,0, + 0,0,163,164,5,18,0,0,164,165,5,82,0,0,165,166,3,26,13,0,166,170,5,19,0, + 0,167,169,3,22,11,0,168,167,1,0,0,0,169,172,1,0,0,0,170,168,1,0,0,0,170, + 171,1,0,0,0,171,173,1,0,0,0,172,170,1,0,0,0,173,174,5,20,0,0,174,21,1,0, + 0,0,175,176,5,12,0,0,176,177,5,82,0,0,177,178,3,26,13,0,178,179,3,24,12, + 0,179,23,1,0,0,0,180,184,5,19,0,0,181,183,3,32,16,0,182,181,1,0,0,0,183, + 186,1,0,0,0,184,182,1,0,0,0,184,185,1,0,0,0,185,187,1,0,0,0,186,184,1,0, + 0,0,187,188,5,20,0,0,188,25,1,0,0,0,189,201,5,14,0,0,190,195,3,28,14,0, + 191,192,5,15,0,0,192,194,3,28,14,0,193,191,1,0,0,0,194,197,1,0,0,0,195, + 193,1,0,0,0,195,196,1,0,0,0,196,199,1,0,0,0,197,195,1,0,0,0,198,200,5,15, + 0,0,199,198,1,0,0,0,199,200,1,0,0,0,200,202,1,0,0,0,201,190,1,0,0,0,201, + 202,1,0,0,0,202,203,1,0,0,0,203,204,5,16,0,0,204,27,1,0,0,0,205,209,3,86, + 43,0,206,208,3,80,40,0,207,206,1,0,0,0,208,211,1,0,0,0,209,207,1,0,0,0, + 209,210,1,0,0,0,210,212,1,0,0,0,211,209,1,0,0,0,212,213,5,82,0,0,213,29, + 1,0,0,0,214,218,5,19,0,0,215,217,3,32,16,0,216,215,1,0,0,0,217,220,1,0, + 0,0,218,216,1,0,0,0,218,219,1,0,0,0,219,221,1,0,0,0,220,218,1,0,0,0,221, + 224,5,20,0,0,222,224,3,32,16,0,223,214,1,0,0,0,223,222,1,0,0,0,224,31,1, + 0,0,0,225,230,3,40,20,0,226,227,3,34,17,0,227,228,5,2,0,0,228,230,1,0,0, + 0,229,225,1,0,0,0,229,226,1,0,0,0,230,33,1,0,0,0,231,240,3,42,21,0,232, + 240,3,44,22,0,233,240,3,48,24,0,234,240,3,50,25,0,235,240,3,52,26,0,236, + 240,3,36,18,0,237,240,3,54,27,0,238,240,3,38,19,0,239,231,1,0,0,0,239,232, + 1,0,0,0,239,233,1,0,0,0,239,234,1,0,0,0,239,235,1,0,0,0,239,236,1,0,0,0, + 239,237,1,0,0,0,239,238,1,0,0,0,240,35,1,0,0,0,241,242,3,74,37,0,242,37, + 1,0,0,0,243,244,5,21,0,0,244,249,3,78,39,0,245,246,5,15,0,0,246,248,3,78, + 39,0,247,245,1,0,0,0,248,251,1,0,0,0,249,247,1,0,0,0,249,250,1,0,0,0,250, + 39,1,0,0,0,251,249,1,0,0,0,252,255,3,56,28,0,253,255,3,58,29,0,254,252, + 1,0,0,0,254,253,1,0,0,0,255,41,1,0,0,0,256,260,3,86,43,0,257,259,3,80,40, + 0,258,257,1,0,0,0,259,262,1,0,0,0,260,258,1,0,0,0,260,261,1,0,0,0,261,263, + 1,0,0,0,262,260,1,0,0,0,263,264,5,82,0,0,264,265,5,10,0,0,265,266,3,78, + 39,0,266,43,1,0,0,0,267,270,3,46,23,0,268,269,5,15,0,0,269,271,3,46,23, + 0,270,268,1,0,0,0,271,272,1,0,0,0,272,270,1,0,0,0,272,273,1,0,0,0,273,274, + 1,0,0,0,274,275,5,10,0,0,275,276,3,78,39,0,276,290,1,0,0,0,277,278,5,14, + 0,0,278,281,3,46,23,0,279,280,5,15,0,0,280,282,3,46,23,0,281,279,1,0,0, + 0,282,283,1,0,0,0,283,281,1,0,0,0,283,284,1,0,0,0,284,285,1,0,0,0,285,286, + 5,16,0,0,286,287,5,10,0,0,287,288,3,78,39,0,288,290,1,0,0,0,289,267,1,0, + 0,0,289,277,1,0,0,0,290,45,1,0,0,0,291,295,3,86,43,0,292,294,3,80,40,0, + 293,292,1,0,0,0,294,297,1,0,0,0,295,293,1,0,0,0,295,296,1,0,0,0,296,298, + 1,0,0,0,297,295,1,0,0,0,298,299,5,82,0,0,299,302,1,0,0,0,300,302,5,82,0, + 0,301,291,1,0,0,0,301,300,1,0,0,0,302,47,1,0,0,0,303,304,5,82,0,0,304,305, + 7,1,0,0,305,309,3,78,39,0,306,307,5,82,0,0,307,309,7,2,0,0,308,303,1,0, + 0,0,308,306,1,0,0,0,309,49,1,0,0,0,310,311,5,26,0,0,311,312,5,14,0,0,312, + 313,5,79,0,0,313,314,5,6,0,0,314,317,3,78,39,0,315,316,5,15,0,0,316,318, + 3,68,34,0,317,315,1,0,0,0,317,318,1,0,0,0,318,319,1,0,0,0,319,320,5,16, + 0,0,320,51,1,0,0,0,321,322,5,26,0,0,322,323,5,14,0,0,323,326,3,78,39,0, + 324,325,5,15,0,0,325,327,3,68,34,0,326,324,1,0,0,0,326,327,1,0,0,0,327, + 328,1,0,0,0,328,329,5,16,0,0,329,53,1,0,0,0,330,331,5,27,0,0,331,332,3, + 72,36,0,332,55,1,0,0,0,333,334,5,28,0,0,334,335,5,14,0,0,335,336,3,78,39, + 0,336,337,5,16,0,0,337,340,3,30,15,0,338,339,5,29,0,0,339,341,3,30,15,0, + 340,338,1,0,0,0,340,341,1,0,0,0,341,57,1,0,0,0,342,346,3,60,30,0,343,346, + 3,62,31,0,344,346,3,64,32,0,345,342,1,0,0,0,345,343,1,0,0,0,345,344,1,0, + 0,0,346,59,1,0,0,0,347,348,5,30,0,0,348,349,3,30,15,0,349,350,5,31,0,0, + 350,351,5,14,0,0,351,352,3,78,39,0,352,353,5,16,0,0,353,354,5,2,0,0,354, + 61,1,0,0,0,355,356,5,31,0,0,356,357,5,14,0,0,357,358,3,78,39,0,358,359, + 5,16,0,0,359,360,3,30,15,0,360,63,1,0,0,0,361,362,5,32,0,0,362,363,5,14, + 0,0,363,364,3,66,33,0,364,365,5,2,0,0,365,366,3,78,39,0,366,367,5,2,0,0, + 367,368,3,48,24,0,368,369,5,16,0,0,369,370,3,30,15,0,370,65,1,0,0,0,371, + 374,3,42,21,0,372,374,3,48,24,0,373,371,1,0,0,0,373,372,1,0,0,0,374,67, + 1,0,0,0,375,376,5,76,0,0,376,69,1,0,0,0,377,380,5,82,0,0,378,380,3,82,41, + 0,379,377,1,0,0,0,379,378,1,0,0,0,380,71,1,0,0,0,381,393,5,14,0,0,382,387, + 3,70,35,0,383,384,5,15,0,0,384,386,3,70,35,0,385,383,1,0,0,0,386,389,1, + 0,0,0,387,385,1,0,0,0,387,388,1,0,0,0,388,391,1,0,0,0,389,387,1,0,0,0,390, + 392,5,15,0,0,391,390,1,0,0,0,391,392,1,0,0,0,392,394,1,0,0,0,393,382,1, + 0,0,0,393,394,1,0,0,0,394,395,1,0,0,0,395,396,5,16,0,0,396,73,1,0,0,0,397, + 398,5,82,0,0,398,399,3,76,38,0,399,75,1,0,0,0,400,412,5,14,0,0,401,406, + 3,78,39,0,402,403,5,15,0,0,403,405,3,78,39,0,404,402,1,0,0,0,405,408,1, + 0,0,0,406,404,1,0,0,0,406,407,1,0,0,0,407,410,1,0,0,0,408,406,1,0,0,0,409, + 411,5,15,0,0,410,409,1,0,0,0,410,411,1,0,0,0,411,413,1,0,0,0,412,401,1, + 0,0,0,412,413,1,0,0,0,413,414,1,0,0,0,414,415,5,16,0,0,415,77,1,0,0,0,416, + 417,6,39,-1,0,417,418,5,14,0,0,418,419,3,78,39,0,419,420,5,16,0,0,420,466, + 1,0,0,0,421,422,3,88,44,0,422,423,5,14,0,0,423,425,3,78,39,0,424,426,5, + 15,0,0,425,424,1,0,0,0,425,426,1,0,0,0,426,427,1,0,0,0,427,428,5,16,0,0, + 428,466,1,0,0,0,429,466,3,74,37,0,430,431,5,33,0,0,431,432,5,82,0,0,432, + 466,3,76,38,0,433,434,5,36,0,0,434,435,5,34,0,0,435,436,3,78,39,0,436,437, + 5,35,0,0,437,438,7,3,0,0,438,466,1,0,0,0,439,440,5,42,0,0,440,441,5,34, + 0,0,441,442,3,78,39,0,442,443,5,35,0,0,443,444,7,4,0,0,444,466,1,0,0,0, + 445,446,7,5,0,0,446,466,3,78,39,15,447,459,5,34,0,0,448,453,3,78,39,0,449, + 450,5,15,0,0,450,452,3,78,39,0,451,449,1,0,0,0,452,455,1,0,0,0,453,451, + 1,0,0,0,453,454,1,0,0,0,454,457,1,0,0,0,455,453,1,0,0,0,456,458,5,15,0, + 0,457,456,1,0,0,0,457,458,1,0,0,0,458,460,1,0,0,0,459,448,1,0,0,0,459,460, + 1,0,0,0,460,461,1,0,0,0,461,466,5,35,0,0,462,466,5,81,0,0,463,466,5,82, + 0,0,464,466,3,82,41,0,465,416,1,0,0,0,465,421,1,0,0,0,465,429,1,0,0,0,465, + 430,1,0,0,0,465,433,1,0,0,0,465,439,1,0,0,0,465,445,1,0,0,0,465,447,1,0, + 0,0,465,462,1,0,0,0,465,463,1,0,0,0,465,464,1,0,0,0,466,519,1,0,0,0,467, + 468,10,14,0,0,468,469,7,6,0,0,469,518,3,78,39,15,470,471,10,13,0,0,471, + 472,7,7,0,0,472,518,3,78,39,14,473,474,10,12,0,0,474,475,7,8,0,0,475,518, + 3,78,39,13,476,477,10,11,0,0,477,478,7,9,0,0,478,518,3,78,39,12,479,480, + 10,10,0,0,480,481,7,10,0,0,481,518,3,78,39,11,482,483,10,9,0,0,483,484, + 5,61,0,0,484,518,3,78,39,10,485,486,10,8,0,0,486,487,5,4,0,0,487,518,3, + 78,39,9,488,489,10,7,0,0,489,490,5,62,0,0,490,518,3,78,39,8,491,492,10, + 6,0,0,492,493,5,63,0,0,493,518,3,78,39,7,494,495,10,5,0,0,495,496,5,64, + 0,0,496,518,3,78,39,6,497,498,10,21,0,0,498,499,5,34,0,0,499,500,5,69,0, + 0,500,518,5,35,0,0,501,502,10,18,0,0,502,518,7,11,0,0,503,504,10,17,0,0, + 504,505,5,49,0,0,505,506,5,14,0,0,506,507,3,78,39,0,507,508,5,16,0,0,508, + 518,1,0,0,0,509,510,10,16,0,0,510,511,5,50,0,0,511,512,5,14,0,0,512,513, + 3,78,39,0,513,514,5,15,0,0,514,515,3,78,39,0,515,516,5,16,0,0,516,518,1, + 0,0,0,517,467,1,0,0,0,517,470,1,0,0,0,517,473,1,0,0,0,517,476,1,0,0,0,517, + 479,1,0,0,0,517,482,1,0,0,0,517,485,1,0,0,0,517,488,1,0,0,0,517,491,1,0, + 0,0,517,494,1,0,0,0,517,497,1,0,0,0,517,501,1,0,0,0,517,503,1,0,0,0,517, + 509,1,0,0,0,518,521,1,0,0,0,519,517,1,0,0,0,519,520,1,0,0,0,520,79,1,0, + 0,0,521,519,1,0,0,0,522,523,7,12,0,0,523,81,1,0,0,0,524,530,5,67,0,0,525, + 530,3,84,42,0,526,530,5,76,0,0,527,530,5,77,0,0,528,530,5,78,0,0,529,524, + 1,0,0,0,529,525,1,0,0,0,529,526,1,0,0,0,529,527,1,0,0,0,529,528,1,0,0,0, + 530,83,1,0,0,0,531,533,5,69,0,0,532,534,5,68,0,0,533,532,1,0,0,0,533,534, + 1,0,0,0,534,85,1,0,0,0,535,536,7,13,0,0,536,87,1,0,0,0,537,538,7,14,0,0, + 538,89,1,0,0,0,48,93,99,105,119,122,135,147,152,170,184,195,199,201,209, + 218,223,229,239,249,254,260,272,283,289,295,301,308,317,326,340,345,373, + 379,387,391,393,406,410,412,425,453,457,459,465,517,519,529,533]; private static __ATN: ATN; public static get _ATN(): ATN { @@ -2564,9 +3041,6 @@ export class SourceFileContext extends ParserRuleContext { super(parent, invokingState); this.parser = parser; } - public contractDefinition(): ContractDefinitionContext { - return this.getTypedRuleContext(ContractDefinitionContext, 0) as ContractDefinitionContext; - } public EOF(): TerminalNode { return this.getToken(CashScriptParser.EOF, 0); } @@ -2576,6 +3050,18 @@ export class SourceFileContext extends ParserRuleContext { public pragmaDirective(i: number): PragmaDirectiveContext { return this.getTypedRuleContext(PragmaDirectiveContext, i) as PragmaDirectiveContext; } + public importDirective_list(): ImportDirectiveContext[] { + return this.getTypedRuleContexts(ImportDirectiveContext) as ImportDirectiveContext[]; + } + public importDirective(i: number): ImportDirectiveContext { + return this.getTypedRuleContext(ImportDirectiveContext, i) as ImportDirectiveContext; + } + public topLevelDefinition_list(): TopLevelDefinitionContext[] { + return this.getTypedRuleContexts(TopLevelDefinitionContext) as TopLevelDefinitionContext[]; + } + public topLevelDefinition(i: number): TopLevelDefinitionContext { + return this.getTypedRuleContext(TopLevelDefinitionContext, i) as TopLevelDefinitionContext; + } public get ruleIndex(): number { return CashScriptParser.RULE_sourceFile; } @@ -2703,6 +3189,118 @@ export class VersionOperatorContext extends ParserRuleContext { } +export class ImportDirectiveContext extends ParserRuleContext { + constructor(parser?: CashScriptParser, parent?: ParserRuleContext, invokingState?: number) { + super(parent, invokingState); + this.parser = parser; + } + public StringLiteral(): TerminalNode { + return this.getToken(CashScriptParser.StringLiteral, 0); + } + public get ruleIndex(): number { + return CashScriptParser.RULE_importDirective; + } + // @Override + public accept(visitor: CashScriptVisitor): Result { + if (visitor.visitImportDirective) { + return visitor.visitImportDirective(this); + } else { + return visitor.visitChildren(this); + } + } +} + + +export class TopLevelDefinitionContext extends ParserRuleContext { + constructor(parser?: CashScriptParser, parent?: ParserRuleContext, invokingState?: number) { + super(parent, invokingState); + this.parser = parser; + } + public globalFunctionDefinition(): GlobalFunctionDefinitionContext { + return this.getTypedRuleContext(GlobalFunctionDefinitionContext, 0) as GlobalFunctionDefinitionContext; + } + public constantDefinition(): ConstantDefinitionContext { + return this.getTypedRuleContext(ConstantDefinitionContext, 0) as ConstantDefinitionContext; + } + public contractDefinition(): ContractDefinitionContext { + return this.getTypedRuleContext(ContractDefinitionContext, 0) as ContractDefinitionContext; + } + public get ruleIndex(): number { + return CashScriptParser.RULE_topLevelDefinition; + } + // @Override + public accept(visitor: CashScriptVisitor): Result { + if (visitor.visitTopLevelDefinition) { + return visitor.visitTopLevelDefinition(this); + } else { + return visitor.visitChildren(this); + } + } +} + + +export class GlobalFunctionDefinitionContext extends ParserRuleContext { + constructor(parser?: CashScriptParser, parent?: ParserRuleContext, invokingState?: number) { + super(parent, invokingState); + this.parser = parser; + } + public Identifier(): TerminalNode { + return this.getToken(CashScriptParser.Identifier, 0); + } + public parameterList(): ParameterListContext { + return this.getTypedRuleContext(ParameterListContext, 0) as ParameterListContext; + } + public functionBody(): FunctionBodyContext { + return this.getTypedRuleContext(FunctionBodyContext, 0) as FunctionBodyContext; + } + public typeName_list(): TypeNameContext[] { + return this.getTypedRuleContexts(TypeNameContext) as TypeNameContext[]; + } + public typeName(i: number): TypeNameContext { + return this.getTypedRuleContext(TypeNameContext, i) as TypeNameContext; + } + public get ruleIndex(): number { + return CashScriptParser.RULE_globalFunctionDefinition; + } + // @Override + public accept(visitor: CashScriptVisitor): Result { + if (visitor.visitGlobalFunctionDefinition) { + return visitor.visitGlobalFunctionDefinition(this); + } else { + return visitor.visitChildren(this); + } + } +} + + +export class ConstantDefinitionContext extends ParserRuleContext { + constructor(parser?: CashScriptParser, parent?: ParserRuleContext, invokingState?: number) { + super(parent, invokingState); + this.parser = parser; + } + public typeName(): TypeNameContext { + return this.getTypedRuleContext(TypeNameContext, 0) as TypeNameContext; + } + public Identifier(): TerminalNode { + return this.getToken(CashScriptParser.Identifier, 0); + } + public expression(): ExpressionContext { + return this.getTypedRuleContext(ExpressionContext, 0) as ExpressionContext; + } + public get ruleIndex(): number { + return CashScriptParser.RULE_constantDefinition; + } + // @Override + public accept(visitor: CashScriptVisitor): Result { + if (visitor.visitConstantDefinition) { + return visitor.visitConstantDefinition(this); + } else { + return visitor.visitChildren(this); + } + } +} + + export class ContractDefinitionContext extends ParserRuleContext { constructor(parser?: CashScriptParser, parent?: ParserRuleContext, invokingState?: number) { super(parent, invokingState); @@ -2714,11 +3312,11 @@ export class ContractDefinitionContext extends ParserRuleContext { public parameterList(): ParameterListContext { return this.getTypedRuleContext(ParameterListContext, 0) as ParameterListContext; } - public functionDefinition_list(): FunctionDefinitionContext[] { - return this.getTypedRuleContexts(FunctionDefinitionContext) as FunctionDefinitionContext[]; + public contractFunctionDefinition_list(): ContractFunctionDefinitionContext[] { + return this.getTypedRuleContexts(ContractFunctionDefinitionContext) as ContractFunctionDefinitionContext[]; } - public functionDefinition(i: number): FunctionDefinitionContext { - return this.getTypedRuleContext(FunctionDefinitionContext, i) as FunctionDefinitionContext; + public contractFunctionDefinition(i: number): ContractFunctionDefinitionContext { + return this.getTypedRuleContext(ContractFunctionDefinitionContext, i) as ContractFunctionDefinitionContext; } public get ruleIndex(): number { return CashScriptParser.RULE_contractDefinition; @@ -2734,7 +3332,7 @@ export class ContractDefinitionContext extends ParserRuleContext { } -export class FunctionDefinitionContext extends ParserRuleContext { +export class ContractFunctionDefinitionContext extends ParserRuleContext { constructor(parser?: CashScriptParser, parent?: ParserRuleContext, invokingState?: number) { super(parent, invokingState); this.parser = parser; @@ -2749,12 +3347,12 @@ export class FunctionDefinitionContext extends ParserRuleContext { return this.getTypedRuleContext(FunctionBodyContext, 0) as FunctionBodyContext; } public get ruleIndex(): number { - return CashScriptParser.RULE_functionDefinition; + return CashScriptParser.RULE_contractFunctionDefinition; } // @Override public accept(visitor: CashScriptVisitor): Result { - if (visitor.visitFunctionDefinition) { - return visitor.visitFunctionDefinition(this); + if (visitor.visitContractFunctionDefinition) { + return visitor.visitContractFunctionDefinition(this); } else { return visitor.visitChildren(this); } @@ -2823,6 +3421,12 @@ export class ParameterContext extends ParserRuleContext { public Identifier(): TerminalNode { return this.getToken(CashScriptParser.Identifier, 0); } + public modifier_list(): ModifierContext[] { + return this.getTypedRuleContexts(ModifierContext) as ModifierContext[]; + } + public modifier(i: number): ModifierContext { + return this.getTypedRuleContext(ModifierContext, i) as ModifierContext; + } public get ruleIndex(): number { return CashScriptParser.RULE_parameter; } @@ -2907,9 +3511,15 @@ export class NonControlStatementContext extends ParserRuleContext { public requireStatement(): RequireStatementContext { return this.getTypedRuleContext(RequireStatementContext, 0) as RequireStatementContext; } + public functionCallStatement(): FunctionCallStatementContext { + return this.getTypedRuleContext(FunctionCallStatementContext, 0) as FunctionCallStatementContext; + } public consoleStatement(): ConsoleStatementContext { return this.getTypedRuleContext(ConsoleStatementContext, 0) as ConsoleStatementContext; } + public returnStatement(): ReturnStatementContext { + return this.getTypedRuleContext(ReturnStatementContext, 0) as ReturnStatementContext; + } public get ruleIndex(): number { return CashScriptParser.RULE_nonControlStatement; } @@ -2924,6 +3534,53 @@ export class NonControlStatementContext extends ParserRuleContext { } +export class FunctionCallStatementContext extends ParserRuleContext { + constructor(parser?: CashScriptParser, parent?: ParserRuleContext, invokingState?: number) { + super(parent, invokingState); + this.parser = parser; + } + public functionCall(): FunctionCallContext { + return this.getTypedRuleContext(FunctionCallContext, 0) as FunctionCallContext; + } + public get ruleIndex(): number { + return CashScriptParser.RULE_functionCallStatement; + } + // @Override + public accept(visitor: CashScriptVisitor): Result { + if (visitor.visitFunctionCallStatement) { + return visitor.visitFunctionCallStatement(this); + } else { + return visitor.visitChildren(this); + } + } +} + + +export class ReturnStatementContext extends ParserRuleContext { + constructor(parser?: CashScriptParser, parent?: ParserRuleContext, invokingState?: number) { + super(parent, invokingState); + this.parser = parser; + } + public expression_list(): ExpressionContext[] { + return this.getTypedRuleContexts(ExpressionContext) as ExpressionContext[]; + } + public expression(i: number): ExpressionContext { + return this.getTypedRuleContext(ExpressionContext, i) as ExpressionContext; + } + public get ruleIndex(): number { + return CashScriptParser.RULE_returnStatement; + } + // @Override + public accept(visitor: CashScriptVisitor): Result { + if (visitor.visitReturnStatement) { + return visitor.visitReturnStatement(this); + } else { + return visitor.visitChildren(this); + } + } +} + + export class ControlStatementContext extends ParserRuleContext { constructor(parser?: CashScriptParser, parent?: ParserRuleContext, invokingState?: number) { super(parent, invokingState); @@ -2988,17 +3645,11 @@ export class TupleAssignmentContext extends ParserRuleContext { super(parent, invokingState); this.parser = parser; } - public typeName_list(): TypeNameContext[] { - return this.getTypedRuleContexts(TypeNameContext) as TypeNameContext[]; - } - public typeName(i: number): TypeNameContext { - return this.getTypedRuleContext(TypeNameContext, i) as TypeNameContext; - } - public Identifier_list(): TerminalNode[] { - return this.getTokens(CashScriptParser.Identifier); + public tupleTarget_list(): TupleTargetContext[] { + return this.getTypedRuleContexts(TupleTargetContext) as TupleTargetContext[]; } - public Identifier(i: number): TerminalNode { - return this.getToken(CashScriptParser.Identifier, i); + public tupleTarget(i: number): TupleTargetContext { + return this.getTypedRuleContext(TupleTargetContext, i) as TupleTargetContext; } public expression(): ExpressionContext { return this.getTypedRuleContext(ExpressionContext, 0) as ExpressionContext; @@ -3017,6 +3668,37 @@ export class TupleAssignmentContext extends ParserRuleContext { } +export class TupleTargetContext extends ParserRuleContext { + constructor(parser?: CashScriptParser, parent?: ParserRuleContext, invokingState?: number) { + super(parent, invokingState); + this.parser = parser; + } + public typeName(): TypeNameContext { + return this.getTypedRuleContext(TypeNameContext, 0) as TypeNameContext; + } + public Identifier(): TerminalNode { + return this.getToken(CashScriptParser.Identifier, 0); + } + public modifier_list(): ModifierContext[] { + return this.getTypedRuleContexts(ModifierContext) as ModifierContext[]; + } + public modifier(i: number): ModifierContext { + return this.getTypedRuleContext(ModifierContext, i) as ModifierContext; + } + public get ruleIndex(): number { + return CashScriptParser.RULE_tupleTarget; + } + // @Override + public accept(visitor: CashScriptVisitor): Result { + if (visitor.visitTupleTarget) { + return visitor.visitTupleTarget(this); + } else { + return visitor.visitChildren(this); + } + } +} + + export class AssignStatementContext extends ParserRuleContext { public _op!: Token; constructor(parser?: CashScriptParser, parent?: ParserRuleContext, invokingState?: number) { diff --git a/packages/cashc/src/grammar/CashScriptVisitor.ts b/packages/cashc/src/grammar/CashScriptVisitor.ts index ffb8f081c..3c4d117bc 100644 --- a/packages/cashc/src/grammar/CashScriptVisitor.ts +++ b/packages/cashc/src/grammar/CashScriptVisitor.ts @@ -9,17 +9,24 @@ import { PragmaNameContext } from "./CashScriptParser.js"; import { PragmaValueContext } from "./CashScriptParser.js"; import { VersionConstraintContext } from "./CashScriptParser.js"; import { VersionOperatorContext } from "./CashScriptParser.js"; +import { ImportDirectiveContext } from "./CashScriptParser.js"; +import { TopLevelDefinitionContext } from "./CashScriptParser.js"; +import { GlobalFunctionDefinitionContext } from "./CashScriptParser.js"; +import { ConstantDefinitionContext } from "./CashScriptParser.js"; import { ContractDefinitionContext } from "./CashScriptParser.js"; -import { FunctionDefinitionContext } from "./CashScriptParser.js"; +import { ContractFunctionDefinitionContext } from "./CashScriptParser.js"; import { FunctionBodyContext } from "./CashScriptParser.js"; import { ParameterListContext } from "./CashScriptParser.js"; import { ParameterContext } from "./CashScriptParser.js"; import { BlockContext } from "./CashScriptParser.js"; import { StatementContext } from "./CashScriptParser.js"; import { NonControlStatementContext } from "./CashScriptParser.js"; +import { FunctionCallStatementContext } from "./CashScriptParser.js"; +import { ReturnStatementContext } from "./CashScriptParser.js"; import { ControlStatementContext } from "./CashScriptParser.js"; import { VariableDefinitionContext } from "./CashScriptParser.js"; import { TupleAssignmentContext } from "./CashScriptParser.js"; +import { TupleTargetContext } from "./CashScriptParser.js"; import { AssignStatementContext } from "./CashScriptParser.js"; import { TimeOpStatementContext } from "./CashScriptParser.js"; import { RequireStatementContext } from "./CashScriptParser.js"; @@ -99,6 +106,30 @@ export default class CashScriptVisitor extends ParseTreeVisitor * @return the visitor result */ visitVersionOperator?: (ctx: VersionOperatorContext) => Result; + /** + * Visit a parse tree produced by `CashScriptParser.importDirective`. + * @param ctx the parse tree + * @return the visitor result + */ + visitImportDirective?: (ctx: ImportDirectiveContext) => Result; + /** + * Visit a parse tree produced by `CashScriptParser.topLevelDefinition`. + * @param ctx the parse tree + * @return the visitor result + */ + visitTopLevelDefinition?: (ctx: TopLevelDefinitionContext) => Result; + /** + * Visit a parse tree produced by `CashScriptParser.globalFunctionDefinition`. + * @param ctx the parse tree + * @return the visitor result + */ + visitGlobalFunctionDefinition?: (ctx: GlobalFunctionDefinitionContext) => Result; + /** + * Visit a parse tree produced by `CashScriptParser.constantDefinition`. + * @param ctx the parse tree + * @return the visitor result + */ + visitConstantDefinition?: (ctx: ConstantDefinitionContext) => Result; /** * Visit a parse tree produced by `CashScriptParser.contractDefinition`. * @param ctx the parse tree @@ -106,11 +137,11 @@ export default class CashScriptVisitor extends ParseTreeVisitor */ visitContractDefinition?: (ctx: ContractDefinitionContext) => Result; /** - * Visit a parse tree produced by `CashScriptParser.functionDefinition`. + * Visit a parse tree produced by `CashScriptParser.contractFunctionDefinition`. * @param ctx the parse tree * @return the visitor result */ - visitFunctionDefinition?: (ctx: FunctionDefinitionContext) => Result; + visitContractFunctionDefinition?: (ctx: ContractFunctionDefinitionContext) => Result; /** * Visit a parse tree produced by `CashScriptParser.functionBody`. * @param ctx the parse tree @@ -147,6 +178,18 @@ export default class CashScriptVisitor extends ParseTreeVisitor * @return the visitor result */ visitNonControlStatement?: (ctx: NonControlStatementContext) => Result; + /** + * Visit a parse tree produced by `CashScriptParser.functionCallStatement`. + * @param ctx the parse tree + * @return the visitor result + */ + visitFunctionCallStatement?: (ctx: FunctionCallStatementContext) => Result; + /** + * Visit a parse tree produced by `CashScriptParser.returnStatement`. + * @param ctx the parse tree + * @return the visitor result + */ + visitReturnStatement?: (ctx: ReturnStatementContext) => Result; /** * Visit a parse tree produced by `CashScriptParser.controlStatement`. * @param ctx the parse tree @@ -165,6 +208,12 @@ export default class CashScriptVisitor extends ParseTreeVisitor * @return the visitor result */ visitTupleAssignment?: (ctx: TupleAssignmentContext) => Result; + /** + * Visit a parse tree produced by `CashScriptParser.tupleTarget`. + * @param ctx the parse tree + * @return the visitor result + */ + visitTupleTarget?: (ctx: TupleTargetContext) => Result; /** * Visit a parse tree produced by `CashScriptParser.assignStatement`. * @param ctx the parse tree diff --git a/packages/cashc/src/index.ts b/packages/cashc/src/index.ts index 4321d8085..708f394a6 100644 --- a/packages/cashc/src/index.ts +++ b/packages/cashc/src/index.ts @@ -1,7 +1,10 @@ export * from './Errors.js'; +export * from './Warnings.js'; export * as utils from '@cashscript/utils'; -export { compileFile, compileString, type CompileOptions } from './compiler.js'; +export { + compileFile, compileString, type CompileOptions, type CompileStringOptions, +} from './compiler.js'; export * from './ast/Location.js'; export * from './ast/error-listeners.js'; -export const version = '0.13.3'; +export const version = '0.14.0-next.5'; diff --git a/packages/cashc/src/internal.ts b/packages/cashc/src/internal.ts new file mode 100644 index 000000000..583874697 --- /dev/null +++ b/packages/cashc/src/internal.ts @@ -0,0 +1,8 @@ +/* + * Internal entry point for this repo's own test suites. The compile functions exported here also + * accept internal-only compiler options (e.g. `disableInlining`), which are deliberately absent + * from the package's public API. This module is not re-exported from the package index and + * carries no stability guarantees. + */ +export { compileFileInternal as compileFile, compileStringInternal as compileString, DEFAULT_COMPILER_OPTIONS } from './compiler.js'; +export type { InternalCompilerOptions } from './compiler.js'; diff --git a/packages/cashc/src/parser.ts b/packages/cashc/src/parser.ts new file mode 100644 index 000000000..e7e0e799d --- /dev/null +++ b/packages/cashc/src/parser.ts @@ -0,0 +1,30 @@ +import { CharStream, CommonTokenStream } from 'antlr4'; +import { Ast } from './ast/AST.js'; +import AstBuilder from './ast/AstBuilder.js'; +import { ThrowingErrorListener, CashScriptErrorListener, ForwardingErrorListener } from './ast/error-listeners.js'; +import CashScriptLexer from './grammar/CashScriptLexer.js'; +import CashScriptParser from './grammar/CashScriptParser.js'; + +export function parseCode( + code: string, + errorListener: CashScriptErrorListener = ThrowingErrorListener.INSTANCE, +): Ast { + const syntaxErrorListener = new ForwardingErrorListener(errorListener); + + // Lexing (throwing on errors) + const inputStream = new CharStream(code); + const lexer = new CashScriptLexer(inputStream); + lexer.removeErrorListeners(); + lexer.addErrorListener(syntaxErrorListener); + const tokenStream = new CommonTokenStream(lexer); + + // Parsing (throwing on errors) + const parser = new CashScriptParser(tokenStream); + parser.removeErrorListeners(); + parser.addErrorListener(syntaxErrorListener); + const parseTree = parser.sourceFile(); + syntaxErrorListener.throwFirstError(); + + // AST building + return new AstBuilder(parseTree).build() as Ast; +} diff --git a/packages/cashc/src/print/OutputSourceCodeTraversal.ts b/packages/cashc/src/print/OutputSourceCodeTraversal.ts index 70f62db2c..cdf7fead6 100644 --- a/packages/cashc/src/print/OutputSourceCodeTraversal.ts +++ b/packages/cashc/src/print/OutputSourceCodeTraversal.ts @@ -2,10 +2,13 @@ import { binToHex } from '@bitauth/libauth'; import { SymbolTable } from '../ast/SymbolTable.js'; import { Node, + SourceFileNode, + ImportNode, ContractNode, ParameterNode, VariableDefinitionNode, FunctionDefinitionNode, + ConstantDefinitionNode, AssignNode, IdentifierNode, BranchNode, @@ -22,16 +25,19 @@ import { ArrayNode, TupleIndexOpNode, RequireNode, + ReturnNode, InstantiationNode, TupleAssignmentNode, NullaryOpNode, ConsoleStatementNode, ConsoleParameterNode, + FunctionCallStatementNode, SliceNode, DoWhileNode, WhileNode, ForNode, NonControlStatementNode, + ExpressionNode, } from '../ast/AST.js'; import AstTraversal from '../ast/AstTraversal.js'; @@ -61,6 +67,19 @@ export default class OutputSourceCodeTraversal extends AstTraversal { this.addOutput(` --> ST: ${symbolTable}`); } + visitSourceFile(node: SourceFileNode): Node { + node.imports = this.visitList(node.imports) as ImportNode[]; + node.constants = this.visitList(node.constants) as ConstantDefinitionNode[]; + node.functions = this.visitList(node.functions) as FunctionDefinitionNode[]; + if (node.contract) node.contract = this.visit(node.contract) as ContractNode; + return node; + } + + visitImport(node: ImportNode): Node { + this.addOutput(`import "${node.path}";\n`, true); + return node; + } + visitContract(node: ContractNode): Node { this.addOutput(`contract ${node.name}(`, true); node.parameters = this.visitCommaList(node.parameters) as ParameterNode[]; @@ -80,6 +99,7 @@ export default class OutputSourceCodeTraversal extends AstTraversal { this.addOutput(`function ${node.name}(`, true); node.parameters = this.visitCommaList(node.parameters) as ParameterNode[]; this.addOutput(')'); + if (node.returnTypes) this.addOutput(` returns (${node.returnTypes.join(', ')})`); this.outputSymbolTable(node.symbolTable); this.addOutput(' '); @@ -89,6 +109,13 @@ export default class OutputSourceCodeTraversal extends AstTraversal { return node; } + visitConstantDefinition(node: ConstantDefinitionNode): Node { + this.addOutput(`${node.type} constant ${node.name} = `, true); + node.value = this.visit(node.value) as ExpressionNode; + this.addOutput(';\n'); + return node; + } + visitCommaList(list: Node[]): Node[] { return list.map((e, i) => { const visited = this.visit(e); @@ -100,19 +127,28 @@ export default class OutputSourceCodeTraversal extends AstTraversal { } visitParameter(node: ParameterNode): Node { - this.addOutput(`${node.type} ${node.name}`); + const modifiers = node.modifiers.length > 0 ? `${node.modifiers.join(' ')} ` : ''; + this.addOutput(`${node.type} ${modifiers}${node.name}`); return node; } visitVariableDefinition(node: VariableDefinitionNode): Node { - this.addOutput(`${node.type} ${node.name} = `, true); + const modifiers = node.modifiers.length > 0 ? `${node.modifiers.join(' ')} ` : ''; + this.addOutput(`${node.type} ${modifiers}${node.name} = `, true); this.visit(node.expression); return node; } visitTupleAssignment(node: TupleAssignmentNode): Node { - this.addOutput(`${node.left.type} ${node.left.name}, ${node.right.type} ${node.right.name} = `, true); + const targets = node.targets + .map((target) => { + if (target.isReassignment) return target.identifier.name; + const modifiers = target.modifiers.length > 0 ? `${target.modifiers.join(' ')} ` : ''; + return `${target.type} ${modifiers}${target.identifier.name}`; + }) + .join(', '); + this.addOutput(`${targets} = `, true); this.visit(node.tuple); return node; @@ -147,6 +183,18 @@ export default class OutputSourceCodeTraversal extends AstTraversal { return node; } + visitReturn(node: ReturnNode): Node { + this.addOutput('return ', true); + node.expressions = this.visitCommaList(node.expressions) as ExpressionNode[]; + return node; + } + + visitFunctionCallStatement(node: FunctionCallStatementNode): Node { + this.addOutput('', true); + node.functionCall = this.visit(node.functionCall) as FunctionCallNode; + return node; + } + visitConsoleStatement(node: ConsoleStatementNode): Node { this.addOutput('console.log(', true); node.parameters = this.visitCommaList(node.parameters) as ConsoleParameterNode[]; diff --git a/packages/cashc/src/semantic/DeadCodeEliminationTraversal.ts b/packages/cashc/src/semantic/DeadCodeEliminationTraversal.ts new file mode 100644 index 000000000..cacaff94d --- /dev/null +++ b/packages/cashc/src/semantic/DeadCodeEliminationTraversal.ts @@ -0,0 +1,36 @@ +import { + FunctionCallNode, + FunctionDefinitionNode, + Node, + SourceFileNode, +} from '../ast/AST.js'; +import AstTraversal from '../ast/AstTraversal.js'; + +export default class DeadCodeEliminationTraversal extends AstTraversal { + private visitedFunctions: FunctionDefinitionNode[] = []; + private reachableFunctions: FunctionDefinitionNode[] = []; + + visitSourceFile(node: SourceFileNode): Node { + super.visitOptional(node.contract); + + // The reachable functions are stored callee-first, which code generation relies on + node.functions = this.reachableFunctions; + return node; + } + + visitFunctionCall(node: FunctionCallNode): Node { + node = super.visitFunctionCall(node) as FunctionCallNode; + + const functionDefinition = node.identifier.symbol?.definition; + if (!functionDefinition || !(functionDefinition instanceof FunctionDefinitionNode)) return node; + + // Only descend into a function the first time it is reached to prevent infinite recursion. + if (!this.visitedFunctions.includes(functionDefinition)) { + this.visitedFunctions.push(functionDefinition); + this.visit(functionDefinition.body); + this.reachableFunctions.push(functionDefinition); + } + + return node; + } +} diff --git a/packages/cashc/src/semantic/EnsureFinalRequireTraversal.ts b/packages/cashc/src/semantic/EnsureFinalRequireTraversal.ts index 2974f16cb..bc474c3dc 100644 --- a/packages/cashc/src/semantic/EnsureFinalRequireTraversal.ts +++ b/packages/cashc/src/semantic/EnsureFinalRequireTraversal.ts @@ -3,6 +3,7 @@ import { ParameterNode, FunctionDefinitionNode, RequireNode, + ReturnNode, TimeOpNode, BranchNode, ConsoleStatementNode, @@ -10,9 +11,17 @@ import { WhileNode, ForNode, BlockNode, + StatementNode, + Node, } from '../ast/AST.js'; import AstTraversal from '../ast/AstTraversal.js'; -import { EmptyContractError, EmptyFunctionError, FinalRequireStatementError } from '../Errors.js'; +import { + EmptyContractError, + EmptyFunctionError, + FinalRequireStatementError, + MissingReturnError, + MisplacedReturnError, +} from '../Errors.js'; export default class EnsureFinalRequireTraversal extends AstTraversal { visitContract(node: ContractNode): ContractNode { @@ -34,12 +43,57 @@ export default class EnsureFinalRequireTraversal extends AstTraversal { throw new EmptyFunctionError(node); } - ensureFinalStatementIsRequire(node.body); + if (node.returnTypes !== undefined) { + ensureSingleTailReturn(node.body); + } else { + ensureFinalStatementIsRequire(node.body); + } return node; } } +// TODO: This code is a bit convoluted, but we're likely to make changes to allow early returns before a mainline release, +// so we're leaving this code as-is for now. + +function ensureSingleTailReturn(body: BlockNode): void { + const statements = body.statements ?? []; + const finalStatement = statements[statements.length - 1]; + if (!(finalStatement instanceof ReturnNode)) { + throw new MissingReturnError(finalStatement ?? body); + } + + const stray = findReturn(statements.slice(0, -1)); + if (stray) throw new MisplacedReturnError(stray); +} + +function findReturn(statements: StatementNode[]): ReturnNode | undefined { + for (const statement of statements) { + if (statement instanceof ReturnNode) return statement; + for (const list of nestedStatementLists(statement)) { + const found = findReturn(list); + if (found) return found; + } + } + return undefined; +} + +function nestedStatementLists(statement: StatementNode): StatementNode[][] { + const asStatements = (block?: Node): StatementNode[] => { + if (!block) return []; + if (block instanceof BlockNode) return block.statements ?? []; + return [block as StatementNode]; + }; + + if (statement instanceof BranchNode) { + return [asStatements(statement.ifBlock), asStatements(statement.elseBlock)]; + } + if (statement instanceof DoWhileNode || statement instanceof WhileNode || statement instanceof ForNode) { + return [asStatements(statement.block)]; + } + return []; +} + function ensureFinalStatementIsRequire(block: BlockNode): void { const statementsWithoutLogs = (block.statements ?? []).filter((statement) => !(statement instanceof ConsoleStatementNode)); const finalStatement = statementsWithoutLogs[statementsWithoutLogs.length - 1]; diff --git a/packages/cashc/src/semantic/EnsureFunctionsSafeTraversal.ts b/packages/cashc/src/semantic/EnsureFunctionsSafeTraversal.ts new file mode 100644 index 000000000..dabd9ab4e --- /dev/null +++ b/packages/cashc/src/semantic/EnsureFunctionsSafeTraversal.ts @@ -0,0 +1,46 @@ +import { + FunctionCallNode, + FunctionDefinitionNode, + FunctionKind, + Node, + NullaryOpNode, +} from '../ast/AST.js'; +import AstTraversal from '../ast/AstTraversal.js'; +import { GlobalFunction } from '../ast/Globals.js'; +import { NullaryOperator } from '../ast/Operator.js'; +import { UnsafeFunctionOperationError } from '../Errors.js'; + +// checkSig and checkMultisig, as well as this.activeBytecode use the function's bytecode instead of the contract's. +// This is almost never what a developer would expect, so we reject using these inside global functions altogether. +export default class EnsureFunctionsSafeTraversal extends AstTraversal { + private insideGlobalFunction = false; + + visitFunctionDefinition(node: FunctionDefinitionNode): Node { + const enclosingInsideGlobalFunction = this.insideGlobalFunction; + this.insideGlobalFunction = node.kind === FunctionKind.GLOBAL; + + node = super.visitFunctionDefinition(node) as FunctionDefinitionNode; + + this.insideGlobalFunction = enclosingInsideGlobalFunction; + return node; + } + + visitNullaryOp(node: NullaryOpNode): Node { + if (this.insideGlobalFunction && node.operator === NullaryOperator.BYTECODE) { + throw new UnsafeFunctionOperationError(node, node.operator); + } + + return node; + } + + visitFunctionCall(node: FunctionCallNode): Node { + node = super.visitFunctionCall(node) as FunctionCallNode; + + const UNSAFE_FUNCTIONS: string[] = [GlobalFunction.CHECKSIG, GlobalFunction.CHECKMULTISIG]; + if (this.insideGlobalFunction && UNSAFE_FUNCTIONS.includes(node.identifier.name)) { + throw new UnsafeFunctionOperationError(node, node.identifier.name); + } + + return node; + } +} diff --git a/packages/cashc/src/semantic/FoldGlobalConstantsTraversal.ts b/packages/cashc/src/semantic/FoldGlobalConstantsTraversal.ts new file mode 100644 index 000000000..a75890077 --- /dev/null +++ b/packages/cashc/src/semantic/FoldGlobalConstantsTraversal.ts @@ -0,0 +1,149 @@ +import { PrimitiveType } from '@cashscript/utils'; +import { + BinaryOpNode, + ConstantDefinitionNode, + ExpressionNode, + HexLiteralNode, + IdentifierNode, + IntLiteralNode, + LiteralNode, + Node, + SourceFileNode, + StringLiteralNode, + UnaryOpNode, +} from '../ast/AST.js'; +import AstTraversal from '../ast/AstTraversal.js'; +import { GLOBAL_SYMBOL_TABLE } from '../ast/Globals.js'; +import { BinaryOperator, UnaryOperator } from '../ast/Operator.js'; +import { cloneConstantValue } from './LowerGlobalConstantsTraversal.js'; +import { resultingTypeForBinaryOp } from '../utils.js'; +import { + CashScriptError, + DivisionByZeroError, + InvalidConstantExpressionError, + UndefinedReferenceError, + UnequalTypeError, + UnsupportedTypeError, +} from '../Errors.js'; + +// Supports literals, references to other constants, integer arithmetic (+, -, *, /, %, unary -) and concatenation (+) +export class FoldGlobalConstantsTraversal extends AstTraversal { + private foldedConstants: Map = new Map(); + private functionNames: Set = new Set(); + + visitSourceFile(node: SourceFileNode): Node { + this.functionNames = new Set(node.functions.map((func) => func.name)); + node.constants = this.visitList(node.constants) as ConstantDefinitionNode[]; + return node; + } + + visitConstantDefinition(node: ConstantDefinitionNode): Node { + node.value = this.visitExpression(node.value); + this.foldedConstants.set(node.name, node); + return node; + } + + // Folds an expression through the regular visitor dispatch and rejects any expression kind + // that did not fold down to a single literal + private visitExpression(node: ExpressionNode): LiteralNode { + const folded = this.visit(node); + if (!(folded instanceof LiteralNode)) throw new InvalidConstantExpressionError(node); + return folded; + } + + visitIdentifier(node: IdentifierNode): Node { + const constant = this.foldedConstants.get(node.name); + if (constant) return cloneConstantValue(constant, node); + + // Existing names (except previously declared constants) are invalid in a constant initialiser + if (this.functionNames.has(node.name) || GLOBAL_SYMBOL_TABLE.get(node.name)) { + throw new InvalidConstantExpressionError(node); + } + + throw new UndefinedReferenceError(node); + } + + visitUnaryOp(node: UnaryOpNode): Node { + if (!FOLDABLE_UNARY_OPERATORS.includes(node.operator)) throw new InvalidConstantExpressionError(node); + + node.expression = this.visitExpression(node.expression); + if (!(node.expression instanceof IntLiteralNode)) { + throw new UnsupportedTypeError(node, node.expression.type, PrimitiveType.INT); + } + + return withLocation(new IntLiteralNode(-node.expression.value), node); + } + + visitBinaryOp(node: BinaryOpNode): Node { + if (!FOLDABLE_BINARY_OPERATORS.includes(node.operator)) throw new InvalidConstantExpressionError(node); + + // The folded operands are written back so the type errors below report the resolved operand types + node.left = this.visitExpression(node.left); + node.right = this.visitExpression(node.right); + + if (node.operator === BinaryOperator.PLUS) return foldPlus(node); + return foldIntArithmetic(node); + } +} + +const FOLDABLE_UNARY_OPERATORS = [ + UnaryOperator.NEGATE, +]; + +const FOLDABLE_BINARY_OPERATORS = [ + BinaryOperator.PLUS, + BinaryOperator.MINUS, + BinaryOperator.MUL, + BinaryOperator.DIV, + BinaryOperator.MOD, +]; + +function foldPlus(node: BinaryOpNode): LiteralNode { + const { left, right } = node; + + if (left instanceof IntLiteralNode && right instanceof IntLiteralNode) { + return withLocation(new IntLiteralNode(left.value + right.value), node); + } + + if (left instanceof StringLiteralNode && right instanceof StringLiteralNode) { + return withLocation(new StringLiteralNode(left.value + right.value, left.quote), node); + } + + if (left instanceof HexLiteralNode && right instanceof HexLiteralNode) { + return withLocation(new HexLiteralNode(new Uint8Array([...left.value, ...right.value])), node); + } + + throw typeMismatchError(node, PrimitiveType.INT); +} + +function foldIntArithmetic(node: BinaryOpNode): LiteralNode { + const { left, right, operator } = node; + + if (!(left instanceof IntLiteralNode) || !(right instanceof IntLiteralNode)) { + throw typeMismatchError(node, PrimitiveType.INT); + } + + if ((operator === BinaryOperator.DIV || operator === BinaryOperator.MOD) && right.value === 0n) { + throw new DivisionByZeroError(node); + } + + switch (operator) { + case BinaryOperator.MINUS: return withLocation(new IntLiteralNode(left.value - right.value), node); + case BinaryOperator.MUL: return withLocation(new IntLiteralNode(left.value * right.value), node); + // Note: BigInt division and modulo truncate towards zero, matching OP_DIV / OP_MOD semantics + case BinaryOperator.DIV: return withLocation(new IntLiteralNode(left.value / right.value), node); + case BinaryOperator.MOD: return withLocation(new IntLiteralNode(left.value % right.value), node); + default: throw new InvalidConstantExpressionError(node); + } +} + +function typeMismatchError(node: BinaryOpNode, expected: PrimitiveType): CashScriptError { + const resultingType = resultingTypeForBinaryOp(node.operator, node.left.type!, node.right.type!); + if (resultingType) return new UnsupportedTypeError(node, resultingType, expected); + return new UnequalTypeError(node); +} + +function withLocation(literal: T, source: Node): T { + literal.location = source.location; + return literal; +} diff --git a/packages/cashc/src/semantic/InjectLocktimeGuardTraversal.ts b/packages/cashc/src/semantic/InjectLocktimeGuardTraversal.ts index 1aa0983f4..6137d6e3c 100644 --- a/packages/cashc/src/semantic/InjectLocktimeGuardTraversal.ts +++ b/packages/cashc/src/semantic/InjectLocktimeGuardTraversal.ts @@ -1,10 +1,13 @@ import { PrimitiveType } from '@cashscript/utils'; import { BlockNode, + ContractNode, + FunctionCallNode, FunctionDefinitionNode, IntLiteralNode, Node, NullaryOpNode, + SourceFileNode, TimeOpNode, } from '../ast/AST.js'; import AstTraversal from '../ast/AstTraversal.js'; @@ -12,45 +15,90 @@ import { TimeOp } from '../ast/Globals.js'; import { Location } from '../ast/Location.js'; import { NullaryOperator } from '../ast/Operator.js'; -// Per BCH consensus, `tx.locktime` is only protocol-enforced if at least one input has a non-final sequence number -// If a require(tx.time >= ...) check or a require(this.age >= ...) with a compile-time int literal below 2^31 is -// present, then `tx.locktime` is protocol-enforced. If no such check is present, then we add a -// synthetic require(tx.time >= tx.locktime) check. +// Per BCH consensus, `tx.locktime` is only protocol-enforced if at least one input has a non-final +// sequence number. A require(tx.time >= ...) check — or a require(this.age >= ...) with a compile-time +// int literal below 2^31 — forces that non-finality. When a spending path uses `tx.locktime` without +// such a check, we inject a synthetic require(tx.time >= tx.locktime) guard at the start of the function. export default class InjectLocktimeGuardTraversal extends AstTraversal { - private hasTimeCheckOnPath = false; - private functionNeedsGuard = false; + // Keep track of which global functions require a locktime guard when called. + private globalFunctionRequiresLocktimeGuard = new Map(); - visitFunctionDefinition(node: FunctionDefinitionNode): Node { - this.hasTimeCheckOnPath = false; - this.functionNeedsGuard = false; - - super.visitFunctionDefinition(node); + visitSourceFile(node: SourceFileNode): Node { + // Only contract spending functions are traversed (and guarded); globals are analysed on demand. + node.contract = this.visitOptional(node.contract) as ContractNode | undefined; + return node; + } - if (this.functionNeedsGuard) { + visitFunctionDefinition(node: FunctionDefinitionNode): Node { + if (this.requiresLocktimeGuard(node.body)) { node.body.statements = [createLocktimeGuard(node), ...(node.body.statements ?? [])]; } return node; } + private requiresLocktimeGuard(body: BlockNode): boolean { + const analyser = new LocktimeGuardRequirementAnalyser((func) => this.checkGlobalFunctionRequiresLocktimeGuard(func)); + analyser.visit(body); + return analyser.requiresLocktimeGuard; + } + + // Memoised, cycle-safe analysis of a single global function. + private checkGlobalFunctionRequiresLocktimeGuard(func: FunctionDefinitionNode): boolean { + const memoised = this.globalFunctionRequiresLocktimeGuard.get(func); + if (memoised !== undefined) return memoised; + + this.globalFunctionRequiresLocktimeGuard.set(func, false); // seed: a re-entrant (cyclic) call contributes nothing + const requiresLocktimeGuard = this.requiresLocktimeGuard(func.body); + this.globalFunctionRequiresLocktimeGuard.set(func, requiresLocktimeGuard); + return requiresLocktimeGuard; + } +} + +class LocktimeGuardRequirementAnalyser extends AstTraversal { + requiresLocktimeGuard = false; + private isAlreadyCovered = false; + + constructor( + private checkGlobalFunctionRequiresLocktimeGuard: (func: FunctionDefinitionNode) => boolean, + ) { + super(); + } + visitBlock(node: BlockNode): Node { - const previous = this.hasTimeCheckOnPath; + const enclosingIsAlreadyCovered = this.isAlreadyCovered; - // Check whether there are any locktime checks on the same execution path, BEFORE entering the block body. - // So even if there are locktime checks after the `tx.locktime` access, it counts as a locktime check + // A time check anywhere in this block covers the whole block (order within the block is irrelevant); + // a sibling branch's check does not, since each branch body is its own block. if (node.statements?.some(isLocktimeCheck)) { - this.hasTimeCheckOnPath = true; + this.isAlreadyCovered = true; } + super.visitBlock(node); - this.hasTimeCheckOnPath = previous; + + this.isAlreadyCovered = enclosingIsAlreadyCovered; return node; } visitNullaryOp(node: NullaryOpNode): Node { - if (node.operator === NullaryOperator.LOCKTIME && !this.hasTimeCheckOnPath) { - this.functionNeedsGuard = true; + if (node.operator === NullaryOperator.LOCKTIME && !this.isAlreadyCovered) { + this.requiresLocktimeGuard = true; } return node; } + + visitFunctionCall(node: FunctionCallNode): Node { + node = super.visitFunctionCall(node) as FunctionCallNode; + if (this.isAlreadyCovered) return node; + + const functionDefinition = node.identifier.symbol?.definition; + if (!functionDefinition || !(functionDefinition instanceof FunctionDefinitionNode)) return node; + + if (this.checkGlobalFunctionRequiresLocktimeGuard(functionDefinition)) { + this.requiresLocktimeGuard = true; + } + + return node; + } } // Note that `require(tx.time >= ...)` checks are always sufficient to enforce the non-finality of the spending input, diff --git a/packages/cashc/src/semantic/LowerGlobalConstantsTraversal.ts b/packages/cashc/src/semantic/LowerGlobalConstantsTraversal.ts new file mode 100644 index 000000000..7a38b0577 --- /dev/null +++ b/packages/cashc/src/semantic/LowerGlobalConstantsTraversal.ts @@ -0,0 +1,121 @@ +import { + BlockNode, + BoolLiteralNode, + ConsoleStatementNode, + ConstantDefinitionNode, + ContractNode, + ExpressionNode, + FunctionCallNode, + FunctionDefinitionNode, + FunctionKind, + HexLiteralNode, + IdentifierNode, + IntLiteralNode, + LiteralNode, + Node, + ReturnNode, + SourceFileNode, + StringLiteralNode, +} from '../ast/AST.js'; +import { Symbol, SymbolTable } from '../ast/SymbolTable.js'; +import AstTraversal from '../ast/AstTraversal.js'; + +// Lowers constants to synthetic zero-argument global functions, so they can share reachability analysis and +// VM function-ID assignment with user-defined functions. +export class LowerGlobalConstantsTraversal extends AstTraversal { + private symbolTable: SymbolTable; + + visitSourceFile(node: SourceFileNode): Node { + this.symbolTable = node.symbolTable!; + + // Every constant becomes a function definition (replacing the constant's symbol in the table) + const constantFunctions = node.constants.map((constant) => { + const definition = createConstantFunction(constant); + this.symbolTable.set(Symbol.userFunction(definition)); + return definition; + }); + + node.functions = this.visitList(node.functions) as FunctionDefinitionNode[]; + node.contract = this.visitOptional(node.contract) as ContractNode | undefined; + node.functions.push(...constantFunctions); + return node; + } + + // constant usage within console.log statements are exempt from the rewrite because we + // want to log the constant value at compile-time, not the function call at runtime + visitConsoleStatement(node: ConsoleStatementNode): Node { + return node; + } + + visitBoolLiteral(node: BoolLiteralNode): Node { + return this.lowerLiteral(node); + } + + visitIntLiteral(node: IntLiteralNode): Node { + return this.lowerLiteral(node); + } + + visitStringLiteral(node: StringLiteralNode): Node { + return this.lowerLiteral(node); + } + + visitHexLiteral(node: HexLiteralNode): Node { + return this.lowerLiteral(node); + } + + private lowerLiteral(node: LiteralNode): Node { + if (!node.constant) return node; + + const symbol = this.symbolTable.getFromThis(node.constant.name)!; + const identifier = new IdentifierNode(node.constant.name); + identifier.location = node.location; + identifier.type = node.type; + identifier.symbol = symbol; + + const call = new FunctionCallNode(identifier, []); + call.location = node.location; + call.type = node.type; + return call; + } +} + +// Create a synthetic FunctionDefinitionNode that represents a lowered constant +function createConstantFunction(constant: ConstantDefinitionNode): FunctionDefinitionNode { + const literal = cloneLiteral(constant.value); + literal.type = constant.type; + const returnStatement = new ReturnNode([literal]); + returnStatement.location = constant.location; + const body = new BlockNode([returnStatement]); + body.location = constant.location; + + const definition = new FunctionDefinitionNode(FunctionKind.GLOBAL, constant.name, [], body, [constant.type]); + definition.constant = constant; + definition.location = constant.location; + definition.sourceCode = constant.sourceCode; + definition.sourceFile = constant.sourceFile; + return definition; +} + +// Clone a constant's literal value, adopting the reference's location and the constant's declared type +export function cloneConstantValue(constant: ConstantDefinitionNode, reference: IdentifierNode): LiteralNode { + const literal = cloneLiteral(constant.value); + literal.location = reference.location; + literal.type = constant.type; + return literal; +} + +// Create a synthetic LiteralNode that represents a reference to a lowered constant function, +// so later passes can treat it as a literal +export function createConstantLiteral(constant: ConstantDefinitionNode, reference: IdentifierNode): LiteralNode { + const literal = cloneConstantValue(constant, reference); + literal.constant = constant; + return literal; +} + +// SymbolTableTraversal folds every constant's value to a literal before constants are lowered or referenced +function cloneLiteral(node: ExpressionNode): LiteralNode { + if (!(node instanceof LiteralNode)) throw new Error('Expected constant value to be folded to a literal'); // Shouldn't happen + const clone: LiteralNode = Object.assign(Object.create(Object.getPrototypeOf(node)), node); + if (clone instanceof HexLiteralNode) clone.value = clone.value.slice(); + return clone; +} diff --git a/packages/cashc/src/semantic/SymbolTableTraversal.ts b/packages/cashc/src/semantic/SymbolTableTraversal.ts index 8b980b497..b95bf2b07 100644 --- a/packages/cashc/src/semantic/SymbolTableTraversal.ts +++ b/packages/cashc/src/semantic/SymbolTableTraversal.ts @@ -1,9 +1,12 @@ import { GLOBAL_SYMBOL_TABLE, Modifier } from '../ast/Globals.js'; import { + SourceFileNode, ContractNode, ParameterNode, VariableDefinitionNode, FunctionDefinitionNode, + ConstantDefinitionNode, + FunctionKind, IdentifierNode, StatementNode, BlockNode, @@ -15,25 +18,52 @@ import { ConsoleStatementNode, ConsoleParameterNode, ForNode, + TupleAssignmentTarget, } from '../ast/AST.js'; import AstTraversal from '../ast/AstTraversal.js'; -import { SymbolTable, Symbol, SymbolType } from '../ast/SymbolTable.js'; import { - FunctionRedefinitionError, - VariableRedefinitionError, + SymbolTable, Symbol, SymbolType, ReferenceKind, +} from '../ast/SymbolTable.js'; +import { createConstantLiteral } from './LowerGlobalConstantsTraversal.js'; +import { + RedefinitionError, UndefinedReferenceError, - UnusedVariableError, InvalidSymbolTypeError, ConstantModificationError, + DuplicateTupleTargetError, + InvalidModifierError, } from '../Errors.js'; export default class SymbolTableTraversal extends AstTraversal { private symbolTables: SymbolTable[] = [GLOBAL_SYMBOL_TABLE]; - private functionNames: Map = new Map(); + private contractFunctionNames: Map = new Map(); private currentFunction: FunctionDefinitionNode; private expectedSymbolType: SymbolType = SymbolType.VARIABLE; private insideConsoleStatement: boolean = false; + visitSourceFile(node: SourceFileNode): Node { + const globalFunctionTable = new SymbolTable(this.symbolTables[0]); + + node.functions.forEach((functionNode) => { + if (globalFunctionTable.get(functionNode.name)) throw new RedefinitionError(functionNode, functionNode.name); + globalFunctionTable.set(Symbol.userFunction(functionNode)); + }); + + node.constants.forEach((constantNode) => { + if (globalFunctionTable.get(constantNode.name)) throw new RedefinitionError(constantNode, constantNode.name); + globalFunctionTable.set(Symbol.constant(constantNode)); + }); + + node.symbolTable = globalFunctionTable; + this.symbolTables.unshift(globalFunctionTable); + + node.functions = this.visitList(node.functions) as FunctionDefinitionNode[]; + node.contract = this.visitOptional(node.contract) as ContractNode | undefined; + + this.symbolTables.shift(); + return node; + } + visitContract(node: ContractNode): Node { node.symbolTable = new SymbolTable(this.symbolTables[0]); this.symbolTables.unshift(node.symbolTable); @@ -41,33 +71,31 @@ export default class SymbolTableTraversal extends AstTraversal { node.parameters = this.visitList(node.parameters) as ParameterNode[]; node.functions = this.visitList(node.functions) as FunctionDefinitionNode[]; - const unusedSymbols = node.symbolTable.unusedSymbols(); - if (unusedSymbols.length !== 0) { - throw new UnusedVariableError(unusedSymbols[0]); - } - this.symbolTables.shift(); return node; } visitParameter(node: ParameterNode): Node { if (this.symbolTables[0].get(node.name)) { - throw new VariableRedefinitionError(node); + throw new RedefinitionError(node, node.name); } - this.symbolTables[0].set(Symbol.variable(node)); + validateModifiers(node, node.modifiers, [Modifier.UNUSED]); + + node.symbol = Symbol.variable(node); + this.symbolTables[0].set(node.symbol); return node; } visitFunctionDefinition(node: FunctionDefinitionNode): Node { this.currentFunction = node; - // Checked for function redefinition, but they are not included in the - // symbol table, as internal function calls are not supported. - if (this.functionNames.get(node.name)) { - throw new FunctionRedefinitionError(node); + if (node.kind === FunctionKind.CONTRACT) { + if (this.contractFunctionNames.get(node.name)) { + throw new RedefinitionError(node, node.name); + } + this.contractFunctionNames.set(node.name, true); } - this.functionNames.set(node.name, true); node.symbolTable = new SymbolTable(this.symbolTables[0]); this.symbolTables.unshift(node.symbolTable); @@ -75,11 +103,6 @@ export default class SymbolTableTraversal extends AstTraversal { node.parameters = this.visitList(node.parameters) as ParameterNode[]; node.body = this.visit(node.body); - const unusedSymbols = node.symbolTable.unusedSymbols(); - if (unusedSymbols.length !== 0) { - throw new UnusedVariableError(unusedSymbols[0]); - } - this.symbolTables.shift(); return node; } @@ -90,11 +113,6 @@ export default class SymbolTableTraversal extends AstTraversal { node.statements = this.visitOptionalList(node.statements) as StatementNode[]; - const unusedSymbols = node.symbolTable.unusedSymbols(); - if (unusedSymbols.length !== 0) { - throw new UnusedVariableError(unusedSymbols[0]); - } - this.symbolTables.shift(); return node; } @@ -108,54 +126,63 @@ export default class SymbolTableTraversal extends AstTraversal { node.update = this.visit(node.update) as AssignNode; node.block = this.visit(node.block); - const unusedSymbols = node.symbolTable.unusedSymbols(); - if (unusedSymbols.length !== 0) { - throw new UnusedVariableError(unusedSymbols[0]); - } - this.symbolTables.shift(); return node; } visitVariableDefinition(node: VariableDefinitionNode): Node { if (this.symbolTables[0].get(node.name)) { - throw new VariableRedefinitionError(node); + throw new RedefinitionError(node, node.name); } + validateModifiers(node, node.modifiers, [Modifier.CONSTANT, Modifier.UNUSED]); + node.expression = this.visit(node.expression); - this.symbolTables[0].set(Symbol.variable(node)); + node.symbol = Symbol.variable(node); + this.symbolTables[0].set(node.symbol); return node; } visitAssign(node: AssignNode): Node { - const v = this.symbolTables[0].get(node.identifier.name)?.definition as VariableDefinitionNode; - // const used_modifiers = [] # PREVENT USER FROM USING SAME MODIFIER AGAIN - v?.modifier?.forEach((modifier) => { - if (modifier === Modifier.CONSTANT) { - throw new ConstantModificationError(v); - } - }); - - super.visitAssign(node); + node.identifier.symbol = this.resolveAssignmentTarget(node, node.identifier); + node.expression = this.visit(node.expression); + this.addReference(ReferenceKind.WRITE, node.identifier); return node; } visitTupleAssignment(node: TupleAssignmentNode): Node { - [node.left, node.right].forEach((variable) => { - const definition = createTupleVariableDefinition(node, variable); + const seenTargetNames = new Set(); + node.targets.forEach((target) => { + if (seenTargetNames.has(target.identifier.name)) { + throw new DuplicateTupleTargetError(node, target.identifier.name); + } + seenTargetNames.add(target.identifier.name); + + if (target.isReassignment) { + target.identifier.symbol = this.resolveAssignmentTarget(node, target.identifier); + target.type = target.identifier.symbol.type; + } else { + const definition = createTupleVariableDefinition(node, target); - const { name } = variable; - if (this.symbolTables[0].get(name)) { - throw new VariableRedefinitionError(definition); + if (this.symbolTables[0].get(target.identifier.name)) { + throw new RedefinitionError(definition, target.identifier.name); + } + + validateModifiers(definition, definition.modifiers, [Modifier.CONSTANT, Modifier.UNUSED]); + + target.identifier.symbol = Symbol.variable(definition); + this.symbolTables[0].set(target.identifier.symbol); } - this.symbolTables[0].set( - Symbol.variable(definition), - ); }); node.tuple = this.visit(node.tuple); + + node.targets + .filter((target) => target.isReassignment) + .forEach((target) => this.addReference(ReferenceKind.WRITE, target.identifier)); + return node; } @@ -185,17 +212,27 @@ export default class SymbolTableTraversal extends AstTraversal { } visitIdentifier(node: IdentifierNode): Node { - const definition = this.symbolTables[0].get(node.name); - if (!definition) { + const symbol = this.symbolTables[0].get(node.name); + if (!symbol) { throw new UndefinedReferenceError(node); } - if (definition.symbolType !== this.expectedSymbolType) { + if (symbol.symbolType !== this.expectedSymbolType) { throw new InvalidSymbolTypeError(node, this.expectedSymbolType); } - node.definition = definition; - node.definition.references.push(node); + if (symbol.hasModifier(Modifier.UNUSED)) { + throw new InvalidModifierError(node, `Cannot reference variable '${node.name}' because it is marked 'unused'`); + } + + // Global constant references are replaced by their literal value, so all later passes + // (type checking, literal-driven analysis, codegen) see a plain literal at the use site. + if (symbol.definition instanceof ConstantDefinitionNode) { + return createConstantLiteral(symbol.definition, node); + } + + node.symbol = symbol; + this.addReference(ReferenceKind.READ, node); // Keep track of final use of variables for code generation (excluding console statements) if (!this.insideConsoleStatement) { @@ -204,13 +241,64 @@ export default class SymbolTableTraversal extends AstTraversal { return node; } + + private addReference(kind: ReferenceKind, node: IdentifierNode): void { + node.symbol!.references.push({ kind, node }); + } + + // Assignment targets are resolved without counting as a use of the variable, since only reads count + private resolveAssignmentTarget(node: AssignNode | TupleAssignmentNode, identifier: IdentifierNode): Symbol { + const symbol = this.symbolTables[0].get(identifier.name); + + if (!symbol) { + throw new UndefinedReferenceError(identifier); + } + + if (symbol.hasModifier(Modifier.CONSTANT)) { + throw new ConstantModificationError(node, identifier.name); + } + + if (symbol.symbolType !== SymbolType.VARIABLE) { + throw new InvalidSymbolTypeError(identifier, SymbolType.VARIABLE); + } + + if (symbol.hasModifier(Modifier.UNUSED)) { + throw new InvalidModifierError(identifier, `Cannot assign to variable '${identifier.name}' because it is marked 'unused'`); + } + + // An assignment still needs the variable to be on the stack, so it does count as its final use for code generation + this.currentFunction.opRolls.set(identifier.name, identifier); + + return symbol; + } +} + +function validateModifiers( + node: ParameterNode | VariableDefinitionNode, + modifiers: Modifier[], + allowed: Modifier[], +): void { + const seen = new Set(); + + modifiers.forEach((modifier) => { + if (seen.has(modifier)) { + throw new InvalidModifierError(node, `Duplicate modifier '${modifier}'`); + } + + if (!allowed.includes(modifier)) { + const target = node instanceof ParameterNode ? 'parameters' : 'variables'; + throw new InvalidModifierError(node, `Modifier '${modifier}' is not allowed on ${target}`); + } + + seen.add(modifier); + }); } function createTupleVariableDefinition( node: TupleAssignmentNode, - variable: TupleAssignmentNode['left'], + target: TupleAssignmentTarget, ): VariableDefinitionNode { - const definition = new VariableDefinitionNode(variable.type, [], variable.name, node.tuple); + const definition = new VariableDefinitionNode(target.type!, target.modifiers, target.identifier.name, node.tuple); definition.location = node.location; return definition; } diff --git a/packages/cashc/src/semantic/TypeCheckTraversal.ts b/packages/cashc/src/semantic/TypeCheckTraversal.ts index 5e6742c97..b76a75db4 100644 --- a/packages/cashc/src/semantic/TypeCheckTraversal.ts +++ b/packages/cashc/src/semantic/TypeCheckTraversal.ts @@ -14,6 +14,11 @@ import { BranchNode, CastNode, FunctionCallNode, + FunctionCallStatementNode, + FunctionDefinitionNode, + ConstantDefinitionNode, + LiteralNode, + ParameterNode, UnaryOpNode, BinaryOpNode, IdentifierNode, @@ -22,6 +27,7 @@ import { ArrayNode, TupleIndexOpNode, RequireNode, + ReturnNode, Node, InstantiationNode, TupleAssignmentNode, @@ -45,15 +51,25 @@ import { AssignTypeError, ArrayElementError, IndexOutOfBoundsError, - TupleAssignmentError, BitshiftBitcountNegativeError, + UnusedFunctionReturnError, + ReturnTypeError, } from '../Errors.js'; import { BinaryOperator, NullaryOperator, UnaryOperator } from '../ast/Operator.js'; import { GlobalFunction } from '../ast/Globals.js'; import { Symbol } from '../ast/SymbolTable.js'; -import { resultingTypeForBinaryOp } from '../utils.js'; +import { functionReturnType, resultingTypeForBinaryOp } from '../utils.js'; export default class TypeCheckTraversal extends AstTraversal { + private currentFunctionReturnTypes: Type[] = []; + + visitConstantDefinition(node: ConstantDefinitionNode): Node { + // The constant's value has already been folded to a literal by SymbolTableTraversal + node.value = this.visit(node.value) as LiteralNode; + expectAssignable(node, node.value.type, node.type); + return node; + } + visitVariableDefinition(node: VariableDefinitionNode): Node { node.expression = this.visit(node.expression); expectAssignable(node, node.expression.type, node.type); @@ -62,17 +78,15 @@ export default class TypeCheckTraversal extends AstTraversal { visitTupleAssignment(node: TupleAssignmentNode): Node { node.tuple = this.visit(node.tuple); - if (!(node.tuple instanceof BinaryOpNode) || node.tuple.operator !== BinaryOperator.SPLIT) { - throw new TupleAssignmentError(node.tuple); - } - const assignmentType = new TupleType(node.left.type, node.right.type); - - if (!implicitlyCastable(node.tuple.type, assignmentType)) { - const syntheticAssignment = new VariableDefinitionNode(assignmentType, [], node.left.name, node.tuple); + const targetsType = new TupleType(node.targets.map((target) => target.type!)); + if (!implicitlyCastable(node.tuple.type, targetsType)) { + const targetNames = node.targets.map((target) => target.identifier.name).join(', '); + const syntheticAssignment = new VariableDefinitionNode(targetsType, [], targetNames, node.tuple); syntheticAssignment.location = node.location; throw new AssignTypeError(syntheticAssignment); } + return node; } @@ -178,15 +192,47 @@ export default class TypeCheckTraversal extends AstTraversal { return node; } + visitFunctionCallStatement(node: FunctionCallStatementNode): Node { + node.functionCall = this.visit(node.functionCall) as FunctionCallNode; + if (node.functionCall.type !== PrimitiveType.VOID) { + throw new UnusedFunctionReturnError(node.functionCall); + } + return node; + } + + visitFunctionDefinition(node: FunctionDefinitionNode): Node { + this.currentFunctionReturnTypes = node.returnTypes ?? []; + node.parameters = this.visitList(node.parameters) as ParameterNode[]; + node.body = this.visit(node.body) as BlockNode; + return node; + } + + visitReturn(node: ReturnNode): Node { + node.expressions = this.visitList(node.expressions); + + // Every returned expression must be a single value: forwarding a tuple (e.g. `return pair()` + // from another multi-return function) would otherwise slip through the wrapped comparison below. + node.expressions.forEach((expression) => expectSingleValue(expression, expression.type)); + + const actualType = functionReturnType(node.expressions.map((expression) => expression.type!)); + const expectedType = functionReturnType(this.currentFunctionReturnTypes); + + if (!implicitlyCastable(actualType, expectedType)) { + throw new ReturnTypeError(node, actualType, expectedType); + } + + return node; + } + visitFunctionCall(node: FunctionCallNode): Node { node.identifier = this.visit(node.identifier) as IdentifierNode; node.parameters = this.visitList(node.parameters); - const { definition, type } = node.identifier; - if (!definition || !definition.parameters) return node; // already checked in symbol table + const { symbol, type } = node.identifier; + if (!symbol || !symbol.parameters) return node; // already checked in symbol table const parameterTypes = node.parameters.map((p) => p.type!); - expectParameters(node, parameterTypes, definition.parameters); + expectParameters(node, parameterTypes, symbol.parameters); // Additional array length check for checkMultiSig if (node.identifier.name === GlobalFunction.CHECKMULTISIG) { @@ -211,11 +257,11 @@ export default class TypeCheckTraversal extends AstTraversal { node.identifier = this.visit(node.identifier) as IdentifierNode; node.parameters = this.visitList(node.parameters); - const { definition, type } = node.identifier; - if (!definition || !definition.parameters) return node; // already checked in symbol table + const { symbol, type } = node.identifier; + if (!symbol || !symbol.parameters) return node; // already checked in symbol table const parameterTypes = node.parameters.map((p) => p.type!); - expectParameters(node, parameterTypes, definition.parameters); + expectParameters(node, parameterTypes, symbol.parameters); node.type = type; return node; @@ -226,11 +272,18 @@ export default class TypeCheckTraversal extends AstTraversal { expectTuple(node, node.tuple.type); - if (node.index !== 0 && node.index !== 1) { + // Tuple indexing is only supported on .split() results. A multi-return function call leaves all + // of its values on the stack, so it must be destructured instead (codegen has no partial binding). + if (!(node.tuple instanceof BinaryOpNode) || node.tuple.operator !== BinaryOperator.SPLIT) { + expectSingleValue(node.tuple, node.tuple.type); + } + + const { elementTypes } = node.tuple.type as TupleType; + if (node.index < 0 || node.index >= elementTypes.length) { throw new IndexOutOfBoundsError(node); } - node.type = node.index === 0 ? (node.tuple.type as TupleType).leftType : (node.tuple.type as TupleType).rightType; + node.type = elementTypes[node.index]; return node; } @@ -283,6 +336,7 @@ export default class TypeCheckTraversal extends AstTraversal { return node; case BinaryOperator.EQ: case BinaryOperator.NE: + expectSingleValue(node.left, node.left.type); expectCompatibleBytesBounds(node, node.left.type, node.right.type); node.type = PrimitiveType.BOOL; return node; @@ -405,8 +459,8 @@ export default class TypeCheckTraversal extends AstTraversal { } visitIdentifier(node: IdentifierNode): Node { - if (!node.definition) return node; - node.type = node.definition.type; + if (!node.symbol) return node; + node.type = node.symbol.type; return node; } } @@ -452,12 +506,12 @@ function expectCompatibleBytesBounds(node: BinaryOpNode, left?: Type, right?: Ty function expectTuple(node: ExpectedNode, actual?: Type): void { if (!(actual instanceof TupleType)) { // We use a placeholder tuple to indicate that we're expecting *any* tuple at all - const placeholderTuple = new TupleType(new BytesType(), new BytesType()); + const placeholderTuple = new TupleType([new BytesType(), new BytesType()]); throw new UnsupportedTypeError(node, actual, placeholderTuple); } } -type AssigningNode = AssignNode | VariableDefinitionNode; +type AssigningNode = AssignNode | VariableDefinitionNode | ConstantDefinitionNode; function expectAssignable(node: AssigningNode, actual?: Type, expected?: Type): void { if (!implicitlyCastable(actual, expected)) { throw new AssignTypeError(node); @@ -479,7 +533,7 @@ function inferTupleType(node: BinaryOpNode): Type { // string.split() -> string, string if (node.left.type === PrimitiveType.STRING) { - return new TupleType(PrimitiveType.STRING, PrimitiveType.STRING); + return new TupleType([PrimitiveType.STRING, PrimitiveType.STRING]); } // If the expression is not a bytes type, then it must be a different compatible type (e.g. sig/pubkey) @@ -488,14 +542,14 @@ function inferTupleType(node: BinaryOpNode): Type { // bytes.split(variable) -> bytes, bytes if (!(node.right instanceof IntLiteralNode)) { - return new TupleType(new BytesType(), new BytesType()); + return new TupleType([new BytesType(), new BytesType()]); } const splitIndex = Number(node.right.value); // bytes.split(NumberLiteral) -> bytes(NumberLiteral), bytes if (expressionType.bound === undefined) { - return new TupleType(new BytesType(splitIndex), new BytesType()); + return new TupleType([new BytesType(splitIndex), new BytesType()]); } if (splitIndex > expressionType.bound) { @@ -503,10 +557,10 @@ function inferTupleType(node: BinaryOpNode): Type { } // bytesX.split(NumberLiteral) -> bytes(NumberLiteral), bytes(X - NumberLiteral) - return new TupleType( + return new TupleType([ new BytesType(splitIndex), new BytesType(expressionType.bound! - splitIndex), - ); + ]); } function inferSliceType(node: SliceNode): Type { @@ -599,13 +653,13 @@ function extractSingleBytesNarrowing(expr: BinaryOpNode): Narrowing | undefined const { sizeNode, literalNode } = match; if (!(sizeNode.expression instanceof IdentifierNode)) return undefined; - const { definition } = sizeNode.expression; - if (!definition || !(definition.type instanceof BytesType) || definition.type.bound !== undefined) return undefined; + const { symbol } = sizeNode.expression; + if (!symbol || !(symbol.type instanceof BytesType) || symbol.type.bound !== undefined) return undefined; const bound = Number(literalNode.value); if (bound <= 0) return undefined; - return { symbol: definition, bound }; + return { symbol, bound }; } // Matches expr.length N or N expr.length, returning the SIZE node and int literal. @@ -624,3 +678,9 @@ function matchSizeLiteral(expr: BinaryOpNode): { sizeNode: UnaryOpNode, literalN const isSizeOp = (node: ExpressionNode): node is UnaryOpNode => ( node instanceof UnaryOpNode && node.operator === UnaryOperator.SIZE ); + +function expectSingleValue(node: Node, type?: Type): void { + if (type instanceof TupleType) { + throw new TypeError(node, type, undefined, `Found tuple '${type}' where a single value was expected`); + } +} diff --git a/packages/cashc/src/semantic/UnusedCodeTraversal.ts b/packages/cashc/src/semantic/UnusedCodeTraversal.ts new file mode 100644 index 000000000..32276d932 --- /dev/null +++ b/packages/cashc/src/semantic/UnusedCodeTraversal.ts @@ -0,0 +1,129 @@ +import { + AssignNode, + BlockNode, + ContractNode, + DoWhileNode, + ForNode, + FunctionDefinitionNode, + IdentifierNode, + Node, + TupleAssignmentNode, + WhileNode, +} from '../ast/AST.js'; +import AstTraversal from '../ast/AstTraversal.js'; +import { Symbol, SymbolTable } from '../ast/SymbolTable.js'; +import { CashScriptWarningListener, UnusedAssignmentWarning, UnusedVariableWarning } from '../Warnings.js'; + +export default class UnusedCodeWarningsTraversal extends AstTraversal { + private pendingWrites: Map> = new Map>(); + private readWrites: Set = new Set(); + private reportedScopes: Set = new Set(); + + constructor(private warningListener: CashScriptWarningListener) { + super(); + } + + visitContract(node: ContractNode): Node { + super.visitContract(node); + this.collectUnusedVariableWarnings(node.symbolTable!); + return node; + } + + visitFunctionDefinition(node: FunctionDefinitionNode): Node { + super.visitFunctionDefinition(node); + this.collectUnusedVariableWarnings(node.symbolTable!); + this.collectUnusedAssignmentWarnings(); + return node; + } + + visitBlock(node: BlockNode): Node { + super.visitBlock(node); + this.collectUnusedVariableWarnings(node.symbolTable!); + return node; + } + + visitFor(node: ForNode): Node { + this.visit(node.init); + this.visitLoop(() => { + this.visit(node.condition); + this.visit(node.block); + this.visit(node.update); + }); + this.collectUnusedVariableWarnings(node.symbolTable!); + return node; + } + + visitWhile(node: WhileNode): Node { + this.visitLoop(() => { + this.visit(node.condition); + this.visit(node.block); + }); + return node; + } + + visitDoWhile(node: DoWhileNode): Node { + this.visitLoop(() => { + this.visit(node.block); + this.visit(node.condition); + }); + return node; + } + + // Loops are visited twice (in execution order), so that a read at the start of the loop is seen to follow + // a write later in the loop, as it does in the next iteration + private visitLoop(visitIteration: () => void): void { + visitIteration(); + visitIteration(); + } + + visitAssign(node: AssignNode): Node { + // The expression is visited before the write is recorded, so that reads inside the expression + // (e.g. x = x + 1) do not count as reading the assigned value + this.visit(node.expression); + this.recordWrite(node.identifier); + return node; + } + + visitTupleAssignment(node: TupleAssignmentNode): Node { + this.visit(node.tuple); + node.targets + .filter((target) => target.isReassignment) + .forEach((target) => this.recordWrite(target.identifier)); + return node; + } + + visitIdentifier(node: IdentifierNode): Node { + this.pendingWrites.get(node.symbol!)?.forEach((write) => this.readWrites.add(write)); + this.pendingWrites.delete(node.symbol!); + return node; + } + + private recordWrite(identifier: IdentifierNode): void { + if (this.readWrites.has(identifier)) return; + + const symbol = identifier.symbol!; + const pendingWrites = this.pendingWrites.get(symbol) ?? new Set(); + pendingWrites.add(identifier); + this.pendingWrites.set(symbol, pendingWrites); + } + + // Each scope is reported once, even though scopes inside loops are visited twice + private collectUnusedVariableWarnings(symbolTable: SymbolTable): void { + if (this.reportedScopes.has(symbolTable)) return; + this.reportedScopes.add(symbolTable); + + symbolTable.getUnmarkedUnusedSymbols().forEach((symbol) => this.warningListener(new UnusedVariableWarning(symbol))); + } + + // At the end of a function, every read that could follow its assignments has been visited + private collectUnusedAssignmentWarnings(): void { + this.pendingWrites.forEach((writes, symbol) => { + // Writes to a variable that is never read at all are covered by its unused variable warning + if (symbol.isUnused()) return; + writes.forEach((write) => this.warningListener(new UnusedAssignmentWarning(write))); + }); + + this.pendingWrites.clear(); + this.readWrites.clear(); + } +} diff --git a/packages/cashc/src/utils.ts b/packages/cashc/src/utils.ts index c24a140db..6c46e1c85 100644 --- a/packages/cashc/src/utils.ts +++ b/packages/cashc/src/utils.ts @@ -1,6 +1,12 @@ -import { BytesType, implicitlyCastable, PrimitiveType, Type } from '@cashscript/utils'; +import { BytesType, implicitlyCastable, PrimitiveType, TupleType, Type } from '@cashscript/utils'; import { BinaryOperator } from './ast/Operator.js'; +export function functionReturnType(returnTypes?: Type[]): Type { + if (returnTypes === undefined || returnTypes.length === 0) return PrimitiveType.VOID; + if (returnTypes.length === 1) return returnTypes[0]; + return new TupleType(returnTypes); +} + export function resultingTypeForBinaryOp( operator: BinaryOperator, left: Type, diff --git a/packages/cashc/test/ast/AST.test.ts b/packages/cashc/test/ast/AST.test.ts index 7a7c0076e..a9d0deb11 100644 --- a/packages/cashc/test/ast/AST.test.ts +++ b/packages/cashc/test/ast/AST.test.ts @@ -12,7 +12,7 @@ import fs from 'fs'; import { URL } from 'url'; import { fixtures } from './fixtures.js'; -import { parseCode } from '../../src/compiler.js'; +import { parseCode } from '../../src/parser.js'; import { readCashFiles } from '../test-utils.js'; import { Ast } from '../../src/ast/AST.js'; import OutputSourceCodeTraversal from '../../src/print/OutputSourceCodeTraversal.js'; diff --git a/packages/cashc/test/ast/Location.test.ts b/packages/cashc/test/ast/Location.test.ts index 4f7d94187..ebae658fa 100644 --- a/packages/cashc/test/ast/Location.test.ts +++ b/packages/cashc/test/ast/Location.test.ts @@ -1,90 +1,23 @@ import fs from 'fs'; import { URL } from 'url'; -import { compileString, parseCode } from '../../src/compiler.js'; -import { buildLineToAsmMap, bytecodeToAsm, bytecodeToScript } from '@cashscript/utils'; -import { hexToBin } from '@bitauth/libauth'; +import { parseCode } from '../../src/parser.js'; describe('Location', () => { it('should retrieve correct text from location', () => { const code = fs.readFileSync(new URL('../valid-contract-files/simple_functions.cash', import.meta.url), { encoding: 'utf-8' }); const ast = parseCode(code); - const f = ast.contract.functions[0]; + const f = ast.contract!.functions[0]; expect(f.location).toBeDefined(); expect((f.location).text(code)).toEqual('function hello(sig s, pubkey pk) {\n require(checkSig(s, pk));\n }'); }); - const wrap = (code: string): string => { - return ` -contract test() { - function test() { - require(${code}); - } -}`; - }; - - describe('Line to ASM map generation', () => { - const blocks = [ - '1 < 1', '1 <= 1', '1 == 1', '1 != 1', '1 > 1', '1 >= 1', - '(1 - 1) == 1', '(1 + 1) == 1', '(1 * 1) == 1', '(1 / 1) == 1', - '(true && true) == true', '(true || true) == true', - '(0x01 & 0x01) == 0x01', '(0x01 | 0x01) == 0x01', '(0x01 ^ 0x01) == 0x01', - '"1" + "1" == "1"', '"1" + "1" != "1"', '"11".split(1)[0] == "1"', '"11".split(1)[1] == "1"', - '"1".reverse() == "1"', '"1".length == 1', '0x01.length == 1', '-333 == 1', - 'tx.inputs[0].tokenAmount == 1', - 'this.activeInputIndex == 1', 'tx.version == 1', - 'abs(-1) == 1', 'within(1,1,1) == true', 'bytes(sha256(1)) == bytes(0x01)', - 'checkSig(sig(0x00), pubkey(0x00))', 'checkMultiSig([sig(0x00), sig(0x00)], [pubkey(0x00), pubkey(0x00)])', - 'checkDataSig(datasig(0x00), 0x00, pubkey(0x00))', - 'tx.time >= 1', 'this.age >= 1', - 'bytes(1) == 0x01', 'int(0x01) == 1', - ]; - - blocks.forEach(block => { - it(`it should generate the same bytecode using line-to-asm map as the regular compiler for: ${block}`, () => { - { - const source = wrap(block); - - // Compile the source code using regular CashScript compilation - const artifact = compileString(source); - const expected = bytecodeToAsm(hexToBin(artifact.debug!.bytecode)); - - // Generate the line-to-asm map from the source code - const opCodeMap = buildLineToAsmMap( - bytecodeToScript(hexToBin(artifact.debug!.bytecode)), artifact.debug!.sourceMap, - ); - - // Convert the line-to-asm map to CashScript bytecode to make sure that the generated opcode map matches the - // bytecode generated by CashScript - const received = Object.values(opCodeMap).join(' ') - .replaceAll('<0x', '').replaceAll('>', '').replace(/\s+/g, ' '); - expect(received).toBe(expected); - } - - // Repeat the tests with the source code modified to test the position hint functionality - { - const source = wrap(block.replaceAll(' ', '\n').replaceAll(')', '\n)')) - .replaceAll('(\n)', '()').replace(/\((?!\))/g, '(\n'); - const artifact = compileString(source); - const expected = bytecodeToAsm(hexToBin(artifact.debug!.bytecode)); - const opCodeMap = buildLineToAsmMap( - bytecodeToScript(hexToBin(artifact.debug!.bytecode)), artifact.debug!.sourceMap, - ); - - const received = Object.values(opCodeMap).join(' ') - .replaceAll('<0x', '').replaceAll('>', '').replace(/\s+/g, ' '); - expect(received).toBe(expected); - } - }); - }); - }); - it('should set the correct location points', () => { const code = fs.readFileSync(new URL('../valid-contract-files/simple_functions.cash', import.meta.url), { encoding: 'utf-8' }); const ast = parseCode(code); - const secondFunction = ast.contract.functions[1]; + const secondFunction = ast.contract!.functions[1]; expect(secondFunction.location).toBeDefined(); expect(secondFunction.location.start).toEqual({ line: 6, column: 4 }); diff --git a/packages/cashc/test/ast/fixtures.ts b/packages/cashc/test/ast/fixtures.ts index 12cc323cd..e31b273cd 100644 --- a/packages/cashc/test/ast/fixtures.ts +++ b/packages/cashc/test/ast/fixtures.ts @@ -6,6 +6,7 @@ import { Ast, ParameterNode, FunctionDefinitionNode, + FunctionKind, BlockNode, RequireNode, BinaryOpNode, @@ -43,12 +44,13 @@ export const fixtures: Fixture[] = [ ast: new SourceFileNode( new ContractNode( 'P2PKH', - [new ParameterNode(new BytesType(20), 'pkh')], + [new ParameterNode(new BytesType(20), [], 'pkh')], [new FunctionDefinitionNode( + FunctionKind.CONTRACT, 'spend', [ - new ParameterNode(PrimitiveType.PUBKEY, 'pk'), - new ParameterNode(PrimitiveType.SIG, 's'), + new ParameterNode(PrimitiveType.PUBKEY, [], 'pk'), + new ParameterNode(PrimitiveType.SIG, [], 's'), ], new BlockNode([ new RequireNode( @@ -74,10 +76,11 @@ export const fixtures: Fixture[] = [ ast: new SourceFileNode( new ContractNode( 'Reassignment', - [new ParameterNode(PrimitiveType.INT, 'x'), new ParameterNode(PrimitiveType.STRING, 'y')], + [new ParameterNode(PrimitiveType.INT, [], 'x'), new ParameterNode(PrimitiveType.STRING, [], 'y')], [new FunctionDefinitionNode( + FunctionKind.CONTRACT, 'hello', - [new ParameterNode(PrimitiveType.PUBKEY, 'pk'), new ParameterNode(PrimitiveType.SIG, 's')], + [new ParameterNode(PrimitiveType.PUBKEY, [], 'pk'), new ParameterNode(PrimitiveType.SIG, [], 's')], new BlockNode([ new VariableDefinitionNode( PrimitiveType.INT, @@ -147,11 +150,12 @@ export const fixtures: Fixture[] = [ ast: new SourceFileNode( new ContractNode( 'MultiFunctionIfStatements', - [new ParameterNode(PrimitiveType.INT, 'x'), new ParameterNode(PrimitiveType.INT, 'y')], + [new ParameterNode(PrimitiveType.INT, [], 'x'), new ParameterNode(PrimitiveType.INT, [], 'y')], [ new FunctionDefinitionNode( + FunctionKind.CONTRACT, 'transfer', - [new ParameterNode(PrimitiveType.INT, 'a'), new ParameterNode(PrimitiveType.INT, 'b')], + [new ParameterNode(PrimitiveType.INT, [], 'a'), new ParameterNode(PrimitiveType.INT, [], 'b')], new BlockNode([ new VariableDefinitionNode( PrimitiveType.INT, @@ -233,8 +237,9 @@ export const fixtures: Fixture[] = [ ]), ), new FunctionDefinitionNode( + FunctionKind.CONTRACT, 'timeout', - [new ParameterNode(PrimitiveType.INT, 'b')], + [new ParameterNode(PrimitiveType.INT, [], 'b')], new BlockNode([ new VariableDefinitionNode( PrimitiveType.INT, @@ -307,15 +312,16 @@ export const fixtures: Fixture[] = [ new ContractNode( 'MultiSig', [ - new ParameterNode(PrimitiveType.PUBKEY, 'pk1'), - new ParameterNode(PrimitiveType.PUBKEY, 'pk2'), - new ParameterNode(PrimitiveType.PUBKEY, 'pk3'), + new ParameterNode(PrimitiveType.PUBKEY, [], 'pk1'), + new ParameterNode(PrimitiveType.PUBKEY, [], 'pk2'), + new ParameterNode(PrimitiveType.PUBKEY, [], 'pk3'), ], [new FunctionDefinitionNode( + FunctionKind.CONTRACT, 'spend', [ - new ParameterNode(PrimitiveType.SIG, 's1'), - new ParameterNode(PrimitiveType.SIG, 's2'), + new ParameterNode(PrimitiveType.SIG, [], 's1'), + new ParameterNode(PrimitiveType.SIG, [], 's2'), ], new BlockNode([ new RequireNode( @@ -345,22 +351,25 @@ export const fixtures: Fixture[] = [ new ContractNode( 'HodlVault', [ - new ParameterNode(PrimitiveType.PUBKEY, 'ownerPk'), - new ParameterNode(PrimitiveType.PUBKEY, 'oraclePk'), - new ParameterNode(PrimitiveType.INT, 'minBlock'), - new ParameterNode(PrimitiveType.INT, 'priceTarget'), + new ParameterNode(PrimitiveType.PUBKEY, [], 'ownerPk'), + new ParameterNode(PrimitiveType.PUBKEY, [], 'oraclePk'), + new ParameterNode(PrimitiveType.INT, [], 'minBlock'), + new ParameterNode(PrimitiveType.INT, [], 'priceTarget'), ], [new FunctionDefinitionNode( + FunctionKind.CONTRACT, 'spend', [ - new ParameterNode(PrimitiveType.SIG, 'ownerSig'), - new ParameterNode(PrimitiveType.DATASIG, 'oracleSig'), - new ParameterNode(new BytesType(8), 'oracleMessage'), + new ParameterNode(PrimitiveType.SIG, [], 'ownerSig'), + new ParameterNode(PrimitiveType.DATASIG, [], 'oracleSig'), + new ParameterNode(new BytesType(8), [], 'oracleMessage'), ], new BlockNode([ new TupleAssignmentNode( - { name: 'blockHeightBin', type: new BytesType(4) }, - { name: 'priceBin', type: new BytesType(4) }, + [ + { identifier: new IdentifierNode('blockHeightBin'), type: new BytesType(4), modifiers: [] }, + { identifier: new IdentifierNode('priceBin'), type: new BytesType(4), modifiers: [] }, + ], new BinaryOpNode( new IdentifierNode('oracleMessage'), BinaryOperator.SPLIT, @@ -436,6 +445,7 @@ export const fixtures: Fixture[] = [ 'Covenant', [], [new FunctionDefinitionNode( + FunctionKind.CONTRACT, 'spend', [], new BlockNode([ @@ -644,13 +654,14 @@ export const fixtures: Fixture[] = [ new ContractNode( 'Mecenas', [ - new ParameterNode(new BytesType(20), 'recipient'), - new ParameterNode(new BytesType(20), 'funder'), - new ParameterNode(PrimitiveType.INT, 'pledge'), - new ParameterNode(PrimitiveType.INT, 'period'), + new ParameterNode(new BytesType(20), [], 'recipient'), + new ParameterNode(new BytesType(20), [], 'funder'), + new ParameterNode(PrimitiveType.INT, [], 'pledge'), + new ParameterNode(PrimitiveType.INT, [], 'period'), ], [ new FunctionDefinitionNode( + FunctionKind.CONTRACT, 'receive', [], new BlockNode([ @@ -772,10 +783,11 @@ export const fixtures: Fixture[] = [ ]), ), new FunctionDefinitionNode( + FunctionKind.CONTRACT, 'reclaim', [ - new ParameterNode(PrimitiveType.PUBKEY, 'pk'), - new ParameterNode(PrimitiveType.SIG, 's'), + new ParameterNode(PrimitiveType.PUBKEY, [], 'pk'), + new ParameterNode(PrimitiveType.SIG, [], 's'), ], new BlockNode([ new RequireNode( @@ -810,6 +822,7 @@ export const fixtures: Fixture[] = [ 'Announcement', [], [new FunctionDefinitionNode( + FunctionKind.CONTRACT, 'announce', [], new BlockNode([ @@ -903,6 +916,10 @@ export const fixtures: Fixture[] = [ ]), )], ), + [], + [], + [], + ['>=0.8.0'], ), }, { @@ -913,8 +930,9 @@ export const fixtures: Fixture[] = [ [], [ new FunctionDefinitionNode( + FunctionKind.CONTRACT, 'spend', - [new ParameterNode(PrimitiveType.INT, 'value')], + [new ParameterNode(PrimitiveType.INT, [], 'value')], new BlockNode([ new RequireNode( new BinaryOpNode( @@ -957,6 +975,7 @@ export const fixtures: Fixture[] = [ 'Loopy', [], [new FunctionDefinitionNode( + FunctionKind.CONTRACT, 'doLoop', [], new BlockNode([ @@ -1005,6 +1024,7 @@ export const fixtures: Fixture[] = [ 'WhileLoopBasic', [], [new FunctionDefinitionNode( + FunctionKind.CONTRACT, 'spend', [], new BlockNode([ @@ -1050,6 +1070,7 @@ export const fixtures: Fixture[] = [ 'ForLoopBasic', [], [new FunctionDefinitionNode( + FunctionKind.CONTRACT, 'spend', [], new BlockNode([ diff --git a/packages/cashc/test/cashproof/0.1.2=0.2.0.equiv b/packages/cashc/test/cashproof/0.1.2=0.2.0.equiv deleted file mode 100644 index 99d2bf7f8..000000000 --- a/packages/cashc/test/cashproof/0.1.2=0.2.0.equiv +++ /dev/null @@ -1,111 +0,0 @@ -# This file includes several examples CashScript contracts as compiled with 0.1.2 and 0.2.0 -# to verify their equivalence. These Scripts contain the exact output as compiled by Cashc, -# except for the following changes: -# * "Default case" has been added to the 0.1.2 bytecode, as this was a vulnerability fix in 0.2.0 -# but we are only interested in the optimisation equivalence, so we simulate this fix for 0.1.2 -# * Final OP_1/0 has been changed to OP_TRUE/FALSE to play nice with CashProof type checking -# * Raw data has been prepended with 0x or changed to integer representation -# (CashScript hex-encodes numbers, CashProof expects decimal numbers, -# and assumes hex-encoded data is of the raw bytes type). - -!full_script=True; - -# P2PKH -# 12 opcount, 17 bytes -OP_1 OP_PICK OP_HASH160 OP_1 OP_PICK OP_EQUAL OP_VERIFY OP_2 OP_PICK OP_2 OP_PICK OP_CHECKSIG OP_VERIFY OP_DROP OP_DROP OP_DROP OP_TRUE -<=> -# 4 opcount, 4 bytes -OP_OVER OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG -# optimisation: opcount ~67%, bytes ~76% -; - -# TransferWithTimeout -# 31 opcount, 43 bytes -OP_3 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_4 OP_PICK OP_2 OP_PICK OP_CHECKSIG OP_VERIFY OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_TRUE OP_ELSE OP_3 OP_PICK OP_1 OP_NUMEQUAL OP_IF OP_4 OP_PICK OP_1 OP_PICK OP_CHECKSIG OP_VERIFY OP_2 OP_PICK OP_CHECKLOCKTIMEVERIFY OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_TRUE OP_ELSE OP_FALSE OP_ENDIF OP_ENDIF -<=> -# 23 opcount, 31 bytes -OP_3 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_4 OP_ROLL OP_ROT OP_CHECKSIG OP_NIP OP_NIP OP_NIP OP_ELSE OP_3 OP_ROLL OP_1 OP_NUMEQUAL OP_IF OP_3 OP_ROLL OP_SWAP OP_CHECKSIGVERIFY OP_SWAP OP_CHECKLOCKTIMEVERIFY OP_DROP OP_TRUE OP_NIP OP_ELSE OP_FALSE OP_ENDIF OP_ENDIF -# optimisation: opcount ~26%, bytes ~28% -; - -# HodlVault -# 37 opcount, 52 bytes -OP_6 OP_PICK OP_4 OP_SPLIT OP_DROP OP_BIN2NUM OP_7 OP_PICK OP_4 OP_SPLIT OP_NIP OP_BIN2NUM OP_1 OP_PICK OP_5 OP_PICK OP_GREATERTHANOREQUAL OP_VERIFY OP_1 OP_PICK OP_CHECKLOCKTIMEVERIFY OP_DROP OP_0 OP_PICK OP_6 OP_PICK OP_GREATERTHANOREQUAL OP_VERIFY OP_7 OP_PICK OP_9 OP_PICK OP_5 OP_PICK OP_CHECKDATASIG OP_VERIFY OP_6 OP_PICK OP_3 OP_PICK OP_CHECKSIG OP_VERIFY OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_TRUE -<=> -# 23 opcount, 32 bytes -OP_6 OP_PICK OP_4 OP_SPLIT OP_DROP OP_BIN2NUM OP_7 OP_PICK OP_4 OP_SPLIT OP_NIP OP_BIN2NUM OP_OVER OP_5 OP_ROLL OP_GREATERTHANOREQUAL OP_VERIFY OP_SWAP OP_CHECKLOCKTIMEVERIFY OP_DROP OP_3 OP_ROLL OP_GREATERTHANOREQUAL OP_VERIFY OP_3 OP_ROLL OP_4 OP_ROLL OP_3 OP_ROLL OP_CHECKDATASIGVERIFY OP_CHECKSIG -# optimisation: opcount ~38%, bytes ~38% -; - -# # Doesn't work yet -- CashProof doesn't fully support CMS yet -# # 2 of 3 Multisig -# # 12 opcount, 21 bytes -# OP_0 OP_3 OP_PICK OP_5 OP_PICK OP_2 OP_3 OP_PICK OP_5 OP_PICK OP_7 OP_PICK OP_3 OP_CHECKMULTISIG OP_VERIFY OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_TRUE -# <=> -# # 6 opcount, 12 bytes -# OP_0 OP_3 OP_ROLL OP_4 OP_ROLL OP_2 OP_3 OP_ROLL OP_2ROT OP_SWAP OP_3 OP_CHECKMULTISIG -# optimisation: opcount 50%, bytes ~43% -# ; - -# # Does prove, but this is very slow, so it's commented out -# # Zero-conf forfeits -# # 60 opcount, 83 bytes -# OP_2 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_3 OP_PICK OP_HASH160 OP_2 OP_PICK OP_EQUAL OP_VERIFY OP_4 OP_PICK OP_4 OP_PICK OP_CHECKSIG OP_VERIFY OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_TRUE OP_ELSE OP_2 OP_PICK OP_1 OP_NUMEQUAL OP_IF OP_7 OP_PICK OP_HASH160 OP_1 OP_PICK OP_EQUAL OP_VERIFY OP_4 OP_PICK OP_7 OP_PICK OP_EQUAL OP_NOT OP_VERIFY OP_3 OP_PICK OP_SIZE OP_1 OP_SUB OP_SPLIT OP_DROP OP_5 OP_PICK OP_9 OP_PICK OP_CHECKDATASIG OP_VERIFY OP_5 OP_PICK OP_SIZE OP_1 OP_SUB OP_SPLIT OP_DROP OP_7 OP_PICK OP_9 OP_PICK OP_CHECKDATASIG OP_VERIFY OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_TRUE OP_ELSE OP_FALSE OP_ENDIF OP_ENDIF -# <=> -# # 43 opcount, 52 bytes -# OP_2 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_3 OP_PICK OP_HASH160 OP_ROT OP_EQUALVERIFY OP_2SWAP OP_CHECKSIG OP_NIP OP_NIP OP_ELSE OP_ROT OP_1 OP_NUMEQUAL OP_IF OP_6 OP_PICK OP_HASH160 OP_EQUALVERIFY OP_2 OP_PICK OP_5 OP_PICK OP_EQUAL OP_NOT OP_VERIFY OP_SWAP OP_SIZE OP_1SUB OP_SPLIT OP_DROP OP_ROT OP_5 OP_PICK OP_CHECKDATASIGVERIFY OP_SWAP OP_SIZE OP_1SUB OP_SPLIT OP_DROP OP_2SWAP OP_SWAP OP_CHECKDATASIG OP_NIP OP_ELSE OP_FALSE OP_ENDIF OP_ENDIF -# optimisation: opcount ~28%, bytes ~37% -# ; - -# HTLC -# 39 opcount, 53 bytes -OP_4 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_5 OP_PICK OP_SHA256 OP_4 OP_PICK OP_EQUAL OP_VERIFY OP_6 OP_PICK OP_2 OP_PICK OP_CHECKSIG OP_VERIFY OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_TRUE OP_ELSE OP_4 OP_PICK OP_1 OP_NUMEQUAL OP_IF OP_2 OP_PICK OP_CHECKLOCKTIMEVERIFY OP_DROP OP_5 OP_PICK OP_1 OP_PICK OP_CHECKSIG OP_VERIFY OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_TRUE OP_ELSE OP_FALSE OP_ENDIF OP_ENDIF -<=> -# 28 opcount, 37 bytes -OP_4 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_5 OP_ROLL OP_SHA256 OP_4 OP_ROLL OP_EQUALVERIFY OP_4 OP_ROLL OP_ROT OP_CHECKSIG OP_NIP OP_NIP OP_NIP OP_ELSE OP_4 OP_ROLL OP_1 OP_NUMEQUAL OP_IF OP_ROT OP_CHECKLOCKTIMEVERIFY OP_DROP OP_3 OP_ROLL OP_SWAP OP_CHECKSIG OP_NIP OP_NIP OP_ELSE OP_FALSE OP_ENDIF OP_ENDIF -# optimisation: opcount ~28%, bytes ~30% -; - -# Local.bitcoin.com -# 86 opcount, 128 bytes -OP_0 OP_8 OP_NUM2BIN OP_0 OP_8 OP_NUM2BIN OP_10 OP_PICK OP_1 OP_NUMEQUAL OP_IF OP_3 OP_PICK OP_2 OP_ROLL OP_DROP OP_SWAP OP_2 OP_PICK OP_1 OP_ROLL OP_DROP OP_ELSE OP_10 OP_PICK OP_2 OP_NUMEQUAL OP_IF OP_3 OP_PICK OP_2 OP_ROLL OP_DROP OP_SWAP OP_4 OP_PICK OP_1 OP_ROLL OP_DROP OP_ELSE OP_10 OP_PICK OP_3 OP_NUMEQUAL OP_IF OP_2 OP_PICK OP_2 OP_ROLL OP_DROP OP_SWAP OP_3 OP_PICK OP_1 OP_ROLL OP_DROP OP_ELSE OP_10 OP_PICK OP_4 OP_NUMEQUAL OP_IF OP_2 OP_PICK OP_2 OP_ROLL OP_DROP OP_SWAP OP_4 OP_PICK OP_1 OP_ROLL OP_DROP OP_ELSE OP_FALSE OP_VERIFY OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_9 OP_PICK OP_HASH160 OP_1 OP_PICK OP_EQUAL OP_VERIFY OP_7 OP_PICK OP_HASH160 OP_2 OP_PICK OP_EQUAL OP_VERIFY OP_5 OP_PICK OP_11 OP_PICK OP_8 OP_NUM2BIN OP_CAT OP_9 OP_PICK OP_1 OP_PICK OP_12 OP_PICK OP_CHECKDATASIG OP_VERIFY OP_7 OP_PICK OP_9 OP_PICK OP_CHECKSIG OP_VERIFY OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_TRUE -<=> -# 60 opcount, 86 bytes -OP_0 OP_8 OP_NUM2BIN OP_0 OP_8 OP_NUM2BIN OP_10 OP_PICK OP_1 OP_NUMEQUAL OP_IF OP_2DROP OP_2DUP OP_ELSE OP_10 OP_PICK OP_2 OP_NUMEQUAL OP_IF OP_NIP OP_2 OP_PICK OP_SWAP OP_4 OP_PICK OP_NIP OP_ELSE OP_10 OP_PICK OP_3 OP_NUMEQUAL OP_IF OP_NIP OP_OVER OP_SWAP OP_3 OP_PICK OP_NIP OP_ELSE OP_10 OP_PICK OP_4 OP_NUMEQUAL OP_IF OP_NIP OP_OVER OP_SWAP OP_4 OP_PICK OP_NIP OP_ELSE OP_FALSE OP_VERIFY OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_9 OP_PICK OP_HASH160 OP_EQUALVERIFY OP_6 OP_PICK OP_HASH160 OP_EQUALVERIFY OP_3 OP_ROLL OP_8 OP_ROLL OP_8 OP_NUM2BIN OP_CAT OP_6 OP_ROLL OP_SWAP OP_7 OP_ROLL OP_CHECKDATASIGVERIFY OP_3 OP_ROLL OP_4 OP_ROLL OP_CHECKSIG OP_NIP OP_NIP OP_NIP -# optimisation: opcount ~33%, bytes ~33% -; - -# Last Will -# 139 opcount, 207 bytes -OP_3 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_4 OP_PICK OP_SHA256 OP_RIPEMD160 OP_1 OP_PICK OP_EQUAL OP_VERIFY OP_5 OP_PICK OP_5 OP_PICK OP_CHECKSIG OP_VERIFY OP_6 OP_PICK OP_8 OP_PICK OP_CAT OP_9 OP_PICK OP_CAT OP_10 OP_PICK OP_CAT OP_11 OP_PICK OP_CAT OP_12 OP_PICK OP_CAT OP_13 OP_PICK OP_CAT OP_6 OP_PICK OP_SIZE OP_1 OP_SUB OP_SPLIT OP_DROP OP_1 OP_PICK OP_SHA256 OP_7 OP_PICK OP_CHECKDATASIG OP_VERIFY 1000 OP_11 OP_PICK OP_BIN2NUM OP_1 OP_PICK OP_SUB OP_8 OP_NUM2BIN 0x87 0xa9 0x14 0x17 OP_15 OP_PICK OP_1 OP_SPLIT OP_NIP 436800 OP_CHECKSEQUENCEVERIFY OP_DROP OP_14 OP_PICK OP_BIN2NUM OP_2 OP_GREATERTHANOREQUAL OP_VERIFY OP_5 OP_PICK OP_2 OP_PICK OP_CAT OP_4 OP_PICK OP_CAT OP_3 OP_PICK OP_CAT OP_1 OP_PICK OP_HASH160 OP_CAT OP_5 OP_PICK OP_CAT OP_HASH256 20 OP_PICK OP_EQUAL OP_VERIFY OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_TRUE OP_ELSE OP_3 OP_PICK OP_1 OP_NUMEQUAL OP_IF OP_4 OP_PICK OP_HASH160 OP_2 OP_PICK OP_EQUAL OP_VERIFY OP_5 OP_PICK OP_5 OP_PICK OP_CHECKSIG OP_VERIFY OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_TRUE OP_ELSE OP_3 OP_PICK OP_2 OP_NUMEQUAL OP_IF 15552000 OP_CHECKSEQUENCEVERIFY OP_DROP OP_4 OP_PICK OP_HASH160 OP_3 OP_PICK OP_EQUAL OP_VERIFY OP_5 OP_PICK OP_5 OP_PICK OP_CHECKSIG OP_VERIFY OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_TRUE OP_ELSE OP_FALSE OP_ENDIF OP_ENDIF OP_ENDIF -<=> -# 102 opcount, 155 bytes -OP_3 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_4 OP_PICK OP_HASH160 OP_EQUALVERIFY OP_4 OP_PICK OP_4 OP_PICK OP_CHECKSIGVERIFY OP_5 OP_PICK OP_7 OP_ROLL OP_CAT OP_7 OP_PICK OP_CAT OP_8 OP_PICK OP_CAT OP_9 OP_ROLL OP_CAT OP_9 OP_PICK OP_CAT OP_10 OP_ROLL OP_CAT OP_5 OP_ROLL OP_SIZE OP_1SUB OP_SPLIT OP_DROP OP_SWAP OP_SHA256 OP_5 OP_ROLL OP_CHECKDATASIGVERIFY 1000 OP_6 OP_ROLL OP_BIN2NUM OP_SWAP OP_SUB OP_8 OP_NUM2BIN 0x87 0xa9 0x14 0x17 OP_9 OP_ROLL OP_1 OP_SPLIT OP_NIP 436800 OP_CHECKSEQUENCEVERIFY OP_DROP OP_9 OP_ROLL OP_BIN2NUM OP_2 OP_GREATERTHANOREQUAL OP_VERIFY OP_5 OP_ROLL OP_ROT OP_CAT OP_3 OP_ROLL OP_CAT OP_ROT OP_CAT OP_SWAP OP_HASH160 OP_CAT OP_SWAP OP_CAT OP_HASH256 OP_4 OP_ROLL OP_EQUAL OP_NIP OP_NIP OP_NIP OP_ELSE OP_3 OP_PICK OP_1 OP_NUMEQUAL OP_IF OP_4 OP_PICK OP_HASH160 OP_ROT OP_EQUALVERIFY OP_4 OP_ROLL OP_4 OP_ROLL OP_CHECKSIG OP_NIP OP_NIP OP_NIP OP_ELSE OP_3 OP_ROLL OP_2 OP_NUMEQUAL OP_IF 15552000 OP_CHECKSEQUENCEVERIFY OP_DROP OP_3 OP_PICK OP_HASH160 OP_3 OP_ROLL OP_EQUALVERIFY OP_2SWAP OP_CHECKSIG OP_NIP OP_NIP OP_ELSE OP_FALSE OP_ENDIF OP_ENDIF OP_ENDIF -# optimisation: opcount ~27%, bytes ~25% -; - -# Mecenas -# 177 opcount, 260 bytes -OP_3 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_6 OP_PICK OP_SIZE OP_NIP OP_4 OP_NUMEQUAL OP_VERIFY OP_7 OP_PICK OP_SIZE OP_NIP 100 OP_NUMEQUAL OP_VERIFY OP_9 OP_PICK OP_SIZE OP_NIP OP_8 OP_NUMEQUAL OP_VERIFY OP_10 OP_PICK OP_SIZE OP_NIP OP_4 OP_NUMEQUAL OP_VERIFY OP_10 OP_PICK OP_SIZE OP_NIP 32 OP_NUMEQUAL OP_VERIFY OP_12 OP_PICK OP_SIZE OP_NIP OP_8 OP_NUMEQUAL OP_VERIFY OP_5 OP_PICK OP_5 OP_PICK OP_CHECKSIG OP_VERIFY OP_6 OP_PICK OP_8 OP_PICK OP_CAT OP_9 OP_PICK OP_CAT OP_10 OP_PICK OP_CAT OP_11 OP_PICK OP_CAT OP_12 OP_PICK OP_CAT OP_13 OP_PICK OP_CAT OP_6 OP_PICK OP_SIZE OP_1 OP_SUB OP_SPLIT OP_DROP OP_1 OP_PICK OP_SHA256 OP_7 OP_PICK OP_CHECKDATASIG OP_VERIFY 1000 OP_4 OP_PICK OP_8 OP_NUM2BIN OP_12 OP_PICK OP_BIN2NUM OP_6 OP_PICK OP_SUB OP_2 OP_PICK OP_SUB OP_8 OP_NUM2BIN 0x76 0x87 0xa9 0x14 0x17 0x19 0x88 0xac 20 OP_PICK OP_3 OP_SPLIT OP_NIP 2700000 OP_CHECKSEQUENCEVERIFY OP_DROP 19 OP_PICK OP_BIN2NUM OP_2 OP_GREATERTHANOREQUAL OP_VERIFY OP_9 OP_PICK OP_5 OP_PICK OP_CAT OP_7 OP_PICK OP_CAT OP_6 OP_PICK OP_CAT OP_1 OP_PICK OP_HASH160 OP_CAT OP_8 OP_PICK OP_CAT OP_11 OP_PICK OP_5 OP_PICK OP_CAT OP_10 OP_PICK OP_CAT OP_8 OP_PICK OP_CAT OP_7 OP_PICK OP_CAT OP_15 OP_PICK OP_CAT OP_4 OP_PICK OP_CAT OP_3 OP_PICK OP_CAT OP_1 OP_PICK OP_1 OP_PICK OP_CAT OP_HASH256 27 OP_PICK OP_SHA256 OP_EQUAL OP_VERIFY OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_TRUE OP_ELSE OP_3 OP_PICK OP_1 OP_NUMEQUAL OP_IF OP_4 OP_PICK OP_HASH160 OP_2 OP_PICK OP_EQUAL OP_VERIFY OP_5 OP_PICK OP_5 OP_PICK OP_CHECKSIG OP_VERIFY OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_DROP OP_TRUE OP_ELSE OP_FALSE OP_ENDIF OP_ENDIF -<=> -# 131 opcount, 207 bytes -OP_3 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_6 OP_PICK OP_SIZE OP_NIP OP_4 OP_NUMEQUALVERIFY OP_7 OP_PICK OP_SIZE OP_NIP 100 OP_NUMEQUALVERIFY OP_9 OP_PICK OP_SIZE OP_NIP OP_8 OP_NUMEQUALVERIFY OP_10 OP_PICK OP_SIZE OP_NIP OP_4 OP_NUMEQUALVERIFY OP_10 OP_PICK OP_SIZE OP_NIP 32 OP_NUMEQUALVERIFY OP_12 OP_PICK OP_SIZE OP_NIP OP_8 OP_NUMEQUALVERIFY OP_5 OP_PICK OP_5 OP_PICK OP_CHECKSIGVERIFY OP_6 OP_PICK OP_8 OP_ROLL OP_CAT OP_8 OP_PICK OP_CAT OP_9 OP_PICK OP_CAT OP_10 OP_ROLL OP_CAT OP_10 OP_PICK OP_CAT OP_11 OP_ROLL OP_CAT OP_6 OP_ROLL OP_SIZE OP_1SUB OP_SPLIT OP_DROP OP_SWAP OP_SHA256 OP_6 OP_ROLL OP_CHECKDATASIGVERIFY 1000 OP_3 OP_PICK OP_8 OP_NUM2BIN OP_8 OP_ROLL OP_BIN2NUM OP_5 OP_ROLL OP_SUB OP_ROT OP_SUB OP_8 OP_NUM2BIN 0x76 0x87 0xa9 0x14 0x17 0x19 0x88 0xac OP_14 OP_ROLL OP_3 OP_SPLIT OP_NIP 2700000 OP_CHECKSEQUENCEVERIFY OP_DROP OP_14 OP_ROLL OP_BIN2NUM OP_2 OP_GREATERTHANOREQUAL OP_VERIFY OP_9 OP_ROLL OP_5 OP_ROLL OP_CAT OP_6 OP_PICK OP_CAT OP_5 OP_PICK OP_CAT OP_SWAP OP_HASH160 OP_CAT OP_6 OP_ROLL OP_CAT OP_7 OP_ROLL OP_4 OP_ROLL OP_CAT OP_6 OP_ROLL OP_CAT OP_5 OP_ROLL OP_CAT OP_4 OP_ROLL OP_CAT OP_4 OP_ROLL OP_CAT OP_3 OP_ROLL OP_CAT OP_ROT OP_CAT OP_CAT OP_HASH256 OP_3 OP_ROLL OP_SHA256 OP_EQUAL OP_NIP OP_NIP OP_ELSE OP_3 OP_ROLL OP_1 OP_NUMEQUAL OP_IF OP_3 OP_PICK OP_HASH160 OP_ROT OP_EQUALVERIFY OP_2SWAP OP_CHECKSIG OP_NIP OP_NIP OP_ELSE OP_FALSE OP_ENDIF OP_ENDIF -# optimisation: opcount ~26%, bytes ~20% -; - -# Avg unoptimised opcount: 593 / 9 ≈ 65.89 -# Avg unoptimised bytes: 864 / 9 = 96 - -# Avg optimised opcount: 420 / 9 ≈ 46.67 -# Avg optimised bytes: 616 / 9 ≈ 68.44 - -# Avg opcount optimisation: (56.75 - 39.75) / 56.75 ≈ 29% -# Avg bytes optimisation: (96 - 68.44) / 96 ≈ 29% - -# Min optimisation (Mecenas): -# opcount: 177 -> 131 (~26%) -# bytes: 250 -> 207 (~20%) - -# Max optimisation (P2PKH): -# opcount: 12 -> 4 (~67%) -# bytes: 17 -> 4 (~76%) diff --git a/packages/cashc/test/cashproof/0.3.3=0.4.0.equiv b/packages/cashc/test/cashproof/0.3.3=0.4.0.equiv deleted file mode 100644 index a6569563d..000000000 --- a/packages/cashc/test/cashproof/0.3.3=0.4.0.equiv +++ /dev/null @@ -1,32 +0,0 @@ -!full_script=True; - - -# THIS CASHPROOF FAILS DUE TO THE NUMEQUALVERIFY OPTIMISATION MAKING THE IF-STATEMENTS -# NOT EQUIVALENT. I'M STILL COMMITTING THIS FILE FOR REFERENCE. - -# TransferWithTimeout -# 22 opcount, 31 bytes -OP_3 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_4 OP_ROLL OP_ROT OP_CHECKSIG OP_NIP OP_NIP OP_NIP OP_ELSE OP_3 OP_ROLL OP_1 OP_NUMEQUAL OP_IF OP_3 OP_ROLL OP_SWAP OP_CHECKSIGVERIFY OP_SWAP OP_CHECKLOCKTIMEVERIFY OP_2DROP OP_TRUE OP_ELSE OP_FALSE OP_ENDIF OP_ENDIF -<=> -# 19 opcount, 26 bytes -OP_3 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_4 OP_ROLL OP_ROT OP_CHECKSIG OP_NIP OP_NIP OP_NIP OP_ELSE OP_3 OP_ROLL OP_1 OP_NUMEQUALVERIFY OP_3 OP_ROLL OP_SWAP OP_CHECKSIGVERIFY OP_SWAP OP_CHECKLOCKTIMEVERIFY OP_2DROP OP_TRUE OP_ENDIF -# optimisation: opcount ~14%, bytes ~16% -; - -# HTLC -# 28 opcount, 37 bytes -OP_4 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_5 OP_ROLL OP_SHA256 OP_4 OP_ROLL OP_EQUALVERIFY OP_4 OP_ROLL OP_ROT OP_CHECKSIG OP_NIP OP_NIP OP_NIP OP_ELSE OP_4 OP_ROLL OP_1 OP_NUMEQUAL OP_IF OP_ROT OP_CHECKLOCKTIMEVERIFY OP_DROP OP_3 OP_ROLL OP_SWAP OP_CHECKSIG OP_NIP OP_NIP OP_ELSE OP_FALSE OP_ENDIF OP_ENDIF -<=> -# 25 opcount, 33 bytes -OP_4 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_5 OP_ROLL OP_SHA256 OP_4 OP_ROLL OP_EQUALVERIFY OP_4 OP_ROLL OP_ROT OP_CHECKSIG OP_NIP OP_NIP OP_NIP OP_ELSE OP_4 OP_ROLL OP_1 OP_NUMEQUALVERIFY OP_ROT OP_CHECKLOCKTIMEVERIFY OP_DROP OP_3 OP_ROLL OP_SWAP OP_CHECKSIG OP_NIP OP_NIP OP_ENDIF -# optimisation: opcount ~11%, bytes ~11% -; - -# Last Will -# 87 opcount, 131 bytes -OP_3 OP_PICK OP_0 OP_NUMEQUAL OP_IF 15552000 OP_CHECKSEQUENCEVERIFY OP_DROP OP_4 OP_PICK OP_HASH160 OP_3 OP_ROLL OP_EQUALVERIFY OP_4 OP_ROLL OP_4 OP_ROLL OP_CHECKSIG OP_NIP OP_NIP OP_NIP OP_ELSE OP_3 OP_PICK OP_1 OP_NUMEQUAL OP_IF OP_4 OP_PICK OP_HASH160 OP_ROT OP_EQUALVERIFY OP_4 OP_ROLL OP_4 OP_ROLL OP_CHECKSIG OP_NIP OP_NIP OP_NIP OP_ELSE OP_3 OP_ROLL OP_2 OP_NUMEQUAL OP_IF OP_3 OP_PICK 105 OP_SPLIT OP_NIP OP_SIZE 52 OP_SUB OP_SPLIT OP_8 OP_SPLIT OP_4 OP_SPLIT OP_NIP 32 OP_SPLIT OP_DROP OP_7 OP_PICK OP_HASH160 OP_4 OP_ROLL OP_EQUALVERIFY OP_7 OP_ROLL OP_7 OP_ROLL OP_2DUP OP_SWAP OP_SIZE OP_1SUB OP_SPLIT OP_DROP OP_9 OP_ROLL OP_SHA256 OP_ROT OP_CHECKDATASIGVERIFY OP_CHECKSIGVERIFY 1000 OP_ROT OP_BIN2NUM OP_SWAP OP_SUB OP_8 OP_NUM2BIN 0x17a914 OP_CAT OP_ROT OP_HASH160 OP_CAT 0x87 OP_CAT OP_HASH256 OP_EQUAL OP_NIP OP_NIP OP_ELSE OP_FALSE OP_ENDIF OP_ENDIF OP_ENDIF -<=> -# 83 opcount, 124 -OP_3 OP_PICK OP_0 OP_NUMEQUAL OP_IF 15552000 OP_CHECKSEQUENCEVERIFY OP_DROP OP_4 OP_PICK OP_HASH160 OP_3 OP_ROLL OP_EQUALVERIFY OP_4 OP_ROLL OP_4 OP_ROLL OP_CHECKSIG OP_NIP OP_NIP OP_NIP OP_ELSE OP_3 OP_PICK OP_1 OP_NUMEQUAL OP_IF OP_4 OP_PICK OP_HASH160 OP_ROT OP_EQUALVERIFY OP_4 OP_ROLL OP_4 OP_ROLL OP_CHECKSIG OP_NIP OP_NIP OP_NIP OP_ELSE OP_3 OP_ROLL OP_2 OP_NUMEQUALVERIFY OP_3 OP_PICK 105 OP_SPLIT OP_NIP OP_SIZE 52 OP_SUB OP_SPLIT OP_8 OP_SPLIT OP_4 OP_SPLIT OP_NIP 32 OP_SPLIT OP_DROP OP_7 OP_PICK OP_HASH160 OP_4 OP_ROLL OP_EQUALVERIFY OP_7 OP_ROLL OP_7 OP_ROLL OP_2DUP OP_SWAP OP_SIZE OP_1SUB OP_SPLIT OP_DROP OP_9 OP_ROLL OP_SHA256 OP_ROT OP_CHECKDATASIGVERIFY OP_CHECKSIGVERIFY 1000 OP_ROT OP_BIN2NUM OP_SWAP OP_SUB OP_8 OP_NUM2BIN 0x17a914 OP_CAT OP_ROT OP_HASH160 OP_CAT 0x87 OP_CAT OP_HASH256 OP_EQUAL OP_NIP OP_NIP OP_ENDIF OP_ENDIF -# optimisation: opcount ~5%, bytes ~5% -; diff --git a/packages/cashc/test/cashproof/slice.equiv b/packages/cashc/test/cashproof/slice.equiv deleted file mode 100644 index e16364cb0..000000000 --- a/packages/cashc/test/cashproof/slice.equiv +++ /dev/null @@ -1,14 +0,0 @@ -!full_script=True; - -# We are unable to run cashproof any more due to Python ecosystem issues, but we're including this file for reference. - -# x.slice(10, 25) & x.split(25)[0].split(10)[1] -25 OP_SPLIT OP_DROP OP_10 OP_SPLIT OP_NIP -<=> -# x.split(10)[1].split(15)[0] -OP_10 OP_SPLIT OP_NIP OP_15 OP_SPLIT OP_DROP -; - -# Slice optimisation -OP_0 OP_SPLIT OP_NIP <=> ; -OP_SIZE OP_SPLIT OP_DROP <=> ; diff --git a/packages/cashc/test/compiler/AssignTypeError/destructure_count_mismatch.cash b/packages/cashc/test/compiler/AssignTypeError/destructure_count_mismatch.cash new file mode 100644 index 000000000..6f56ddb33 --- /dev/null +++ b/packages/cashc/test/compiler/AssignTypeError/destructure_count_mismatch.cash @@ -0,0 +1,11 @@ +function pair(int a) returns (int, int) { + return a, a + 1; +} + +contract Test() { + function spend(int x) { + int p, int q, int r = pair(x); + require(p == q); + require(q == r); + } +} diff --git a/packages/cashc/test/compiler/AssignTypeError/global_constant_wrong_type.cash b/packages/cashc/test/compiler/AssignTypeError/global_constant_wrong_type.cash new file mode 100644 index 000000000..7017faae8 --- /dev/null +++ b/packages/cashc/test/compiler/AssignTypeError/global_constant_wrong_type.cash @@ -0,0 +1,7 @@ +bool constant VALUE = 1; + +contract GlobalConstantWrongType() { + function spend() { + require(true); + } +} diff --git a/packages/cashc/test/compiler/TupleAssignmentError/unpack_not_tuple.cash b/packages/cashc/test/compiler/AssignTypeError/unpack_not_tuple.cash similarity index 100% rename from packages/cashc/test/compiler/TupleAssignmentError/unpack_not_tuple.cash rename to packages/cashc/test/compiler/AssignTypeError/unpack_not_tuple.cash diff --git a/packages/cashc/test/compiler/AssignTypeError/void_function_as_value.cash b/packages/cashc/test/compiler/AssignTypeError/void_function_as_value.cash new file mode 100644 index 000000000..f4a16022d --- /dev/null +++ b/packages/cashc/test/compiler/AssignTypeError/void_function_as_value.cash @@ -0,0 +1,10 @@ +function check(int a) { + require(a > 0); +} + +contract Test() { + function spend(int x) { + int y = check(x); + require(y == 0); + } +} diff --git a/packages/cashc/test/compiler/ConstantModificationError/modify_global_constant.cash b/packages/cashc/test/compiler/ConstantModificationError/modify_global_constant.cash new file mode 100644 index 000000000..4f6bcf0d1 --- /dev/null +++ b/packages/cashc/test/compiler/ConstantModificationError/modify_global_constant.cash @@ -0,0 +1,8 @@ +int constant VALUE = 1; + +contract ModifyGlobalConstant() { + function spend() { + VALUE = 2; + require(true); + } +} diff --git a/packages/cashc/test/compiler/ConstantModificationError/reassign_constant_tuple_target.cash b/packages/cashc/test/compiler/ConstantModificationError/reassign_constant_tuple_target.cash new file mode 100644 index 000000000..fd0dcb216 --- /dev/null +++ b/packages/cashc/test/compiler/ConstantModificationError/reassign_constant_tuple_target.cash @@ -0,0 +1,7 @@ +contract Test() { + function spend() { + bytes constant head, bytes tail = 0x1234.split(1); + head = tail; + require(head == 0x34); + } +} diff --git a/packages/cashc/test/compiler/ConstantModificationError/tuple_reassign_constant.cash b/packages/cashc/test/compiler/ConstantModificationError/tuple_reassign_constant.cash new file mode 100644 index 000000000..f439d5641 --- /dev/null +++ b/packages/cashc/test/compiler/ConstantModificationError/tuple_reassign_constant.cash @@ -0,0 +1,11 @@ +function pair(int n) returns (int, int) { + return n + 1, n + 2; +} + +contract Test() { + function spend(int n) { + int constant f = 5; + int q, f = pair(n); + require(q > f); + } +} diff --git a/packages/cashc/test/compiler/DivisionByZeroError/global_constant_division_by_zero.cash b/packages/cashc/test/compiler/DivisionByZeroError/global_constant_division_by_zero.cash new file mode 100644 index 000000000..464478aa7 --- /dev/null +++ b/packages/cashc/test/compiler/DivisionByZeroError/global_constant_division_by_zero.cash @@ -0,0 +1,7 @@ +int constant DIVIDED = 10 / 0; + +contract GlobalConstantDivisionByZero() { + function spend() { + require(DIVIDED == 0); + } +} diff --git a/packages/cashc/test/compiler/DuplicateTupleTargetError/declaration_then_reassignment.cash b/packages/cashc/test/compiler/DuplicateTupleTargetError/declaration_then_reassignment.cash new file mode 100644 index 000000000..b1785eb5f --- /dev/null +++ b/packages/cashc/test/compiler/DuplicateTupleTargetError/declaration_then_reassignment.cash @@ -0,0 +1,10 @@ +function pair(int n) returns (int, int) { + return n + 1, n + 2; +} + +contract Test() { + function spend(int n) { + int a, a = pair(n); + require(a > 0); + } +} diff --git a/packages/cashc/test/compiler/DuplicateTupleTargetError/duplicate_reassignment_targets.cash b/packages/cashc/test/compiler/DuplicateTupleTargetError/duplicate_reassignment_targets.cash new file mode 100644 index 000000000..d4a721253 --- /dev/null +++ b/packages/cashc/test/compiler/DuplicateTupleTargetError/duplicate_reassignment_targets.cash @@ -0,0 +1,11 @@ +function pair(int n) returns (int, int) { + return n + 1, n + 2; +} + +contract Test() { + function spend(int n) { + int a = n; + (a, a) = pair(n); + require(a > 0); + } +} diff --git a/packages/cashc/test/compiler/FinalRequireStatementError/void_global_function_without_require.cash b/packages/cashc/test/compiler/FinalRequireStatementError/void_global_function_without_require.cash new file mode 100644 index 000000000..c226aab83 --- /dev/null +++ b/packages/cashc/test/compiler/FinalRequireStatementError/void_global_function_without_require.cash @@ -0,0 +1,11 @@ +function increment(int x) { + x = x + 1; + console.log("incremented", x); +} + +contract VoidGlobalFunctionWithoutRequire() { + function spend(int n) { + increment(n); + require(n < 100); + } +} diff --git a/packages/cashc/test/compiler/InvalidConstantExpressionError/global_constant_unsupported_expression.cash b/packages/cashc/test/compiler/InvalidConstantExpressionError/global_constant_unsupported_expression.cash new file mode 100644 index 000000000..cd8f91ff2 --- /dev/null +++ b/packages/cashc/test/compiler/InvalidConstantExpressionError/global_constant_unsupported_expression.cash @@ -0,0 +1,7 @@ +int constant LOCKTIME = tx.locktime; + +contract GlobalConstantUnsupportedExpression() { + function spend() { + require(LOCKTIME == 0); + } +} diff --git a/packages/cashc/test/compiler/InvalidModifierError/constant_on_contract_parameter.cash b/packages/cashc/test/compiler/InvalidModifierError/constant_on_contract_parameter.cash new file mode 100644 index 000000000..bc070dfd1 --- /dev/null +++ b/packages/cashc/test/compiler/InvalidModifierError/constant_on_contract_parameter.cash @@ -0,0 +1,5 @@ +contract Test(int constant value) { + function spend() { + require(true); + } +} diff --git a/packages/cashc/test/compiler/InvalidModifierError/constant_on_function_parameter.cash b/packages/cashc/test/compiler/InvalidModifierError/constant_on_function_parameter.cash new file mode 100644 index 000000000..5f92f84a3 --- /dev/null +++ b/packages/cashc/test/compiler/InvalidModifierError/constant_on_function_parameter.cash @@ -0,0 +1,5 @@ +contract Test() { + function spend(int constant value) { + require(value == 1); + } +} diff --git a/packages/cashc/test/compiler/InvalidModifierError/duplicate_modifier.cash b/packages/cashc/test/compiler/InvalidModifierError/duplicate_modifier.cash new file mode 100644 index 000000000..42be6e649 --- /dev/null +++ b/packages/cashc/test/compiler/InvalidModifierError/duplicate_modifier.cash @@ -0,0 +1,5 @@ +contract Test() { + function spend(bytes unused unused padding) { + require(true); + } +} diff --git a/packages/cashc/test/compiler/InvalidModifierError/duplicate_modifier_tuple_target.cash b/packages/cashc/test/compiler/InvalidModifierError/duplicate_modifier_tuple_target.cash new file mode 100644 index 000000000..f0df88095 --- /dev/null +++ b/packages/cashc/test/compiler/InvalidModifierError/duplicate_modifier_tuple_target.cash @@ -0,0 +1,7 @@ +contract Test() { + function spend() { + bytes constant constant head, bytes tail = 0x1234.split(1); + require(head == 0x12); + require(tail == 0x34); + } +} diff --git a/packages/cashc/test/compiler/InvalidModifierError/reference_unused_parameter.cash b/packages/cashc/test/compiler/InvalidModifierError/reference_unused_parameter.cash new file mode 100644 index 000000000..c36d4380b --- /dev/null +++ b/packages/cashc/test/compiler/InvalidModifierError/reference_unused_parameter.cash @@ -0,0 +1,5 @@ +contract Test() { + function spend(int unused value) { + require(value == 1); + } +} diff --git a/packages/cashc/test/compiler/InvalidModifierError/reference_unused_tuple_target.cash b/packages/cashc/test/compiler/InvalidModifierError/reference_unused_tuple_target.cash new file mode 100644 index 000000000..1d17fcebd --- /dev/null +++ b/packages/cashc/test/compiler/InvalidModifierError/reference_unused_tuple_target.cash @@ -0,0 +1,7 @@ +contract Test() { + function spend() { + bytes unused head, bytes tail = 0x1234.split(1); + require(head == 0x12); + require(tail == 0x34); + } +} diff --git a/packages/cashc/test/compiler/InvalidModifierError/reference_unused_variable.cash b/packages/cashc/test/compiler/InvalidModifierError/reference_unused_variable.cash new file mode 100644 index 000000000..179605e60 --- /dev/null +++ b/packages/cashc/test/compiler/InvalidModifierError/reference_unused_variable.cash @@ -0,0 +1,6 @@ +contract Test() { + function spend(int value) { + int unused scratch = value + 1; + require(scratch == 1); + } +} diff --git a/packages/cashc/test/compiler/InvalidModifierError/tuple_reassign_unused.cash b/packages/cashc/test/compiler/InvalidModifierError/tuple_reassign_unused.cash new file mode 100644 index 000000000..37ae74cc0 --- /dev/null +++ b/packages/cashc/test/compiler/InvalidModifierError/tuple_reassign_unused.cash @@ -0,0 +1,13 @@ +function pair(int n) returns (int, int) { + return n + 1, n + 2; +} + +contract Test() { + function spend(int n, int unused pad) { + if (n > 0) { + (int q, pad) = pair(n); + require(q > 0); + } + require(n < 100); + } +} diff --git a/packages/cashc/test/compiler/InvalidSymbolTypeError/assign_to_builtin_name.cash b/packages/cashc/test/compiler/InvalidSymbolTypeError/assign_to_builtin_name.cash new file mode 100644 index 000000000..435003317 --- /dev/null +++ b/packages/cashc/test/compiler/InvalidSymbolTypeError/assign_to_builtin_name.cash @@ -0,0 +1,6 @@ +contract AssignToBuiltinName() { + function spend(int x) { + abs = x; + require(abs > 0); + } +} diff --git a/packages/cashc/test/compiler/InvalidSymbolTypeError/assign_to_function_name.cash b/packages/cashc/test/compiler/InvalidSymbolTypeError/assign_to_function_name.cash new file mode 100644 index 000000000..7c89c6f5a --- /dev/null +++ b/packages/cashc/test/compiler/InvalidSymbolTypeError/assign_to_function_name.cash @@ -0,0 +1,10 @@ +function double(int a) returns (int) { + return a * 2; +} + +contract AssignToFunctionName() { + function spend(int x) { + double = 5; + require(double(x) == 10); + } +} diff --git a/packages/cashc/test/compiler/InvalidSymbolTypeError/tuple_reassign_function_name.cash b/packages/cashc/test/compiler/InvalidSymbolTypeError/tuple_reassign_function_name.cash new file mode 100644 index 000000000..572e098eb --- /dev/null +++ b/packages/cashc/test/compiler/InvalidSymbolTypeError/tuple_reassign_function_name.cash @@ -0,0 +1,10 @@ +function pair(int n) returns (int, int) { + return n + 1, n + 2; +} + +contract Test() { + function spend(int n) { + int q, pair = pair(n); + require(q > 0); + } +} diff --git a/packages/cashc/test/compiler/MisplacedReturnError/conditional_return.cash b/packages/cashc/test/compiler/MisplacedReturnError/conditional_return.cash new file mode 100644 index 000000000..db96fdca9 --- /dev/null +++ b/packages/cashc/test/compiler/MisplacedReturnError/conditional_return.cash @@ -0,0 +1,12 @@ +function clamp(int a) returns (int) { + if (a > 10) { + return 10; + } + return a; +} + +contract Test() { + function spend(int x) { + require(clamp(x) == 5); + } +} diff --git a/packages/cashc/test/compiler/MissingReturnError/global_function_missing_return.cash b/packages/cashc/test/compiler/MissingReturnError/global_function_missing_return.cash new file mode 100644 index 000000000..33c5843f9 --- /dev/null +++ b/packages/cashc/test/compiler/MissingReturnError/global_function_missing_return.cash @@ -0,0 +1,10 @@ +function broken(int a) returns (int) { + int b = a + 1; + require(b > 0); +} + +contract Test() { + function spend(int x) { + require(broken(x) == 2); + } +} diff --git a/packages/cashc/test/compiler/ParseError/import_after_contract.cash b/packages/cashc/test/compiler/ParseError/import_after_contract.cash new file mode 100644 index 000000000..33b18164a --- /dev/null +++ b/packages/cashc/test/compiler/ParseError/import_after_contract.cash @@ -0,0 +1,7 @@ +contract Test() { + function spend() { + require(true); + } +} + +import "./helpers.cash"; diff --git a/packages/cashc/test/compiler/ParseError/import_after_function.cash b/packages/cashc/test/compiler/ParseError/import_after_function.cash new file mode 100644 index 000000000..ee2d7c601 --- /dev/null +++ b/packages/cashc/test/compiler/ParseError/import_after_function.cash @@ -0,0 +1,11 @@ +function helper(int a) returns (int) { + return a + 1; +} + +import "./other.cash"; + +contract Test() { + function spend(int x) { + require(helper(x) == 2); + } +} diff --git a/packages/cashc/test/compiler/ParseError/modifier_on_tuple_reassignment.cash b/packages/cashc/test/compiler/ParseError/modifier_on_tuple_reassignment.cash new file mode 100644 index 000000000..9311e4e4f --- /dev/null +++ b/packages/cashc/test/compiler/ParseError/modifier_on_tuple_reassignment.cash @@ -0,0 +1,7 @@ +contract Test() { + function spend() { + bytes head = 0x00; + constant head, bytes tail = 0x1234.split(1); + require(tail == 0x34); + } +} diff --git a/packages/cashc/test/compiler/ParseError/single_type_destructuring.cash b/packages/cashc/test/compiler/ParseError/single_type_destructuring.cash deleted file mode 100644 index 89afb1bfe..000000000 --- a/packages/cashc/test/compiler/ParseError/single_type_destructuring.cash +++ /dev/null @@ -1,5 +0,0 @@ -contract Test() { - function test1(string s) { - string x, y = s.split(5); - } -} diff --git a/packages/cashc/test/compiler/RedefinitionError/duplicate_global_constant.cash b/packages/cashc/test/compiler/RedefinitionError/duplicate_global_constant.cash new file mode 100644 index 000000000..697a30610 --- /dev/null +++ b/packages/cashc/test/compiler/RedefinitionError/duplicate_global_constant.cash @@ -0,0 +1,8 @@ +int constant VALUE = 1; +int constant VALUE = 2; + +contract DuplicateGlobalConstant() { + function spend() { + require(VALUE == 1); + } +} diff --git a/packages/cashc/test/compiler/RedefinitionError/duplicate_global_function.cash b/packages/cashc/test/compiler/RedefinitionError/duplicate_global_function.cash new file mode 100644 index 000000000..41ed861d0 --- /dev/null +++ b/packages/cashc/test/compiler/RedefinitionError/duplicate_global_function.cash @@ -0,0 +1,13 @@ +function foo(int a) returns (int) { + return a + 1; +} + +function foo(int a) returns (int) { + return a + 2; +} + +contract DuplicateGlobalFunction() { + function spend() { + require(foo(1) == 2); + } +} diff --git a/packages/cashc/test/compiler/RedefinitionError/global_constant_with_builtin_name.cash b/packages/cashc/test/compiler/RedefinitionError/global_constant_with_builtin_name.cash new file mode 100644 index 000000000..d7ba8d88d --- /dev/null +++ b/packages/cashc/test/compiler/RedefinitionError/global_constant_with_builtin_name.cash @@ -0,0 +1,7 @@ +int constant abs = 1; + +contract GlobalConstantWithBuiltinName() { + function spend() { + require(abs == 1); + } +} diff --git a/packages/cashc/test/compiler/RedefinitionError/global_constant_with_function_name.cash b/packages/cashc/test/compiler/RedefinitionError/global_constant_with_function_name.cash new file mode 100644 index 000000000..204d18c4a --- /dev/null +++ b/packages/cashc/test/compiler/RedefinitionError/global_constant_with_function_name.cash @@ -0,0 +1,11 @@ +int constant helper = 1; + +function helper() returns (int) { + return 1; +} + +contract GlobalConstantWithFunctionName() { + function spend() { + require(helper() == 1); + } +} diff --git a/packages/cashc/test/compiler/RedefinitionError/parameter_shadows_global_constant.cash b/packages/cashc/test/compiler/RedefinitionError/parameter_shadows_global_constant.cash new file mode 100644 index 000000000..d2a8dc395 --- /dev/null +++ b/packages/cashc/test/compiler/RedefinitionError/parameter_shadows_global_constant.cash @@ -0,0 +1,7 @@ +int constant VALUE = 1; + +contract ParameterShadowsGlobalConstant(int VALUE) { + function spend() { + require(VALUE == 1); + } +} diff --git a/packages/cashc/test/compiler/RedefinitionError/variable_shadows_global_constant.cash b/packages/cashc/test/compiler/RedefinitionError/variable_shadows_global_constant.cash new file mode 100644 index 000000000..602590527 --- /dev/null +++ b/packages/cashc/test/compiler/RedefinitionError/variable_shadows_global_constant.cash @@ -0,0 +1,8 @@ +int constant VALUE = 1; + +contract VariableShadowsGlobalConstant() { + function spend() { + int VALUE = 2; + require(VALUE == 2); + } +} diff --git a/packages/cashc/test/compiler/ReturnTypeError/return_in_contract_function.cash b/packages/cashc/test/compiler/ReturnTypeError/return_in_contract_function.cash new file mode 100644 index 000000000..1f7c67b97 --- /dev/null +++ b/packages/cashc/test/compiler/ReturnTypeError/return_in_contract_function.cash @@ -0,0 +1,5 @@ +contract Test() { + function spend(int x) { + return x; + } +} diff --git a/packages/cashc/test/compiler/ReturnTypeError/too_many_return_values.cash b/packages/cashc/test/compiler/ReturnTypeError/too_many_return_values.cash new file mode 100644 index 000000000..d4df23cf9 --- /dev/null +++ b/packages/cashc/test/compiler/ReturnTypeError/too_many_return_values.cash @@ -0,0 +1,10 @@ +function bad(int a) returns (int, int) { + return a; +} + +contract Test() { + function spend(int x) { + int p, int q = bad(x); + require(p == q); + } +} diff --git a/packages/cashc/test/compiler/ReturnTypeError/wrong_return_type.cash b/packages/cashc/test/compiler/ReturnTypeError/wrong_return_type.cash new file mode 100644 index 000000000..e3cb3efad --- /dev/null +++ b/packages/cashc/test/compiler/ReturnTypeError/wrong_return_type.cash @@ -0,0 +1,9 @@ +function toBytes(int a) returns (bytes32) { + return a; +} + +contract Test() { + function spend(int x) { + require(toBytes(x) == 0x0000000000000000000000000000000000000000000000000000000000000000); + } +} diff --git a/packages/cashc/test/compiler/TypeError/multi_return_tuple_comparison.cash b/packages/cashc/test/compiler/TypeError/multi_return_tuple_comparison.cash new file mode 100644 index 000000000..dad470b12 --- /dev/null +++ b/packages/cashc/test/compiler/TypeError/multi_return_tuple_comparison.cash @@ -0,0 +1,11 @@ +function pair(int a) returns (int, int) { + return a, a + 1; +} + +contract Test() { + function spend(int x) { + // Both sides have equal tuple types, so this would pass a pure type-equality check, + // but no operator works on tuples + require(pair(x) == pair(x)); + } +} diff --git a/packages/cashc/test/compiler/TypeError/multi_return_tuple_forwarding.cash b/packages/cashc/test/compiler/TypeError/multi_return_tuple_forwarding.cash new file mode 100644 index 000000000..f9230953a --- /dev/null +++ b/packages/cashc/test/compiler/TypeError/multi_return_tuple_forwarding.cash @@ -0,0 +1,15 @@ +function pair(int a) returns (int, int) { + return a, a + 1; +} + +function forward(int a) returns (int, int) { + // The tuple types match, but a returned expression must be a single value + return pair(a); +} + +contract Test() { + function spend(int x) { + int p, int q = forward(x); + require(p == q); + } +} diff --git a/packages/cashc/test/compiler/TypeError/multi_return_tuple_index.cash b/packages/cashc/test/compiler/TypeError/multi_return_tuple_index.cash new file mode 100644 index 000000000..bdacc05d1 --- /dev/null +++ b/packages/cashc/test/compiler/TypeError/multi_return_tuple_index.cash @@ -0,0 +1,11 @@ +function pair(int a) returns (int, int) { + return a, a + 1; +} + +contract Test() { + function spend(int x) { + // Tuple indexing is only supported on .split() results; a multi-return call + // must be destructured + require(pair(x)[0] == 1); + } +} diff --git a/packages/cashc/test/compiler/UndefinedReferenceError/global_constant_forward_reference.cash b/packages/cashc/test/compiler/UndefinedReferenceError/global_constant_forward_reference.cash new file mode 100644 index 000000000..15b8d7c43 --- /dev/null +++ b/packages/cashc/test/compiler/UndefinedReferenceError/global_constant_forward_reference.cash @@ -0,0 +1,8 @@ +int constant DERIVED = BASE + 1; +int constant BASE = 41; + +contract GlobalConstantForwardReference() { + function spend() { + require(DERIVED == 42); + } +} diff --git a/packages/cashc/test/compiler/UndefinedReferenceError/tuple_reassign_undefined.cash b/packages/cashc/test/compiler/UndefinedReferenceError/tuple_reassign_undefined.cash new file mode 100644 index 000000000..396dcf8ac --- /dev/null +++ b/packages/cashc/test/compiler/UndefinedReferenceError/tuple_reassign_undefined.cash @@ -0,0 +1,12 @@ +function swap(int x, int y) returns (int, int) { + return y, x; +} + +contract Test() { + function spend(int seed) { + int a = seed; + // `b` is never declared, so reassigning it must fail + (a, b) = swap(a, seed); + require(a + b >= 0); + } +} diff --git a/packages/cashc/test/compiler/UnequalTypeError/global_constant_mixed_operands.cash b/packages/cashc/test/compiler/UnequalTypeError/global_constant_mixed_operands.cash new file mode 100644 index 000000000..b297cb48c --- /dev/null +++ b/packages/cashc/test/compiler/UnequalTypeError/global_constant_mixed_operands.cash @@ -0,0 +1,7 @@ +int constant MIXED = 10 + true; + +contract GlobalConstantMixedOperands() { + function spend() { + require(MIXED == 0); + } +} diff --git a/packages/cashc/test/compiler/UnequalTypeError/multi_return_used_as_single_value.cash b/packages/cashc/test/compiler/UnequalTypeError/multi_return_used_as_single_value.cash new file mode 100644 index 000000000..27e7c92e6 --- /dev/null +++ b/packages/cashc/test/compiler/UnequalTypeError/multi_return_used_as_single_value.cash @@ -0,0 +1,9 @@ +function pair(int a) returns (int, int) { + return a, a + 1; +} + +contract Test() { + function spend(int x) { + require(pair(x) == 3); + } +} diff --git a/packages/cashc/test/compiler/UnsafeFunctionOperationError/active_bytecode_in_function.cash b/packages/cashc/test/compiler/UnsafeFunctionOperationError/active_bytecode_in_function.cash new file mode 100644 index 000000000..52af373c3 --- /dev/null +++ b/packages/cashc/test/compiler/UnsafeFunctionOperationError/active_bytecode_in_function.cash @@ -0,0 +1,9 @@ +function bodyMatches(bytes expected) returns (bool) { + return this.activeBytecode == expected; +} + +contract Test() { + function spend(bytes expected) { + require(bodyMatches(expected)); + } +} diff --git a/packages/cashc/test/compiler/UnsafeFunctionOperationError/checkmultisig_in_function.cash b/packages/cashc/test/compiler/UnsafeFunctionOperationError/checkmultisig_in_function.cash new file mode 100644 index 000000000..d2a200870 --- /dev/null +++ b/packages/cashc/test/compiler/UnsafeFunctionOperationError/checkmultisig_in_function.cash @@ -0,0 +1,9 @@ +function verifyMulti(sig s1, sig s2, pubkey pk1, pubkey pk2) returns (bool) { + return checkMultiSig([s1, s2], [pk1, pk2]); +} + +contract Test() { + function spend(sig s1, sig s2, pubkey pk1, pubkey pk2) { + require(verifyMulti(s1, s2, pk1, pk2)); + } +} diff --git a/packages/cashc/test/compiler/UnsafeFunctionOperationError/checksig_in_function.cash b/packages/cashc/test/compiler/UnsafeFunctionOperationError/checksig_in_function.cash new file mode 100644 index 000000000..ddd69039b --- /dev/null +++ b/packages/cashc/test/compiler/UnsafeFunctionOperationError/checksig_in_function.cash @@ -0,0 +1,9 @@ +function verify(sig s, pubkey pk) returns (bool) { + return checkSig(s, pk); +} + +contract Test() { + function spend(sig s, pubkey pk) { + require(verify(s, pk)); + } +} diff --git a/packages/cashc/test/compiler/UnsupportedTypeError/multi_return_nested_in_split_rhs.cash b/packages/cashc/test/compiler/UnsupportedTypeError/multi_return_nested_in_split_rhs.cash new file mode 100644 index 000000000..dd4d46103 --- /dev/null +++ b/packages/cashc/test/compiler/UnsupportedTypeError/multi_return_nested_in_split_rhs.cash @@ -0,0 +1,12 @@ +function pair() returns (bytes, bytes) { + return 0x0102, 0x030405; +} + +contract Test() { + function spend() { + // pair() is nested inside the RHS, not the RHS itself: its first return value + // would be silently discarded + bytes a, bytes b = pair().split(1); + require(a != b); + } +} diff --git a/packages/cashc/test/compiler/ParseError/nonvoid_function_call.cash b/packages/cashc/test/compiler/UnusedFunctionReturnError/nonvoid_function_call.cash similarity index 100% rename from packages/cashc/test/compiler/ParseError/nonvoid_function_call.cash rename to packages/cashc/test/compiler/UnusedFunctionReturnError/nonvoid_function_call.cash diff --git a/packages/cashc/test/compiler/compiler.test.ts b/packages/cashc/test/compiler/compiler.test.ts index f121476ca..7c5050007 100644 --- a/packages/cashc/test/compiler/compiler.test.ts +++ b/packages/cashc/test/compiler/compiler.test.ts @@ -1,13 +1,7 @@ -/* Compiler.test.ts - * - * - This file is used to test the overall functioning of the compiler. - * - It tests successful compilation using fixture .cash files in ../valid-contract-files. - * - It tests compile errors using fixture .cash files in respective Error directories. - */ - import { URL } from 'url'; import { getSubdirectories, readCashFiles } from '../test-utils.js'; import * as Errors from '../../src/Errors.js'; +import * as Warnings from '../../src/Warnings.js'; import { compileString } from '../../src/index.js'; import type { CashScriptErrorListener } from '../../src/index.js'; @@ -19,16 +13,16 @@ contract Test() { } `; const INVALID_SOURCE = 'contract Test() { function unlock() { require(true) } }'; +const UNUSED_VARIABLE_SOURCE = ` +contract Test() { + function hello(sig s, pubkey pk) { + string x = 'Hello'; + require(checkSig(s, pk)); + } +} +`; describe('Compiler', () => { - describe('Successful compilation', () => { - readCashFiles(new URL('../valid-contract-files', import.meta.url)).forEach((file) => { - it(`${file.fn} should succeed`, () => { - expect(() => compileString(file.contents)).not.toThrow(); - }); - }); - }); - describe('Compilation errors', () => { const errorTypes = getSubdirectories(new URL('.', import.meta.url)); @@ -38,7 +32,6 @@ describe('Compiler', () => { it(`${file.fn} should throw ${errorType}`, () => { // Retrieve the correct Error constructor from the Errors.ts file const expectedError = Errors[errorType as keyof typeof Errors]; - if (!expectedError) throw new Error(`Invalid test configuration: error ${errorType} does not exist`); expect(() => compileString(file.contents)).toThrow(expectedError); @@ -48,6 +41,31 @@ describe('Compiler', () => { }); }); + describe('Compilation warnings', () => { + const warningTypes = getSubdirectories(new URL('../warnings/', import.meta.url)); + + warningTypes.forEach((warningType) => { + describe(warningType.toString(), () => { + readCashFiles(new URL(`../warnings/${warningType}`, import.meta.url)).forEach((file) => { + it(`${file.fn} should report ${warningType}`, () => { + // Retrieve the correct Warning constructor from the Warnings.ts file + const expectedWarning = Warnings[warningType as keyof typeof Warnings]; + if (!expectedWarning) throw new Error(`Invalid test configuration: warning ${warningType} does not exist`); + + const warnings: Warnings.CashScriptWarning[] = []; + try { + compileString(file.contents, { warningListener: (warning) => { warnings.push(warning); } }); + } catch { + // ignore compilation errors from later phases + } + + expect(warnings).toContainEqual(expect.any(expectedWarning)); + }); + }); + }); + }); + }); + describe('Custom error listener', () => { it('uses the custom error listener for parse errors', () => { const errors: string[] = []; @@ -74,14 +92,15 @@ describe('Compiler', () => { expect(errors).toHaveLength(1); }); - it('does not include custom error listeners in compiler artifact options', () => { + it('does not include custom error or warning listeners in compiler artifact options', () => { const errorListener: CashScriptErrorListener = { syntaxError(): void { throw new Error('Unexpected parse error'); }, }; - const artifact = compileString(VALID_SOURCE, { enforceLocktimeGuard: false, errorListener }); + const compileOptions = { enforceLocktimeGuard: false, errorListener, warningListener: () => {} }; + const artifact = compileString(VALID_SOURCE, compileOptions); expect(artifact.compiler.options).toEqual({ enforceFunctionParameterTypes: true, @@ -89,4 +108,27 @@ describe('Compiler', () => { }); }); }); + + describe('Custom warning listener', () => { + it('uses the custom warning listener for compilation warnings', () => { + const warnings: Warnings.CashScriptWarning[] = []; + compileString(UNUSED_VARIABLE_SOURCE, { warningListener: (warning) => { warnings.push(warning); } }); + + expect(warnings).toHaveLength(1); + expect(warnings[0]).toBeInstanceOf(Warnings.UnusedVariableWarning); + expect(warnings[0].message).toEqual("Unused variable 'x' at Line 4, Column 8"); + }); + + it('prints warnings with console.warn when no warning listener is provided', () => { + const consoleWarn = vi.spyOn(console, 'warn').mockImplementation(() => {}); + + try { + compileString(UNUSED_VARIABLE_SOURCE); + expect(consoleWarn).toHaveBeenCalledTimes(1); + expect(consoleWarn).toHaveBeenCalledWith("Warning: Unused variable 'x' at Line 4, Column 8"); + } finally { + consoleWarn.mockRestore(); + } + }); + }); }); diff --git a/packages/cashc/test/generation/fixture-utils.ts b/packages/cashc/test/generation/fixture-utils.ts new file mode 100644 index 000000000..5946ffd7c --- /dev/null +++ b/packages/cashc/test/generation/fixture-utils.ts @@ -0,0 +1,30 @@ +import fs from 'fs'; +import { URL } from 'url'; +import { Artifact } from '@cashscript/utils'; +import { InternalCompilerOptions } from '../../src/internal.js'; + +export interface Fixture { + compilerOptions?: InternalCompilerOptions; + artifact: Omit; +} + +export interface FixtureModule { + cashFile: string; + fixtures: Fixture[]; +} + +export async function loadFixtureModules(): Promise { + const modulePaths = fs.readdirSync(new URL('fixtures/', import.meta.url), { recursive: true, encoding: 'utf-8' }) + .map((entry) => entry.replaceAll('\\', '/')) + .filter((entry) => entry.endsWith('.ts')); + + return Promise.all(modulePaths.map(async (modulePath) => { + const imported = await import(`./fixtures/${modulePath.replace(/\.ts$/, '.js')}`); + + if (!Array.isArray(imported.fixtures)) { + throw new Error(`Fixture module fixtures/${modulePath} must export a 'fixtures' array`); + } + + return { cashFile: modulePath.replace(/\.ts$/, '.cash'), fixtures: imported.fixtures }; + })); +} diff --git a/packages/cashc/test/generation/fixtures.ts b/packages/cashc/test/generation/fixtures.ts deleted file mode 100644 index c03ac203d..000000000 --- a/packages/cashc/test/generation/fixtures.ts +++ /dev/null @@ -1,1411 +0,0 @@ -import { Artifact, CompilerOptions } from '@cashscript/utils'; -import fs from 'fs'; -import { URL } from 'url'; -import { version } from '../../src/index.js'; - -interface Fixture { - fn: string, - artifact: Artifact, - compilerOptions?: CompilerOptions, -} - -export const fixtures: Fixture[] = [ - { - fn: 'p2pkh.cash', - artifact: { - contractName: 'P2PKH', - constructorInputs: [{ name: 'pkh', type: 'bytes20' }], - abi: [{ name: 'spend', inputs: [{ name: 'pk', type: 'pubkey' }, { name: 's', type: 'sig' }] }], - bytecode: - // require(hash160(pk) == pkh) - 'OP_OVER OP_HASH160 OP_EQUALVERIFY ' - // require(checkSig(s, pk)) - + 'OP_CHECKSIG', - debug: { - bytecode: '78a988ac', - logs: [], - requires: [ - { ip: 3, line: 3 }, - { ip: 5, line: 4 }, - ], - sourceMap: '3:24:3:26;:16::27:1;:8::36;4::4:33', - }, - source: fs.readFileSync(new URL('../valid-contract-files/p2pkh.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '07f5c2c2cf10439f063f3b92b9420b110614fb57b5c5015120bfca2688fedcc7', - }, - }, - { - fn: 'reassignment.cash', - artifact: { - contractName: 'Reassignment', - constructorInputs: [{ name: 'x', type: 'int' }, { name: 'y', type: 'string' }], - abi: [{ name: 'hello', inputs: [{ name: 'pk', type: 'pubkey' }, { name: 's', type: 'sig' }] }], - bytecode: - // int myVariable = 10 - 4 - 'OP_10 OP_4 OP_SUB ' - // int myOtherVariable = 20 + myVariable % 2 - + '14 OP_SWAP OP_2 OP_MOD OP_ADD ' - // require(myOtherVariable > x) - + 'OP_LESSTHAN OP_VERIFY ' - // string hw = "Hello World" - + '48656c6c6f20576f726c64 ' - // hw = hw + y - + 'OP_DUP OP_ROT OP_CAT ' - // require(ripemd160(pk) == ripemd160(hw)) - + 'OP_2 OP_PICK OP_RIPEMD160 OP_SWAP OP_RIPEMD160 OP_EQUALVERIFY ' - // require(checkSig(s, pk)) - + 'OP_ROT OP_ROT OP_CHECKSIG ' - + 'OP_NIP', - debug: { - bytecode: '5a549401147c5297939f690b48656c6c6f20576f726c64767b7e5279a67ca6887b7bac77', - logs: [], - requires: [ - { ip: 11, line: 5 }, - { ip: 21, line: 10 }, - { ip: 25, line: 11 }, - ], - sourceMap: '3:25:3:27;:30::31;:25:::1;4:30:4:32:0;:35::45;:48::49;:35:::1;:30;5:16:5:35;:8::37;7:20:7:33:0;8:13:8:15;:18::19;:13:::1;10:26:10:28:0;;:16::29:1;:43::45:0;:33::46:1;:8::48;11:25:11:26:0;:28::30;:8::33:1;2:37:12:5', - sourceTags: '23:23:sc', - }, - source: fs.readFileSync(new URL('../valid-contract-files/reassignment.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '7dfcd01f0ecb5e1dec3d2fb363b5af79eb84a15395e1b57fe9c383cb3861d634', - }, - }, - { - fn: 'if_statement.cash', - artifact: { - contractName: 'IfStatement', - constructorInputs: [{ name: 'x', type: 'int' }, { name: 'y', type: 'int' }], - abi: [{ name: 'hello', inputs: [{ name: 'a', type: 'int' }, { name: 'b', type: 'int' }] }], - bytecode: - // int d = a + b - 'OP_2OVER OP_ADD ' - // d = d - a - + 'OP_DUP OP_4 OP_PICK OP_SUB ' - // if (d == x - 2) { - + 'OP_DUP OP_3 OP_ROLL OP_2 OP_SUB OP_NUMEQUAL OP_IF ' - // int c = d + b - + 'OP_DUP OP_5 OP_PICK OP_ADD ' - // d = a + c - + 'OP_4 OP_PICK OP_OVER OP_ADD OP_ROT OP_DROP OP_SWAP ' - // require(c > d) - + 'OP_2DUP OP_LESSTHAN OP_VERIFY ' - // } else { - + 'OP_DROP OP_ELSE ' - // require(d == a) } - + 'OP_DUP OP_4 OP_PICK OP_NUMEQUALVERIFY OP_ENDIF ' - // d = d + a - + 'OP_DUP OP_4 OP_ROLL OP_ADD ' - // require(d == y) - + 'OP_3 OP_ROLL OP_NUMEQUAL ' - + 'OP_NIP OP_NIP OP_NIP', - debug: { - bytecode: '70937654799476537a52949c6376557993547978937b757c6e9f6975677654799d6876547a93537a9c777777', - logs: [], - requires: [ - { ip: 28, line: 8 }, - { ip: 34, line: 10 }, - { ip: 43, line: 13 }, - ], - sourceMap: '3:16:3:21;::::1;4:12:4:13:0;:16::17;;:12:::1;5::5:13:0;:17::18;;:21::22;:17:::1;:12;:24:9:9:0;6:20:6:21;:24::25;;:20:::1;7:16:7:17:0;;:20::21;:16:::1;:12::22;;;8:20:8:25:0;::::1;:12::27;5:24:9:9;9:15:11::0;10:20:10:21;:25::26;;:12::28:1;9:15:11:9;12:12:12:13:0;:16::17;;:12:::1;13:21:13:22:0;;:8::24:1;2:33:14:5;;', - sourceTags: '27:27:sc;41:43:sc', - }, - source: fs.readFileSync(new URL('../valid-contract-files/if_statement.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: 'b5f6c8b6bd5a7e4bfa2a596b5639fac59338e9cbf93673abc8c32fd4565f2846', - }, - }, - { - fn: 'multifunction.cash', - artifact: { - contractName: 'MultiFunction', - constructorInputs: [{ name: 'sender', type: 'pubkey' }, { name: 'recipient', type: 'pubkey' }, { name: 'timeout', type: 'int' }], - abi: [ - { name: 'transfer', inputs: [{ name: 'recipientSig', type: 'sig' }] }, - { name: 'timeout', inputs: [{ name: 'senderSig', type: 'sig' }] }, - ], - bytecode: - // function transfer - 'OP_3 OP_PICK OP_0 OP_NUMEQUAL OP_IF ' - // require(checkSig(recipientSig, recipient)) - + 'OP_4 OP_ROLL OP_ROT OP_CHECKSIG ' - + 'OP_NIP OP_NIP OP_NIP OP_ELSE ' - // function timeout - + 'OP_3 OP_ROLL OP_1 OP_NUMEQUALVERIFY ' - // require(checkSig(senderSig, sender)) - + 'OP_3 OP_ROLL OP_SWAP OP_CHECKSIGVERIFY ' - // require(tx.time >= timeout) - + 'OP_SWAP OP_CHECKLOCKTIMEVERIFY OP_2DROP OP_1 ' - + 'OP_ENDIF', - debug: { - bytecode: '5379009c63547a7bac77777767537a519d537a7cad7cb16d5168', - logs: [], - requires: [ - { ip: 12, line: 7 }, - { ip: 23, line: 11 }, - { ip: 25, line: 12 }, - ], - sourceMap: '6:4:8:5;;;;;7:25:7:37;;:39::48;:8::51:1;6:40:8:5;;;:4;10::13::0;;;;11:25:11:34;;:36::42;:8::45:1;12:27:12:34:0;:8::36:1;10:36:13:5;;1:0:14:1', - sourceTags: '9:11:sc', - }, - source: fs.readFileSync(new URL('../valid-contract-files/multifunction.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '4367893ec13aecfe4624b6d5b12681b9782de30dd657d1313eed269faba937e4', - }, - }, - { - fn: 'multifunction_if_statements.cash', - artifact: { - contractName: 'MultiFunctionIfStatements', - constructorInputs: [{ name: 'x', type: 'int' }, { name: 'y', type: 'int' }], - abi: [ - { name: 'transfer', inputs: [{ name: 'a', type: 'int' }, { name: 'b', type: 'int' }] }, - { name: 'timeout', inputs: [{ name: 'b', type: 'int' }] }, - ], - bytecode: - // function transfer - 'OP_2 OP_PICK OP_0 OP_NUMEQUAL OP_IF ' - // int d = a + b - + 'OP_3 OP_PICK OP_5 OP_PICK OP_ADD ' - // d = d - a - + 'OP_DUP OP_5 OP_PICK OP_SUB ' - // if (d == x && bool(x)) { - + 'OP_DUP OP_3 OP_PICK OP_NUMEQUAL OP_3 OP_ROLL OP_0NOTEQUAL OP_BOOLAND OP_IF ' - // int c = d + b - + 'OP_DUP OP_6 OP_PICK OP_ADD ' - // d = a + c - + 'OP_5 OP_PICK OP_OVER OP_ADD OP_ROT OP_DROP OP_SWAP ' - // require(c > d) - + 'OP_2DUP OP_LESSTHAN OP_VERIFY ' - // } else { - + 'OP_DROP OP_ELSE ' - // d = a } - + 'OP_4 OP_PICK OP_NIP OP_ENDIF ' - // d = d + a - + 'OP_DUP OP_5 OP_ROLL OP_ADD ' - // require(d == y) - + 'OP_3 OP_ROLL OP_NUMEQUALVERIFY ' - + 'OP_2DROP OP_2DROP OP_1 OP_ELSE ' - // function timeout - + 'OP_ROT OP_1 OP_NUMEQUALVERIFY ' - // int d = b - + 'OP_2 OP_PICK ' - // d = d + 2 - + 'OP_DUP OP_2 OP_ADD ' - // if (d == x) { - + 'OP_DUP OP_3 OP_ROLL OP_NUMEQUAL OP_IF ' - // int c = d + b - + 'OP_DUP OP_4 OP_PICK OP_ADD ' - // d = c + d - + 'OP_2DUP OP_ADD OP_ROT OP_DROP OP_SWAP ' - // require(c > d) } - + 'OP_2DUP OP_LESSTHAN OP_VERIFY ' - + 'OP_DROP OP_ENDIF ' - // d = b - + '' - // require(d == y) - + 'OP_2SWAP OP_NUMEQUAL ' - + 'OP_NIP OP_NIP OP_ENDIF', - debug: { - bytecode: '5279009c635379557993765579947653799c537a929a6376567993557978937b757c6e9f6975675479776876557a93537a9d6d6d51677b519d527976529376537a9c63765479936e937b757c6e9f697568729c777768', - logs: [], - requires: [ - { ip: 38, line: 8 }, - { ip: 51, line: 13 }, - { ip: 80, line: 22 }, - { ip: 85, line: 25 }, - ], - sourceMap: '2:4:14:5;;;;;3:16:3:17;;:20::21;;:16:::1;4:12:4:13:0;:16::17;;:12:::1;5::5:13:0;:17::18;;:12:::1;:27::28:0;;:22::29:1;:12;:31:9:9:0;6:20:6:21;:24::25;;:20:::1;7:16:7:17:0;;:20::21;:16:::1;:12::22;;;8:20:8:25:0;::::1;:12::27;5:31:9:9;9:15:11::0;10:16:10:17;;:12::18:1;9:15:11:9;12:12:12:13:0;:16::17;;:12:::1;13:21:13:22:0;;:8::24:1;2:36:14:5;;;:4;16::26::0;;;17:16:17:17;;18:12:18:13;:16::17;:12:::1;19::19:13:0;:17::18;;:12:::1;:20:23:9:0;20::20:21;:24::25;;:20:::1;21:16:21:21:0;::::1;:12::22;;;22:20:22:25:0;::::1;:12::27;19:20:23:9;;24:12:25:22:0;25:8::24:1;16:28:26:5;;1:0:27:1', - sourceTags: '37:37:sc;79:79:sc;83:84:sc', - }, - source: fs.readFileSync(new URL('../valid-contract-files/multifunction_if_statements.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: 'f57b39c9be272a31d1aedd678087b7dd327095fde647570149d237fb34401469', - }, - }, - { - fn: '2_of_3_multisig.cash', - artifact: { - contractName: 'MultiSig', - constructorInputs: [{ name: 'pk1', type: 'pubkey' }, { name: 'pk2', type: 'pubkey' }, { name: 'pk3', type: 'pubkey' }], - abi: [{ name: 'spend', inputs: [{ name: 's1', type: 'sig' }, { name: 's2', type: 'sig' }] }], - bytecode: - // require(checkMultiSig([s1, s2], [pk1, pk2, pk3])) - 'OP_0 OP_2ROT OP_SWAP OP_2 OP_2ROT OP_SWAP OP_6 OP_ROLL OP_3 OP_CHECKMULTISIG', - debug: { - bytecode: '00717c52717c567a53ae', - logs: [], - requires: [{ ip: 13, line: 3 }], - sourceMap: '3:12:3:52;:27::33;;:26::34:1;:37::45:0;;:47::50;;:36::51:1;:4::54', - }, - source: fs.readFileSync(new URL('../valid-contract-files/2_of_3_multisig.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: 'd06a42627510906395fa927195c1aa420b9adf7da407d71b793fc5d550b57a39', - }, - }, - { - fn: 'split_size.cash', - artifact: { - contractName: 'SplitSize', - constructorInputs: [{ name: 'b', type: 'bytes' }], - abi: [{ name: 'spend', inputs: [] }], - bytecode: - // bytes x = b.split(b.length / 2)[1] - 'OP_DUP OP_DUP OP_SIZE OP_NIP OP_2 OP_DIV OP_SPLIT OP_NIP ' - // require(x != b) - + 'OP_2DUP OP_EQUAL OP_NOT OP_VERIFY ' - // bytes x = b.split(b.length / 2)[1] - + 'OP_SWAP OP_4 OP_SPLIT OP_DROP OP_EQUAL OP_NOT', - debug: { - bytecode: '7676827752967f776e8791697c547f758791', - logs: [], - requires: [ - { ip: 12, line: 4 }, - { ip: 19, line: 5 }, - ], - sourceMap: '3:18:3:27;;:26::34:1;;:37::38:0;:26:::1;:18::39;:::42;4:16:4:22:0;::::1;;:8::24;5:16:5:17:0;:24::25;:16::26:1;:::29;:::34;:8::36', - }, - source: fs.readFileSync(new URL('../valid-contract-files/split_size.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '49d50376d8aa76534f29c049b987238d577ce332202a2f0da0cb74d94a027b79', - }, - }, - { - fn: 'cast_hash_checksig.cash', - artifact: { - contractName: 'CastHashChecksig', - constructorInputs: [], - abi: [{ name: 'hello', inputs: [{ name: 'pk', type: 'pubkey' }, { name: 's', type: 'sig' }] }], - bytecode: - // require((ripemd160(bytes(pk)) == hash160(0x0) == !true)); - 'OP_DUP OP_RIPEMD160 OP_0 OP_HASH160 OP_EQUAL OP_1 OP_NOT OP_NUMEQUALVERIFY ' - // require(checkSig(s, pk)); - + 'OP_CHECKSIG', - debug: { - bytecode: '76a600a98751919dac', - logs: [], - requires: [ - { ip: 7, line: 3 }, - { ip: 9, line: 4 }, - ], - sourceMap: '3:33:3:35;:17::37:1;:49::51:0;:41::52:1;:17;:57::61:0;:56:::1;:8::64;4::4:33', - }, - source: fs.readFileSync(new URL('../valid-contract-files/cast_hash_checksig.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: 'bbf25f5a4cfbc9380707afca6d1f14a29c7971f589949d284859225508f4bad6', - }, - }, - { - fn: 'hodl_vault.cash', - artifact: { - contractName: 'HodlVault', - constructorInputs: [ - { name: 'ownerPk', type: 'pubkey' }, - { name: 'oraclePk', type: 'pubkey' }, - { name: 'minBlock', type: 'int' }, - { name: 'priceTarget', type: 'int' }, - ], - abi: [ - { - name: 'spend', - inputs: [ - { name: 'ownerSig', type: 'sig' }, - { name: 'oracleSig', type: 'datasig' }, - { name: 'oracleMessage', type: 'bytes8' }, - ], - }, - ], - bytecode: - // Implicit type enforcement for oracleMessage: require(oracleMessage.length == 8) - 'OP_6 OP_ROLL OP_SIZE OP_8 OP_EQUALVERIFY ' - // bytes4 blockHeightBin, bytes4 priceBin = oracleMessage.split(4); - + 'OP_DUP OP_4 OP_SPLIT ' - // int blockHeight = int(blockHeightBin); - + 'OP_SWAP OP_BIN2NUM ' - // int price = int(priceBin); - + 'OP_SWAP OP_BIN2NUM ' - // require(blockHeight >= minBlock); - + 'OP_OVER OP_6 OP_ROLL OP_GREATERTHANOREQUAL OP_VERIFY ' - // require(tx.time >= blockHeight); - + 'OP_SWAP OP_CHECKLOCKTIMEVERIFY OP_DROP ' - // require(price >= priceTarget); - + 'OP_4 OP_ROLL OP_GREATERTHANOREQUAL OP_VERIFY ' - // require(checkDataSig(oracleSig, oracleMessage, oraclePk)); - + 'OP_4 OP_ROLL OP_SWAP OP_3 OP_ROLL OP_CHECKDATASIGVERIFY ' - // require(checkSig(ownerSig, ownerPk)); - + 'OP_CHECKSIG', - debug: { - bytecode: '567a82588876547f7c817c8178567aa2697cb175547aa269547a7c537abbac', - logs: [], - requires: [ - { ip: 20, line: 23 }, - { ip: 22, line: 24 }, - { ip: 27, line: 27 }, - { ip: 33, line: 30 }, - { ip: 35, line: 35 }, - ], - sourceMap: '15:8:15:28;;;;;18:49:18:62;:69::70;:49::71:1;19:30:19:44:0;:26::45:1;20:24:20:32:0;:20::33:1;23:16:23:27:0;:31::39;;:16:::1;:8::41;24:27:24:38:0;:8::40:1;;27:25:27:36:0;;:16:::1;:8::38;31:12:31:21:0;;32::32:25;33::33:20;;30:8:34:11:1;35::35:45', - sourceTags: '0:4:pv', - }, - source: fs.readFileSync(new URL('../valid-contract-files/hodl_vault.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: 'd87449bc71344f12ed3c9ce3f69f844cd1699df29553ec3884c1fcc92a2cfccf', - }, - }, - { - fn: 'deep_replace.cash', - artifact: { - contractName: 'DeepReplace', - constructorInputs: [], - abi: [{ name: 'hello', inputs: [] }], - bytecode: - // int a = 1; int b = 2; int c = 3; int d = 4; int e = 5; int f = 6; - 'OP_1 OP_2 OP_3 OP_4 OP_5 OP_6 ' - // if (a < 3) { - + 'OP_5 OP_PICK OP_3 OP_LESSTHAN OP_IF ' - // a = 3 } - + 'OP_3 OP_6 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP ' - + 'OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK ' - + 'OP_FROMALTSTACK OP_FROMALTSTACK OP_ENDIF ' - // require(a > b + c + d + e + f); - + 'OP_2ROT OP_5 OP_ROLL OP_ADD OP_4 OP_ROLL OP_ADD ' - + 'OP_3 OP_ROLL OP_ADD OP_ROT OP_ADD OP_GREATERTHAN', - debug: { - bytecode: '5152535455565579539f6353567a757c6b7c6b7c6b7c6b7c6c6c6c6c6871557a93547a93537a937b93a0', - logs: [], - requires: [{ ip: 42, line: 14 }], - sourceMap: '3:16:3:17;4::4;5::5;6::6;7::7;8::8;10:12:10:13;;:16::17;:12:::1;:19:12:9:0;11:16:11:17;:12::18:1;;;;;;;;;;;;;;;;10:19:12:9;14:16:14:21:0;:24::25;;:20:::1;:28::29:0;;:20:::1;:32::33:0;;:20:::1;:36::37:0;:20:::1;:8::39', - }, - source: fs.readFileSync(new URL('../valid-contract-files/deep_replace.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '5ec5f2a100dc5c29c95f58ed51e4da469b74e5932b070e38f3c30c3405f40005', - }, - }, - { - fn: 'bounded_bytes.cash', - artifact: { - contractName: 'BoundedBytes', - constructorInputs: [], - abi: [{ name: 'spend', inputs: [{ name: 'b', type: 'bytes4' }, { name: 'i', type: 'int' }] }], - bytecode: - // Implicit type enforcement for b: require(b.length == 4) - 'OP_SIZE OP_4 OP_EQUALVERIFY ' - // require(b == toPaddedBytes(i, 4)) - + 'OP_SWAP OP_4 OP_NUM2BIN OP_EQUAL', - debug: { - bytecode: '8254887c548087', - logs: [], - requires: [{ ip: 7, line: 3 }], - sourceMap: '2:19:2:27;;;3:35:3:36;:38::39;:21::40:1;:8::42', - sourceTags: '0:2:pv', - }, - source: fs.readFileSync(new URL('../valid-contract-files/bounded_bytes.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: 'b6381ead9be56fd0f9aa43b986ae8c3ba6aae2276596cdbd8268628397391064', - }, - }, - { - fn: 'covenant.cash', - artifact: { - contractName: 'Covenant', - constructorInputs: [ - { - name: 'requiredVersion', - type: 'int', - }, - ], - abi: [{ name: 'spend', inputs: [] }], - bytecode: - // require(tx.version == requiredVersion) - 'OP_TXVERSION OP_NUMEQUALVERIFY ' - // require(tx.bytecode == 0x00) - + 'OP_ACTIVEBYTECODE 00 OP_EQUAL', - debug: { - bytecode: 'c29dc1010087', - logs: [], - requires: [ - { ip: 2, line: 3 }, - { ip: 6, line: 4 }, - ], - sourceMap: '3:16:3:26;:8::47:1;4:16:4:35:0;:39::43;:8::45:1', - }, - source: fs.readFileSync(new URL('../valid-contract-files/covenant.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '1fd180f7d78e9670d7b2ae95e7af1f7cc533fc42c3cfdc7872619ec5810487d2', - }, - }, - { - fn: 'covenant_all_fields.cash', - artifact: { - contractName: 'Covenant', - constructorInputs: [], - abi: [{ name: 'spend', inputs: [] }], - bytecode: - // injected by InjectLocktimeGuardTraversal because tx.locktime is used - 'OP_TXLOCKTIME OP_CHECKLOCKTIMEVERIFY OP_DROP ' - // require(tx.version == 2) - + 'OP_TXVERSION OP_2 OP_NUMEQUALVERIFY ' - // require(tx.locktime == 0) - + 'OP_TXLOCKTIME OP_0 OP_NUMEQUALVERIFY ' - // require(tx.inputs.length == 1) - + 'OP_TXINPUTCOUNT OP_1 OP_NUMEQUALVERIFY ' - // require(tx.outputs.length == 1) - + 'OP_TXOUTPUTCOUNT OP_1 OP_NUMEQUALVERIFY ' - // require(this.activeInputIndex == 0) - + 'OP_INPUTINDEX OP_0 OP_NUMEQUALVERIFY ' - // require(this.activeBytecode.length == 300) - + 'OP_ACTIVEBYTECODE OP_SIZE OP_NIP 2c01 OP_NUMEQUALVERIFY ' - // require(tx.inputs[0].value == 10000) - + 'OP_0 OP_UTXOVALUE 1027 OP_NUMEQUALVERIFY ' - // require(tx.inputs[0].lockingBytecode.length == 10000) - + 'OP_0 OP_UTXOBYTECODE OP_SIZE OP_NIP 1027 OP_NUMEQUALVERIFY ' - // require(tx.inputs[0].outpointTransactionHash == 0x00...00) - + 'OP_0 OP_OUTPOINTTXHASH 0000000000000000000000000000000000000000000000000000000000000000 OP_EQUALVERIFY ' - // require(tx.inputs[0].outpointIndex == 0) - + 'OP_0 OP_OUTPOINTINDEX OP_0 OP_NUMEQUALVERIFY ' - // require(tx.inputs[0].unlockingBytecode.length == 100) - + 'OP_0 OP_INPUTBYTECODE OP_SIZE OP_NIP 64 OP_NUMEQUALVERIFY ' - // require(tx.inputs[0].sequenceNumber == 0) - + 'OP_0 OP_INPUTSEQUENCENUMBER OP_0 OP_NUMEQUALVERIFY ' - // require(tx.outputs[0].value == 10000) - + 'OP_0 OP_OUTPUTVALUE 1027 OP_NUMEQUALVERIFY ' - // require(tx.outputs[0].lockingBytecode.length == 100) - + 'OP_0 OP_OUTPUTBYTECODE OP_SIZE OP_NIP 64 OP_NUMEQUALVERIFY ' - // require(tx.inputs[0].tokenCategory == 0x000000000000000000000000000000000000000000000000000000000000000) - + 'OP_0 OP_UTXOTOKENCATEGORY 0000000000000000000000000000000000000000000000000000000000000000 OP_EQUALVERIFY ' - // require(tx.inputs[0].nftCommitment == 0x00); - + 'OP_0 OP_UTXOTOKENCOMMITMENT 00 OP_EQUALVERIFY ' - // require(tx.inputs[0].tokenAmount == 100); - + 'OP_0 OP_UTXOTOKENAMOUNT 64 OP_NUMEQUALVERIFY ' - // require(tx.outputs[0].tokenCategory == 0x000000000000000000000000000000000000000000000000000000000000000) - + 'OP_0 OP_OUTPUTTOKENCATEGORY 0000000000000000000000000000000000000000000000000000000000000000 OP_EQUALVERIFY ' - // require(tx.outputs[0].nftCommitment == 0x00); - + 'OP_0 OP_OUTPUTTOKENCOMMITMENT 00 OP_EQUALVERIFY ' - // require(tx.outputs[0].tokenAmount == 100); - + 'OP_0 OP_OUTPUTTOKENAMOUNT 64 OP_NUMEQUAL', - debug: { - bytecode: 'c5b175c2529dc5009dc3519dc4519dc0009dc18277022c019d00c60210279d00c782770210279d00c82000000000000000000000000000000000000000000000000000000000000000008800c9009d00ca827701649d00cb009d00cc0210279d00cd827701649d00ce2000000000000000000000000000000000000000000000000000000000000000008800cf01008800d001649d00d12000000000000000000000000000000000000000000000000000000000000000008800d201008800d301649c', - logs: [], - requires: [ - { - ip: 1, - line: 2, - message: 'Using tx.locktime requires a non-final sequence number on the spending input', - }, - { ip: 5, line: 3 }, - { ip: 8, line: 4 }, - { ip: 11, line: 5 }, - { ip: 14, line: 6 }, - { ip: 17, line: 7 }, - { ip: 22, line: 8 }, - { ip: 26, line: 9 }, - { ip: 32, line: 10 }, - { ip: 36, line: 11 }, - { ip: 40, line: 12 }, - { ip: 46, line: 13 }, - { ip: 50, line: 14 }, - { ip: 54, line: 15 }, - { ip: 60, line: 16 }, - { ip: 64, line: 17 }, - { ip: 68, line: 18 }, - { ip: 72, line: 19 }, - { ip: 76, line: 20 }, - { ip: 80, line: 21 }, - { ip: 85, line: 22 }, - ], - sourceMap: '2:21:2:21;::::1;;3:16:3:26:0;:30::31;:8::33:1;4:16:4:27:0;:31::32;:8::34:1;5:16:5:32:0;:36::37;:8::39:1;6:16:6:33:0;:37::38;:8::40:1;7:16:7:37:0;:41::42;:8::44:1;8:16:8:35:0;:::42:1;;:46::49:0;:8::51:1;9:26:9:27:0;:16::34:1;:38::43:0;:8::45:1;10:26:10:27:0;:16::44:1;:::51;;:55::60:0;:8::62:1;11:26:11:27:0;:16::52:1;:56::121:0;:8::123:1;12:26:12:27:0;:16::42:1;:46::47:0;:8::49:1;13:26:13:27:0;:16::46:1;:::53;;:57::60:0;:8::62:1;14:26:14:27:0;:16::43:1;:47::48:0;:8::50:1;15:27:15:28:0;:16::35:1;:39::44:0;:8::46:1;16:27:16:28:0;:16::45:1;:::52;;:56::59:0;:8::61:1;17:26:17:27:0;:16::42:1;:46::111:0;:8::113:1;18:26:18:27:0;:16::42:1;:46::50:0;:8::52:1;19:26:19:27:0;:16::40:1;:44::47:0;:8::49:1;20:27:20:28:0;:16::43:1;:47::112:0;:8::114:1;21:27:21:28:0;:16::43:1;:47::51:0;:8::53:1;22:27:22:28:0;:16::41:1;:45::48:0;:8::50:1', - sourceTags: '0:2:lg', - }, - source: fs.readFileSync(new URL('../valid-contract-files/covenant_all_fields.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '371d30dbd28672395a164baee67b27ad86454fa53daccdb7a770a7916902f607', - }, - }, - { - fn: 'mecenas.cash', - artifact: { - contractName: 'Mecenas', - constructorInputs: [ - { name: 'recipient', type: 'bytes20' }, - { name: 'funder', type: 'bytes20' }, - { name: 'pledge', type: 'int' }, - { name: 'period', type: 'int' }, - ], - abi: [ - { name: 'receive', inputs: [] }, - { name: 'reclaim', inputs: [{ name: 'pk', type: 'pubkey' }, { name: 's', type: 'sig' }] }, - ], - bytecode: - // function receive - 'OP_4 OP_PICK OP_0 OP_NUMEQUAL OP_IF ' - // require(this.age >= period) - + 'OP_3 OP_ROLL OP_CHECKSEQUENCEVERIFY OP_DROP ' - // require(tx.inputs.length == 1) - + 'OP_TXINPUTCOUNT OP_1 OP_NUMEQUALVERIFY ' - // require(tx.outputs[0].lockingBytecode == new LockingBytecodeP2PKH(recipient)) - + 'OP_0 OP_OUTPUTBYTECODE 76a914 OP_ROT OP_CAT 88ac OP_CAT OP_EQUALVERIFY ' - // int minerFee = 1000 - + 'e803 ' - // int currentValue = tx.inputs[this.activeInputIndex].value - + 'OP_INPUTINDEX OP_UTXOVALUE ' - // int changeValue = currentValue - pledge - minerFee - + 'OP_DUP OP_4 OP_PICK OP_SUB OP_2 OP_PICK OP_SUB ' - // if (changeValue <= pledge + minerFee) { - + 'OP_DUP OP_5 OP_PICK OP_4 OP_PICK OP_ADD OP_LESSTHANOREQUAL OP_IF ' - // require(tx.outputs[0].value == currentValue - minerFee) - + 'OP_0 OP_OUTPUTVALUE OP_2OVER OP_SWAP OP_SUB OP_NUMEQUALVERIFY ' - // } else { - + 'OP_ELSE ' - // require(tx.outputs[0].value == pledge) - + 'OP_0 OP_OUTPUTVALUE OP_5 OP_PICK OP_NUMEQUALVERIFY ' - // require( - // tx.outputs[1].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode - // ) - + 'OP_1 OP_OUTPUTBYTECODE OP_INPUTINDEX OP_UTXOBYTECODE OP_EQUALVERIFY ' - // require(tx.outputs[1].value == changeValue) } - + 'OP_1 OP_OUTPUTVALUE OP_OVER OP_NUMEQUALVERIFY ' - // Cleanup - + 'OP_ENDIF OP_2DROP OP_2DROP OP_2DROP OP_1 OP_ELSE ' - // function reclaim - + 'OP_4 OP_ROLL OP_1 OP_NUMEQUALVERIFY ' - // require(hash160(pk) == funder) - + 'OP_4 OP_PICK OP_HASH160 OP_ROT OP_EQUALVERIFY ' - // require(checkSig(s, pk)) - + 'OP_4 OP_ROLL OP_4 OP_ROLL OP_CHECKSIG ' - // Cleanup - + 'OP_NIP OP_NIP OP_NIP OP_ENDIF', - debug: { - bytecode: '5479009c63537ab275c3519d00cd0376a9147b7e0288ac7e8802e803c0c676547994527994765579547993a16300cc707c949d6700cc55799d51cdc0c78851cc789d686d6d6d5167547a519d5479a97b88547a547aac77777768', - logs: [], - requires: [ - { ip: 11, line: 3 }, - { ip: 15, line: 7 }, - { ip: 23, line: 10 }, - { ip: 47, line: 19 }, - { ip: 53, line: 21 }, - { ip: 58, line: 22 }, - { ip: 62, line: 23 }, - { ip: 77, line: 28 }, - { ip: 83, line: 29 }, - ], - sourceMap: '2:4:25:5;;;;;3:28:3:34;;:8::36:1;;7:16:7:32:0;:36::37;:8::39:1;10:27:10:28:0;:16::45:1;:49::84:0;:74::83;:49::84:1;;;:8::86;12:23:12:27:0;13:37:13:58;:27::65:1;14:26:14:38:0;:41::47;;:26:::1;:50::58:0;;:26:::1;18:12:18:23:0;:27::33;;:36::44;;:27:::1;:12;:46:20:9:0;19:31:19:32;:20::39:1;:43::66:0;;::::1;:12::68;20:15:24:9:0;21:31:21:32;:20::39:1;:43::49:0;;:12::51:1;22:31:22:32:0;:20::49:1;:63::84:0;:53::101:1;:12::103;23:31:23:32:0;:20::39:1;:43::54:0;:12::56:1;20:15:24:9;2:23:25:5;;;;:4;27::30::0;;;;28:24:28:26;;:16::27:1;:31::37:0;:8::39:1;29:25:29:26:0;;:28::30;;:8::33:1;27:39:30:5;;;1:0:31:1', - sourceTags: '79:81:sc', - }, - source: fs.readFileSync(new URL('../valid-contract-files/mecenas.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '82af4e70abe6257185f9fa2b9b65377949794a7f3f862a65eb3c61ec6bbff28a', - }, - }, - { - fn: 'announcement.cash', - artifact: { - contractName: 'Announcement', - constructorInputs: [], - abi: [{ name: 'announce', inputs: [] }], - bytecode: - // bytes announcement = new LockingBytecodeNullData(...) - '6a 6d02 OP_SIZE OP_SWAP OP_CAT OP_CAT ' - + '4120636f6e7472616374206d6179206e6f7420696e6a75726520612068756d616e20626' - + '5696e67206f722c207468726f75676820696e616374696f6e2c20616c6c6f77206120687' - + '56d616e206265696e6720746f20636f6d6520746f206861726d2e ' - + 'OP_SIZE OP_DUP 4b OP_GREATERTHAN OP_IF 4c OP_SWAP OP_CAT OP_ENDIF OP_SWAP OP_CAT OP_CAT ' - // require(tx.outputs[0].value == 0) - + 'OP_0 OP_OUTPUTVALUE OP_0 OP_NUMEQUALVERIFY ' - // require(tx.outputs[0].lockingBytecode == announcement) - + 'OP_0 OP_OUTPUTBYTECODE OP_EQUALVERIFY ' - // int minerFee = 1000 - + 'e803 ' - // int changeAmount = tx.inputs[this.activeInputIndex].value - minerFee - + 'OP_INPUTINDEX OP_UTXOVALUE OP_OVER OP_SUB ' - // if (changeAmount >= minerFee) - + 'OP_DUP OP_ROT OP_GREATERTHANOREQUAL OP_IF ' - // require( - // tx.outputs[1].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode - // ) - + 'OP_1 OP_OUTPUTBYTECODE OP_INPUTINDEX OP_UTXOBYTECODE OP_EQUALVERIFY ' - // require(tx.outputs[1].value == changeAmount) } - + 'OP_1 OP_OUTPUTVALUE OP_OVER OP_NUMEQUALVERIFY OP_ENDIF ' - // Stack clean-up - + 'OP_DROP OP_1', - debug: { - bytecode: '016a026d02827c7e7e4c624120636f6e7472616374206d6179206e6f7420696e6a75726520612068756d616e206265696e67206f722c207468726f75676820696e616374696f6e2c20616c6c6f7720612068756d616e206265696e6720746f20636f6d6520746f206861726d2e8276014ba063014c7c7e687c7e7e00cc009d00cd8802e803c0c67894767ba26351cdc0c78851cc789d687551', - logs: [], - requires: [ - { ip: 22, line: 16 }, - { ip: 25, line: 17 }, - { ip: 39, line: 24 }, - { ip: 43, line: 25 }, - ], - sourceMap: '10:29:13:10;11:12:11:18;::::1;;;;12:18:12:118:0;:12::119:1;;;;;;;;;;;;16:27:16:28:0;:16::35:1;:39::40:0;:8::42:1;17:27:17:28:0;:16::45:1;:8::63;21:23:21:27:0;22:37:22:58;:27::65:1;:68::76:0;:27:::1;23:12:23:24:0;:28::36;:12:::1;:38:26:9:0;24:31:24:32;:20::49:1;:63::84:0;:53::101:1;:12::103;25:31:25:32:0;:20::39:1;:43::55:0;:12::57:1;23:38:26:9;8:24:27:5;', - }, - source: fs.readFileSync(new URL('../valid-contract-files/announcement.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '542596767034cea0f3a5933a1efa49bb9aa569ec70c5f0261afd36ea845fa746', - }, - }, - { - fn: 'p2palindrome.cash', - artifact: { - contractName: 'P2Palindrome', - constructorInputs: [], - abi: [ - { name: 'spend', inputs: [{ name: 'palindrome', type: 'string' }] }, - ], - bytecode: 'OP_DUP OP_REVERSEBYTES OP_EQUAL', - debug: { - bytecode: '76bc87', - logs: [], - requires: [{ ip: 3, line: 3 }], - sourceMap: '3:16:3:26;:::36:1;:8::52', - }, - source: fs.readFileSync(new URL('../valid-contract-files/p2palindrome.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '4e9480ee14cf131a78be8da27e585e3d62c0e9cfa75d8338f2d51a67d84df0c7', - }, - }, - { - fn: 'num2bin_variable.cash', - artifact: { - contractName: 'Num2Bin', - constructorInputs: [], - abi: [ - { name: 'spend', inputs: [{ name: 'size', type: 'int' }] }, - ], - bytecode: 'OP_10 OP_SWAP OP_NUM2BIN OP_BIN2NUM OP_10 OP_NUMEQUAL', - debug: { - bytecode: '5a7c80815a9c', - logs: [], - requires: [{ ip: 6, line: 4 }], - sourceMap: '3:36:3:38;:40::44;:22::45:1;4:16:4:26;:30::32:0;:8::34:1', - }, - source: fs.readFileSync(new URL('../valid-contract-files/num2bin_variable.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '7ebb008689b080dce1061a508173a617c58b68202899cfd17a32f1a5decd4bb6', - }, - }, - { - fn: 'debug_messages.cash', - artifact: { - contractName: 'DebugMessages', - constructorInputs: [], - abi: [ - { name: 'spend', inputs: [{ name: 'value', type: 'int' }] }, - ], - bytecode: 'OP_DUP OP_1 OP_NUMEQUALVERIFY OP_1ADD OP_2 OP_NUMEQUAL', - debug: { - bytecode: '76519d8b529c', - logs: [ - { data: [{ stackIndex: 0, type: 'int', ip: 3 }, 'test'], ip: 3, line: 4 }, - { data: [{ stackIndex: 0, type: 'int', ip: 3 }, 'test2'], ip: 3, line: 5 }, - ], - requires: [{ ip: 2, line: 3, message: 'Wrong value passed' }, { ip: 6, line: 6, message: 'Sum doesn\'t work' }], - sourceMap: '3:12:3:17;:21::22;:4::46:1;6:12:6:21;:25::26:0;:4::48:1', - }, - source: fs.readFileSync(new URL('../valid-contract-files/debug_messages.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '2a4f5039ce0a481742e5b1712388a879ec8c5b617c4d4a8d676cc28d029c604f', - }, - }, - { - fn: 'integer_formatting.cash', - artifact: { - contractName: 'IntegerFormatting', - constructorInputs: [], - abi: [ - { name: 'test', inputs: [] }, - ], - bytecode: '0010a5d4e800 0010a5d4e800 0010a5d4e800 0010a5d4e800 0010a5d4e800 OP_4 OP_ROLL OP_OVER OP_NUMEQUALVERIFY OP_3 OP_ROLL OP_OVER OP_NUMEQUALVERIFY OP_ROT OP_OVER OP_NUMEQUALVERIFY OP_NUMEQUAL', - debug: { - bytecode: '060010a5d4e800060010a5d4e800060010a5d4e800060010a5d4e800060010a5d4e800547a789d537a789d7b789d9c', - logs: [], - requires: [{ ip: 8, line: 10 }, { ip: 12, line: 11 }, { ip: 15, line: 12 }, { ip: 17, line: 13 }], - sourceMap: '3:26:3:30;4::4;5::5:43;6:23:6:30;8:22:8:35;10:16:10:27;;:31::38;:8::40:1;11:16:11:27:0;;:31::38;:8::40:1;12:16:12:27:0;:31::38;:8::40:1;13::13:37', - }, - source: fs.readFileSync(new URL('../valid-contract-files/integer_formatting.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '0b4ca541ca3bfd698bda9798bc6ce5565513848fe325360248ec00266d84c874', - }, - }, - { - fn: 'multiline_statements.cash', - artifact: { - contractName: 'MultilineStatements', - constructorInputs: [{ name: 'x', type: 'int' }, { name: 'y', type: 'string' }], - abi: [{ name: 'spend', inputs: [{ name: 'a', type: 'int' }, { name: 'b', type: 'string' }] }], - bytecode: 'OP_ROT OP_SWAP OP_2 OP_SUB OP_NUMEQUAL OP_2 OP_PICK OP_2 OP_PICK OP_EQUAL OP_BOOLAND OP_IF OP_0 OP_VERIFY OP_ELSE OP_OVER 48656c6c6f20 OP_2 OP_PICK OP_CAT OP_EQUAL OP_IF OP_DUP 576f726c64 OP_EQUALVERIFY OP_ELSE OP_1 OP_0 OP_NOT OP_NOT OP_NOT OP_NUMEQUALVERIFY OP_ENDIF OP_ENDIF OP_2DROP OP_1', - debug: { - bytecode: '7b7c52949c52795279879a63006967780648656c6c6f2052797e87637605576f726c64886751009191919d68686d51', - logs: [], - requires: [ - { ip: 15, line: 11 }, - { ip: 26, line: 14 }, - { ip: 33, line: 18 }, - ], - sourceMap: '9:12:9:13;:17::18;:21::22;:17:::1;:12;10::10:13:0;;:17::18;;:12:::1;9;10:20:12:9:0;11::11:25;:12::27:1;12:15:19:9:0;:19:12:20;:24::32;13:10:13:11;;12:24:::1;:19;13:13:17:9:0;15:16:15:17;:21::28;14:12:16:14:1;17:15:19:9:0;18:20:18:24;:31::36;:30:::1;:29;:28;:12::38;17:15:19:9;12;8:6:20:5;', - }, - source: fs.readFileSync(new URL('../valid-contract-files/multiline_statements.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: 'd2aa37425c07883d3c7df823103f88a6a061d3efca1e6317b9e72e0ad4bd3611', - }, - }, - { - fn: 'log_intermediate_results.cash', - artifact: { - contractName: 'LogIntermediateResults', - constructorInputs: [{ name: 'owner', type: 'pubkey' }], - abi: [{ name: 'test_log_intermediate_result', inputs: [] }], - bytecode: 'OP_HASH256 OP_SIZE OP_NIP 20 OP_NUMEQUAL', - debug: { - bytecode: 'aa827701209c', - sourceMap: '3:29:5:47:1;6:16:6:33;;:37::39:0;:8::74:1', - logs: [ - { - ip: 1, - line: 4, - data: [ - { - stackIndex: 0, - type: 'bytes32', - ip: 1, - transformations: 'OP_SHA256', - }, - ], - }, - ], - requires: [ - { - ip: 6, - line: 6, - message: 'doubleHash should be 32 bytes', - }, - ], - }, - source: fs.readFileSync(new URL('../valid-contract-files/log_intermediate_results.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '47bb8f4ee4f62d7ecefc7f43fd7b61d2da71767df35d3dfd90149719287a0860', - }, - }, - { - fn: 'double_split.cash', - artifact: { - contractName: 'DoubleSplit', - constructorInputs: [{ name: 'pkh', type: 'bytes20' }], - abi: [{ name: 'spend', inputs: [] }], - bytecode: 'OP_INPUTINDEX OP_UTXOBYTECODE 17 OP_SPLIT OP_DROP OP_3 OP_SPLIT OP_NIP OP_EQUAL', - debug: { - bytecode: 'c0c701177f75537f7787', - sourceMap: '3:36:3:57;:26::74:1;:81::83:0;:26::84:1;:::87;:94::95:0;:26::96:1;:::99;4:8:4:34', - logs: [], - requires: [ - { - ip: 10, - line: 4, - }, - ], - }, - source: fs.readFileSync(new URL('../valid-contract-files/double_split.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: 'd8574d80ab674df33841526cf2767c09a9dc02d2faea91746465e22e3b81ae3a', - }, - }, - { - fn: 'slice.cash', - artifact: { - contractName: 'Slice', - constructorInputs: [{ name: 'pkh', type: 'bytes20' }], - abi: [{ name: 'spend', inputs: [] }], - bytecode: 'OP_INPUTINDEX OP_UTXOBYTECODE 17 OP_SPLIT OP_DROP OP_3 OP_SPLIT OP_NIP OP_EQUAL', - debug: { - bytecode: 'c0c701177f75537f7787', - sourceMap: '3:36:3:57;:26::74:1;:84::86:0;:26::87:1;;:81::82:0;:26::87:1;;4:8:4:34', - logs: [], - requires: [ - { - ip: 10, - line: 4, - message: undefined, - }, - ], - }, - source: fs.readFileSync(new URL('../valid-contract-files/slice.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: 'd8574d80ab674df33841526cf2767c09a9dc02d2faea91746465e22e3b81ae3a', - }, - }, - { - fn: 'slice_optimised.cash', - artifact: { - contractName: 'Slice', - constructorInputs: [{ name: 'data', type: 'bytes32' }], - abi: [{ name: 'spend', inputs: [] }], - bytecode: '14 OP_SPLIT OP_DROP OP_0 14 OP_NUM2BIN OP_EQUAL', - debug: { - bytecode: '01147f750001148087', - sourceMap: '3:36:3:38;:22::39:1;;4:37:4:38:0;:40::42;:23::43:1;:8::45', - logs: [], - requires: [ - { - ip: 8, - line: 4, - message: undefined, - }, - ], - }, - source: fs.readFileSync(new URL('../valid-contract-files/slice_optimised.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '779b8278e2831727686ad5c96b85bb01bab5f2692edd34d703b9d437da77ac03', - }, - }, - { - fn: 'while_loop_basic.cash', - artifact: { - contractName: 'WhileLoopBasic', - constructorInputs: [], - abi: [{ name: 'spend', inputs: [] }], - bytecode: 'OP_0 OP_BEGIN OP_DUP OP_3 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_DUP OP_1ADD OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_3 OP_NUMEQUAL', - debug: { - bytecode: '006576539f766b63768b77686c9166539c', - sourceMap: '3:16:3:17;5:8:7:9;:15:5:16;:19::20;:15:::1;;;:22:7:9:0;6:16:6:17;:::21:1;:12::22;5:22:7:9;;:8;;9:21:9:22:0;:8::24:1', - logs: [], - requires: [ - { ip: 17, line: 9 }, - ], - sourceTags: '11:14:lc', - }, - source: fs.readFileSync(new URL('../valid-contract-files/while_loop_basic.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '5a456e72142ae6beb6f64a3af7edfe9e14295b17724c4dfec0d84940c545d457', - }, - }, - { - fn: 'for_loop_basic.cash', - artifact: { - contractName: 'ForLoopBasic', - constructorInputs: [], - abi: [{ name: 'spend', inputs: [] }], - bytecode: 'OP_0 OP_0 OP_BEGIN OP_DUP OP_3 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_2DUP OP_ADD OP_ROT OP_DROP OP_SWAP OP_DUP OP_1ADD OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_3 OP_NUMEQUAL', - debug: { - bytecode: '00006576539f766b636e937b757c768b77686c916675539c', - sourceMap: '3:18:3:19;5:21:5:22;:8:7:9;:24:5:25;:28::29;:24:::1;;;:36:7:9:0;6:18:6:25;::::1;:12::26;;;5:31:5:32:0;:::34:1;;:36:7:9;;:8;;;9:23:9:24:0;:8::26:1', - logs: [], - requires: [ - { ip: 24, line: 9 }, - ], - sourceTags: '14:16:fu;17:20:lc;21:21:sc', - }, - source: fs.readFileSync(new URL('../valid-contract-files/for_loop_basic.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '9e52dba656b0743057d4eda76f051c8a1f4460a2becf8f372c690b1901512b4e', - }, - }, - { - fn: 'for_loop_stack_items.cash', - artifact: { - contractName: 'ForLoopBasic', - constructorInputs: [], - abi: [{ name: 'spend', inputs: [] }], - bytecode: 'OP_0 OP_1 OP_1 OP_0 OP_BEGIN OP_DUP OP_3 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_3 OP_PICK OP_OVER OP_ADD OP_4 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_DUP OP_1ADD OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_ROT OP_3 OP_NUMEQUALVERIFY OP_SWAP OP_1 OP_NUMEQUALVERIFY OP_1 OP_NUMEQUAL', - debug: { - bytecode: '005151006576539f766b6353797893547a757c6b7c6b7c6c6c768b77686c9166757b539d7c519d519c', - sourceMap: '3:18:3:19;4:16:4:17;5::5;7:21:7:22;:8:9:9;:24:7:25;:28::29;:24:::1;;;:39:9:9:0;8:18:8:21;;:24::25;:18:::1;:12::26;;;;;;;;;;7:31:7:32:0;:::37:1;;:39:9:9;;:8;;;11:16:11:19:0;:23::24;:8::26:1;12:16:12:17:0;:21::22;:8::24:1;13:21:13:22:0;:8::24:1', - logs: [], - requires: [ - { ip: 35, line: 11 }, - { ip: 38, line: 12 }, - { ip: 41, line: 13 }, - ], - sourceTags: '25:27:fu;28:31:lc;32:32:sc', - }, - source: fs.readFileSync(new URL('../valid-contract-files/for_loop_stack_items.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '05430d75d110ff5d4525f7e34bbf2c86b8d22f4e5758f2e109180b801b21b8ec', - }, - }, - { - fn: 'for_while_nested.cash', - artifact: { - contractName: 'ForWhileNested', - constructorInputs: [], - abi: [{ name: 'spend', inputs: [] }], - bytecode: 'OP_0 OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_2 OP_PICK OP_2 OP_PICK OP_ADD OP_OVER OP_ADD OP_3 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_DUP OP_1ADD OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_OVER OP_1ADD OP_ROT OP_DROP OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_4 OP_NUMEQUAL', - debug: { - bytecode: '00006576529f766b63006576529f766b6352795279937893537a757c6b7c6c768b77686c9166788b7b7577686c916675549c', - sourceMap: '3:18:3:19;5:21:5:22;:8:13:9;:24:5:25;:28::29;:24:::1;;;:42:13:9:0;6:20:6:21;8:12:12:13;:19:8:20;:23::24;:19:::1;;;:26:12:13:0;9:22:9:25;;:28::29;;:22:::1;:32::33:0;:22:::1;:16::34;;;;;;;10:20:10:21:0;:::25:1;:16::26;8:26:12:13;;:12;;5:35:5:36:0;:::40:1;:31;;::13:9;:42;;:8;;;15:23:15:24:0;:8::26:1', - logs: [ - { - ip: 34, - line: 11, - data: [ - 'sum:', - { stackIndex: 2, type: 'int', ip: 34 }, - 'i:', - { stackIndex: 1, type: 'int', ip: 34 }, - 'j:', - { stackIndex: 0, type: 'int', ip: 34 }, - ], - }, - ], - requires: [ - { ip: 50, line: 15 }, - ], - sourceTags: '34:37:lc;38:42:fu;43:46:lc;47:47:sc', - }, - source: fs.readFileSync(new URL('../valid-contract-files/for_while_nested.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '9eb2ec48e103cb2b0d75a326b533756d8f5edb49b2ea43a5578f5ceebde8c2ce', - }, - }, - { - fn: 'while_loop.cash', - artifact: { - contractName: 'Loopy', - constructorInputs: [], - abi: [{ name: 'doLoop', inputs: [] }], - bytecode: 'OP_0 OP_BEGIN OP_DUP OP_TXINPUTCOUNT OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_DUP OP_1ADD OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_2 OP_GREATERTHAN', - debug: { - bytecode: '006576c39f766b63768b77686c916652a0', - sourceMap: '3:16:3:17;5:8:7:9;:15:5:16;:19::35;:15:::1;;;:37:7:9:0;6:16:6:17;:::21:1;:12::22;5:37:7:9;;:8;;10:20:10:21:0;:8::23:1', - logs: [ - { ip: 15, line: 9, data: [{ stackIndex: 0, type: 'int', ip: 15 }] }, - ], - requires: [ - { ip: 17, line: 10 }, - ], - sourceTags: '11:14:lc', - }, - source: fs.readFileSync(new URL('../valid-contract-files/while_loop.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '00fc9253e439b8a2f39ba60d1d173ff4d19f557802b40b750eb6df5f92b1001e', - }, - }, - { - fn: 'complex_loop.cash', - artifact: { - contractName: 'Loopy', - constructorInputs: [], - abi: [{ name: 'doLoop', inputs: [] }], - bytecode: 'OP_0 OP_0 OP_0 OP_0 OP_BEGIN OP_DUP OP_UTXOVALUE OP_4 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_DUP OP_1ADD OP_NIP OP_TXOUTPUTCOUNT OP_2DUP OP_LESSTHAN OP_DUP OP_IF OP_2 OP_PICK OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUAL OP_NOT OP_NIP OP_DUP OP_IF OP_4 OP_PICK OP_3 OP_PICK OP_OUTPUTVALUE OP_ADD OP_5 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_ELSE OP_3 OP_PICK OP_1ADD OP_4 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_ENDIF OP_ENDIF OP_2DROP OP_DUP OP_TXINPUTCOUNT OP_GREATERTHANOREQUAL OP_UNTIL OP_2SWAP OP_GREATERTHAN OP_VERIFY OP_SWAP OP_0 OP_GREATERTHAN OP_NIP', - debug: { - bytecode: '000000006576c6547a757c6b7c6b7c6c6c768b77c46e9f76635279d100879177766354795379cc93557a757c6b7c6b7c6b7c6c6c6c6753798b547a757c6b7c6b7c6c6c68686d76c3a26672a0697c00a077', - sourceMap: '3:23:3:24;4:24:4:25;5:25:5:26;6:16:6:17;8:8:26:39;9:33:9:34;:23::41:1;:12::42;;;;;;;;;;10:16:10:17:0;:::21:1;:12::22;12:20:12:37:0;13:21:13:26;::::1;15:16:15:17:0;:19:25:13;16:31:16:32;;:20::47:1;:51::53:0;:20:::1;;:16::54;18:20:18:21:0;:23:20:17;19:32:19:41;;:55::56;;:44::63:1;:32;:20::64;;;;;;;;;;;;;20:23:22:17:0;21:33:21:43;;:::47:1;:20::48;;;;;;;;;;20:23:22:17;15:19:25:13;8:11:26:9;26:17::18:0;:21::37;8:8::39:1;;28:16:28:36:0;::::1;:8::38;29:16:29:26:0;:29::30;:8::32:1;2:22:30:5', - logs: [ - { ip: 68, line: 24, data: [{ stackIndex: 3, type: 'int', ip: 68 }] }, - ], - requires: [ - { ip: 76, line: 28 }, - { ip: 80, line: 29 }, - ], - sourceTags: '69:69:sc;80:80:sc', - }, - source: fs.readFileSync(new URL('../valid-contract-files/complex_loop.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: 'fa36d9b24d49525e027ba3cf3accd349e9f84d0c1bbd1141e98d6894173a1a2a', - }, - }, - { - fn: 'do_while_loop_no_introspection.cash', - artifact: { - contractName: 'Loopy', - constructorInputs: [], - abi: [{ name: 'doLoop', inputs: [] }], - bytecode: 'OP_0 OP_2 OP_BEGIN OP_OVER OP_1ADD OP_ROT OP_DROP OP_SWAP OP_2DUP OP_ADD OP_10 OP_LESSTHAN OP_VERIFY OP_OVER OP_10 OP_GREATERTHANOREQUAL OP_UNTIL OP_2DROP OP_1', - debug: { - bytecode: '005265788b7b757c6e935a9f69785aa2666d51', - sourceMap: '3:16:3:17;4::4;6:8:10:25;7:16:7:17;:::21:1;:12::22;;;9:20:9:25:0;::::1;:28::30:0;:20:::1;:12::32;10:17:10:18:0;:21::23;6:8::25:1;;2:22:11:5;', - logs: [ - { ip: 8, line: 8, data: [{ stackIndex: 1, type: 'int', ip: 8 }] }, - ], - requires: [ - { ip: 12, line: 9 }, - ], - }, - source: fs.readFileSync(new URL('../valid-contract-files/do_while_loop_no_introspection.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '39b67bb19440620390b83e3bc1e638ea6827998f4a14c2d6e29c2afa9815b2fd', - }, - }, - { - fn: 'bitshift.cash', - artifact: { - contractName: 'Bitshift', - constructorInputs: [], - abi: [{ name: 'spend', inputs: [] }], - bytecode: '1122334455667788 OP_4 OP_LSHIFTBIN OP_4 OP_RSHIFTBIN 0000000055667788 OP_EQUALVERIFY OP_8 OP_2 OP_RSHIFTNUM OP_1 OP_LSHIFTNUM OP_4 OP_NUMEQUAL', - debug: { - bytecode: '081122334455667788549854990800000000556677888858528e518d549c', - sourceMap: '3:19:3:37;4:24:4:25;:19:::1;:29::30:0;:19:::1;6:21:6:39:0;:8::41:1;8:16:8:17:0;9:21:9:22;:16:::1;:26::27:0;:16:::1;11:22:11:23:0;:8::25:1', - logs: [], - requires: [ - { ip: 6, line: 6 }, - { ip: 14, line: 11 }, - ], - }, - source: fs.readFileSync(new URL('../valid-contract-files/bitshift.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '2c9d2ddcd53c6f4e28320acf336260ece7958753802381a2528ddfa1840eb88a', - }, - }, - { - fn: 'type_enforcement.cash', - artifact: { - contractName: 'TypeEnforcement', - constructorInputs: [], - abi: [{ - name: 'spend', inputs: [ - { name: 'nonEnforcedInt', type: 'int' }, - { name: 'enforcedBool', type: 'bool' }, - { name: 'enforcedBytes', type: 'bytes4' }, - { name: 'nonEnforcedBytes', type: 'bytes' }, - ], - }], - bytecode: - // Implicit type enforcement for enforcedBool: enforcedBool = bool(enforcedBool) - 'OP_SWAP OP_0NOTEQUAL ' - // Implicit type enforcement for enforcedBytes: require(enforcedBytes.length == 4) - + 'OP_ROT OP_SIZE OP_4 OP_EQUALVERIFY ' - // if(enforcedBool == true) ) - + 'OP_OVER OP_1 OP_NUMEQUAL OP_IF ' - // require(nonEnforcedInt > 6) - + 'OP_2 OP_PICK OP_6 OP_GREATERTHAN OP_VERIFY ' - // Cleanup - + 'OP_ENDIF ' - // if(enforcedBool == false) { - + 'OP_SWAP OP_0 OP_NUMEQUAL OP_IF ' - // require(enforcedBytes == nonEnforcedBytes) - + 'OP_DUP OP_3 OP_PICK OP_EQUALVERIFY ' - // Cleanup - + 'OP_ENDIF OP_2DROP OP_DROP OP_1', - debug: { - bytecode: '7c927b82548878519c63527956a069687c009c6376537988686d7551', - sourceMap: '4:8:4:25;;5::5:28;;;;8:12:8:24;:28::32;:12:::1;:34:10:9:0;9:20:9:34;;:37::38;:20:::1;:12::40;8:34:10:9;12:12:12:24:0;:28::33;:12:::1;:35:14:9:0;13:20:13:33;:37::53;;:12::55:1;12:35:14:9;7:6:15:5;;', - sourceTags: '0:1:pv;2:5:pv', - logs: [], - requires: [ - { ip: 14, line: 9 }, - { ip: 23, line: 13 }, - ], - }, - source: fs.readFileSync(new URL('../valid-contract-files/type_enforcement.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: true, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: 'afc9b61abaaef4b60d435309b83b2cbd5750e2c1755ca903cfd3f438f7dc6126', - }, - }, - { - // Same as the fixture above, but with enforceFunctionParameterTypes disabled - fn: 'type_enforcement.cash', - compilerOptions: { - enforceFunctionParameterTypes: false, - }, - artifact: { - contractName: 'TypeEnforcement', - constructorInputs: [], - abi: [{ - name: 'spend', inputs: [ - { name: 'nonEnforcedInt', type: 'int' }, - { name: 'enforcedBool', type: 'bool' }, - { name: 'enforcedBytes', type: 'bytes4' }, - { name: 'nonEnforcedBytes', type: 'bytes' }, - ], - }], - bytecode: - // if(enforcedBool == true) - 'OP_OVER OP_1 OP_NUMEQUAL OP_IF ' - // require(nonEnforcedInt > 6) - + 'OP_DUP OP_6 OP_GREATERTHAN OP_VERIFY ' - // Cleanup - + 'OP_ENDIF ' - // if(enforcedBool == false) { - + 'OP_SWAP OP_0 OP_NUMEQUAL OP_IF ' - // require(enforcedBytes == nonEnforcedBytes) - + 'OP_OVER OP_3 OP_PICK OP_EQUALVERIFY ' - // Cleanup - + 'OP_ENDIF OP_2DROP OP_DROP OP_1', - debug: { - bytecode: '78519c637656a069687c009c6378537988686d7551', - sourceMap: '8:12:8:24;:28::32;:12:::1;:34:10:9:0;9:20:9:34;:37::38;:20:::1;:12::40;8:34:10:9;12:12:12:24:0;:28::33;:12:::1;:35:14:9:0;13:20:13:33;:37::53;;:12::55:1;12:35:14:9;7:6:15:5;;', - logs: [], - requires: [ - { ip: 7, line: 9 }, - { ip: 16, line: 13 }, - ], - }, - source: fs.readFileSync(new URL('../valid-contract-files/type_enforcement.cash', import.meta.url), { encoding: 'utf-8' }), - compiler: { - name: 'cashc', - version, - options: { - enforceFunctionParameterTypes: false, - enforceLocktimeGuard: true, - }, - }, - updatedAt: '', - fingerprint: '606e540c38f161868964b683aeb0ddf93094dc36607397ef9b9f507f9028bc37', - }, - }, -]; diff --git a/packages/cashc/test/generation/fixtures/import-fixtures/diamond.ts b/packages/cashc/test/generation/fixtures/import-fixtures/diamond.ts new file mode 100644 index 000000000..12d7dc67d --- /dev/null +++ b/packages/cashc/test/generation/fixtures/import-fixtures/diamond.ts @@ -0,0 +1,123 @@ +import fs from 'fs'; +import { URL } from 'url'; +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Diamond', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [{ name: 'x', type: 'int' }] }], + bytecode: + // require(m1(x) + m2(x) == 18); + 'OP_DUP OP_1ADD OP_2 OP_MUL OP_SWAP OP_1ADD OP_3 OP_ADD OP_ADD 12 OP_NUMEQUAL', + fingerprint: '0467d6dd3d6e156739eece6d6d2111816622b43d31469b7fe7cbbcc59272759b', + debug: { + bytecode: '768b52957c8b53939301129c', + sourceMap: '6:19:6:20;:16::21:1;;;:27::28:0;:24::29:1;;;:16;:33::35:0;:8::37:1', + logs: [], + requires: [{ ip: 11, line: 6 }], + functions: [ + { + name: 'leaf', + inputs: [{ name: 'a', type: 'int' }], + bytecode: '8b', + sourceMap: '2:11:2:16:1', + source: fs.readFileSync(new URL('../../../import-fixtures/leaf.cash', import.meta.url), { encoding: 'utf-8' }), + sourceFile: 'leaf.cash', + logs: [], + requires: [], + }, + { + name: 'm1', + inputs: [{ name: 'a', type: 'int' }], + bytecode: '8b5295', + sourceMap: '3:11:3:18:1;:21::22:0;:11:::1', + source: fs.readFileSync(new URL('../../../import-fixtures/mid1.cash', import.meta.url), { encoding: 'utf-8' }), + sourceFile: 'mid1.cash', + logs: [], + requires: [], + inlineRanges: '0:0:leaf', + }, + { + name: 'm2', + inputs: [{ name: 'a', type: 'int' }], + bytecode: '8b5393', + sourceMap: '3:11:3:18:1;:21::22:0;:11:::1', + source: fs.readFileSync(new URL('../../../import-fixtures/mid2.cash', import.meta.url), { encoding: 'utf-8' }), + sourceFile: 'mid2.cash', + logs: [], + requires: [], + inlineRanges: '0:0:leaf', + }, + ], + inlineRanges: '1:3:m1;5:7:m2', + }, + }, + }, + { + // Imports resolved across a diamond (mid1 and mid2 both import leaf): leaf is defined once, and + // m1/m2 invoke it transitively. + compilerOptions: { disableInlining: true }, + artifact: { + contractName: 'Diamond', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [{ name: 'x', type: 'int' }] }], + bytecode: + // Functions are defined in callee-first order (leaf before its callers), so leaf is id 0, + // m1 id 1, m2 id 2. + // OP_DEFINE leaf (id 0): return a + 1 + '8b OP_0 OP_DEFINE ' + // OP_DEFINE m1 (id 1): return leaf(a) * 2 + + '008a5295 OP_1 OP_DEFINE ' + // OP_DEFINE m2 (id 2): return leaf(a) + 3 + + '008a5393 OP_2 OP_DEFINE ' + // require(m1(x) + m2(x) == 18) + + 'OP_DUP OP_1 OP_INVOKE OP_SWAP OP_2 OP_INVOKE OP_ADD 12 OP_NUMEQUAL', + debug: { + bytecode: '018b008904008a5295518904008a5393528976518a7c528a9301129c', + logs: [], + requires: [ + { ip: 18, line: 6 }, + ], + sourceMap: '1::3:1;;::::1;2::4::0;;::::1;::::0;;::::1;6:19:6:20:0;:16::21:1;;:27::28:0;:24::29:1;;:16;:33::35:0;:8::37:1', + functions: [ + { + id: 0, + name: 'leaf', + inputs: [{ name: 'a', type: 'int' }], + bytecode: '8b', + sourceMap: '2:11:2:16:1', + logs: [], + requires: [], + source: fs.readFileSync(new URL('../../../import-fixtures/leaf.cash', import.meta.url), { encoding: 'utf-8' }), + sourceFile: 'leaf.cash', + }, + { + id: 1, + name: 'm1', + inputs: [{ name: 'a', type: 'int' }], + bytecode: '008a5295', + sourceMap: '3:11:3:18:1;;:21::22:0;:11:::1', + logs: [], + requires: [], + source: fs.readFileSync(new URL('../../../import-fixtures/mid1.cash', import.meta.url), { encoding: 'utf-8' }), + sourceFile: 'mid1.cash', + }, + { + id: 2, + name: 'm2', + inputs: [{ name: 'a', type: 'int' }], + bytecode: '008a5393', + sourceMap: '3:11:3:18:1;;:21::22:0;:11:::1', + logs: [], + requires: [], + source: fs.readFileSync(new URL('../../../import-fixtures/mid2.cash', import.meta.url), { encoding: 'utf-8' }), + sourceFile: 'mid2.cash', + }, + ], + }, + fingerprint: '316a3305152ec0695bf80303736c79dd1f9cc2f1dbccf57d9965094401363307', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/2_of_3_multisig.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/2_of_3_multisig.ts new file mode 100644 index 000000000..762f5e1cb --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/2_of_3_multisig.ts @@ -0,0 +1,21 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'MultiSig', + constructorInputs: [{ name: 'pk1', type: 'pubkey' }, { name: 'pk2', type: 'pubkey' }, { name: 'pk3', type: 'pubkey' }], + abi: [{ name: 'spend', inputs: [{ name: 's1', type: 'sig' }, { name: 's2', type: 'sig' }] }], + bytecode: + // require(checkMultiSig([s1, s2], [pk1, pk2, pk3])) + 'OP_0 OP_2ROT OP_SWAP OP_2 OP_2ROT OP_SWAP OP_6 OP_ROLL OP_3 OP_CHECKMULTISIG', + debug: { + bytecode: '00717c52717c567a53ae', + logs: [], + requires: [{ ip: 13, line: 3 }], + sourceMap: '3:12:3:52;:27::33;;:26::34:1;:37::45:0;;:47::50;;:36::51:1;:4::54', + }, + fingerprint: 'd06a42627510906395fa927195c1aa420b9adf7da407d71b793fc5d550b57a39', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/announcement.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/announcement.ts new file mode 100644 index 000000000..07ff49c81 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/announcement.ts @@ -0,0 +1,48 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Announcement', + constructorInputs: [], + abi: [{ name: 'announce', inputs: [] }], + bytecode: + // bytes announcement = new LockingBytecodeNullData(...) + '6a 6d02 OP_SIZE OP_SWAP OP_CAT OP_CAT ' + + '4120636f6e7472616374206d6179206e6f7420696e6a75726520612068756d616e20626' + + '5696e67206f722c207468726f75676820696e616374696f6e2c20616c6c6f77206120687' + + '56d616e206265696e6720746f20636f6d6520746f206861726d2e ' + + 'OP_SIZE OP_DUP 4b OP_GREATERTHAN OP_IF 4c OP_SWAP OP_CAT OP_ENDIF OP_SWAP OP_CAT OP_CAT ' + // require(tx.outputs[0].value == 0) + + 'OP_0 OP_OUTPUTVALUE OP_0 OP_NUMEQUALVERIFY ' + // require(tx.outputs[0].lockingBytecode == announcement) + + 'OP_0 OP_OUTPUTBYTECODE OP_EQUALVERIFY ' + // int minerFee = 1000 + + 'e803 ' + // int changeAmount = tx.inputs[this.activeInputIndex].value - minerFee + + 'OP_INPUTINDEX OP_UTXOVALUE OP_OVER OP_SUB ' + // if (changeAmount >= minerFee) + + 'OP_DUP OP_ROT OP_GREATERTHANOREQUAL OP_IF ' + // require( + // tx.outputs[1].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode + // ) + + 'OP_1 OP_OUTPUTBYTECODE OP_INPUTINDEX OP_UTXOBYTECODE OP_EQUALVERIFY ' + // require(tx.outputs[1].value == changeAmount) } + + 'OP_1 OP_OUTPUTVALUE OP_OVER OP_NUMEQUALVERIFY OP_ENDIF ' + // Stack clean-up + + 'OP_DROP OP_1', + debug: { + bytecode: '016a026d02827c7e7e4c624120636f6e7472616374206d6179206e6f7420696e6a75726520612068756d616e206265696e67206f722c207468726f75676820696e616374696f6e2c20616c6c6f7720612068756d616e206265696e6720746f20636f6d6520746f206861726d2e8276014ba063014c7c7e687c7e7e00cc009d00cd8802e803c0c67894767ba26351cdc0c78851cc789d687551', + logs: [], + requires: [ + { ip: 22, line: 16 }, + { ip: 25, line: 17 }, + { ip: 39, line: 24 }, + { ip: 43, line: 25 }, + ], + sourceMap: '10:29:13:10;11:12:11:18;::::1;;;;12:18:12:118:0;:12::119:1;;;;;;;;;;;;16:27:16:28:0;:16::35:1;:39::40:0;:8::42:1;17:27:17:28:0;:16::45:1;:8::63;21:23:21:27:0;22:37:22:58;:27::65:1;:68::76:0;:27:::1;23:12:23:24:0;:28::36;:12:::1;:38:26:9:0;24:31:24:32;:20::49:1;:63::84:0;:53::101:1;:12::103;25:31:25:32:0;:20::39:1;:43::55:0;:12::57:1;23:38:26:9;8:24:27:5;', + }, + fingerprint: '542596767034cea0f3a5933a1efa49bb9aa569ec70c5f0261afd36ea845fa746', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/bigint.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/bigint.ts new file mode 100644 index 000000000..9c102c926 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/bigint.ts @@ -0,0 +1,25 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'BigInt', + constructorInputs: [], + abi: [{ name: 'proofOfBigInt', inputs: [{ name: 'x', type: 'int' }, { name: 'y', type: 'int' }] }], + bytecode: + // int maxInt32PlusOne = 2147483648; + '0000008000 ' + // require(x >= maxInt32PlusOne); + + 'OP_2DUP OP_GREATERTHANOREQUAL OP_VERIFY ' + // require(x * y >= maxInt32PlusOne); + + 'OP_SWAP OP_ROT OP_MUL OP_LESSTHANOREQUAL', + fingerprint: 'f14ce38215a2c251b9851e2e8d60b1faf88093ace31621cca1d7cacbe38442a7', + debug: { + bytecode: '0500000080006ea2697c7b95a1', + sourceMap: '3:30:3:40;4:16:4:36;::::1;:8::38;5:16:5:17:0;:20::21;:16:::1;:8::42', + logs: [], + requires: [{ ip: 3, line: 4 }, { ip: 8, line: 5 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/bitshift.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/bitshift.ts new file mode 100644 index 000000000..fa6ac60e4 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/bitshift.ts @@ -0,0 +1,22 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Bitshift', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [] }], + bytecode: '1122334455667788 OP_4 OP_LSHIFTBIN OP_4 OP_RSHIFTBIN 0000000055667788 OP_EQUALVERIFY OP_8 OP_2 OP_RSHIFTNUM OP_1 OP_LSHIFTNUM OP_4 OP_NUMEQUAL', + debug: { + bytecode: '081122334455667788549854990800000000556677888858528e518d549c', + sourceMap: '3:19:3:37;4:24:4:25;:19:::1;:29::30:0;:19:::1;6:21:6:39:0;:8::41:1;8:16:8:17:0;9:21:9:22;:16:::1;:26::27:0;:16:::1;11:22:11:23:0;:8::25:1', + logs: [], + requires: [ + { ip: 6, line: 6 }, + { ip: 14, line: 11 }, + ], + }, + fingerprint: '2c9d2ddcd53c6f4e28320acf336260ece7958753802381a2528ddfa1840eb88a', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/bitwise.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/bitwise.ts new file mode 100644 index 000000000..086d8fb0c --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/bitwise.ts @@ -0,0 +1,16 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [{ name: 'x', type: 'bytes8' }, { name: 'y', type: 'bytes8' }], + abi: [{ name: 'hello', inputs: [] }], + bytecode: + // require((x | y) == y); + 'OP_OVER OP_OR OP_EQUAL', + fingerprint: '323eed626e96a40e7bad6fe48fe17304f30b8ca11760d9c10fdf8dfcffc819cc', + debug: { bytecode: '788587', sourceMap: '3:21:3:22;:17:::1;:8::30', logs: [], requires: [{ ip: 5, line: 3 }] }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/bounded_bytes.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/bounded_bytes.ts new file mode 100644 index 000000000..42bf3645d --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/bounded_bytes.ts @@ -0,0 +1,24 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'BoundedBytes', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [{ name: 'b', type: 'bytes4' }, { name: 'i', type: 'int' }] }], + bytecode: + // Implicit type enforcement for b: require(b.length == 4) + 'OP_SIZE OP_4 OP_EQUALVERIFY ' + // require(b == toPaddedBytes(i, 4)) + + 'OP_SWAP OP_4 OP_NUM2BIN OP_EQUAL', + debug: { + bytecode: '8254887c548087', + logs: [], + requires: [{ ip: 7, line: 3 }], + sourceMap: '2:19:2:27;;;3:35:3:36;:38::39;:21::40:1;:8::42', + sourceTags: '0:2:pv', + }, + fingerprint: 'b6381ead9be56fd0f9aa43b986ae8c3ba6aae2276596cdbd8268628397391064', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/bytes1_equals_byte.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/bytes1_equals_byte.ts new file mode 100644 index 000000000..b339802b5 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/bytes1_equals_byte.ts @@ -0,0 +1,26 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Bytes1EqualsByte', + constructorInputs: [], + abi: [{ name: 'hello', inputs: [{ name: 'a', type: 'int' }, { name: 'b', type: 'bytes1' }] }], + bytecode: + // Implicit parameter type enforcement + 'OP_SWAP OP_SIZE OP_1 OP_EQUALVERIFY ' + // bytes1 c = toPaddedBytes(a, 1); + + 'OP_SWAP OP_1 OP_NUM2BIN ' + // require(b == c); + + 'OP_EQUAL', + fingerprint: 'cbccef08f54b0452cabaf643501a4903dab79b0d676896399771131fea3a0ad1', + debug: { + bytecode: '7c8251887c518087', + sourceMap: '2:26:2:32;;;;3:33:3:34;:36::37;:19::38:1;4:8:4:24', + logs: [], + requires: [{ ip: 8, line: 4 }], + sourceTags: '0:3:pv', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/bytes_type_narrowing.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/bytes_type_narrowing.ts new file mode 100644 index 000000000..6d54d128a --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/bytes_type_narrowing.ts @@ -0,0 +1,152 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'BytesTypeNarrowing', + constructorInputs: [], + abi: [ + { name: 'requireNarrowing', inputs: [{ name: 'data', type: 'bytes' }] }, + { name: 'ifNarrowing', inputs: [{ name: 'data', type: 'bytes' }, { name: 'x', type: 'int' }] }, + { name: 'reversedComparison', inputs: [{ name: 'data', type: 'bytes' }] }, + { name: 'ifNarrowingAnd', inputs: [{ name: 'data', type: 'bytes' }, { name: 'x', type: 'int' }] }, + { + name: 'requireNarrowingMultiple', + inputs: [{ name: 'data1', type: 'bytes' }, { name: 'data2', type: 'bytes' }], + }, + { + name: 'ifNarrowingMultiple', + inputs: [{ name: 'data1', type: 'bytes' }, { name: 'data2', type: 'bytes' }, { name: 'x', type: 'int' }], + }, + ], + bytecode: + // function requireNarrowing(bytes data) { + 'OP_DUP OP_0 OP_NUMEQUAL OP_IF ' + // require(data.length == 20); + + 'OP_OVER OP_SIZE OP_NIP 14 OP_NUMEQUALVERIFY ' + // bytes20 narrowed = data; + + 'OP_OVER ' + // require(narrowed == data); + + 'OP_2 OP_PICK OP_EQUALVERIFY ' + // require(new LockingBytecodeP2SH20(data).length > 20); + + 'a914 OP_ROT OP_CAT 87 OP_CAT OP_SIZE OP_NIP 14 OP_GREATERTHAN ' + // Cleanup + + 'OP_NIP ' + // } + + 'OP_ELSE ' + // function ifNarrowing(bytes data, int x) { + + 'OP_DUP OP_1 OP_NUMEQUAL OP_IF ' + // if (data.length == 20) { + + 'OP_OVER OP_SIZE OP_NIP 14 OP_NUMEQUAL OP_IF ' + // bytes20 narrowed = data; + + 'OP_OVER ' + // require(narrowed == data); + + 'OP_DUP OP_3 OP_PICK OP_EQUALVERIFY ' + // Cleanup + + 'OP_DROP ' + // } + + 'OP_ENDIF ' + // require(x > 0); + + 'OP_ROT OP_0 OP_GREATERTHAN ' + // Cleanup + + 'OP_NIP OP_NIP ' + // } + + 'OP_ELSE ' + // function reversedComparison(bytes data) { + + 'OP_DUP OP_2 OP_NUMEQUAL OP_IF ' + // require(20 == data.length); + + '14 OP_2 OP_PICK OP_SIZE OP_NIP OP_NUMEQUALVERIFY ' + // bytes20 narrowed = data; + + 'OP_OVER ' + // require(narrowed == data); + + 'OP_ROT OP_EQUAL ' + // Cleanup + + 'OP_NIP ' + // } + + 'OP_ELSE ' + // function ifNarrowingAnd(bytes data, int x) { + + 'OP_DUP OP_3 OP_NUMEQUAL OP_IF ' + // if (data.length == 20 && x > 0) { + + 'OP_OVER OP_SIZE OP_NIP 14 OP_NUMEQUAL OP_3 OP_PICK OP_0 OP_GREATERTHAN OP_BOOLAND OP_IF ' + // bytes20 narrowed = data; + + 'OP_OVER ' + // require(narrowed == data); + + 'OP_DUP OP_3 OP_PICK OP_EQUALVERIFY ' + // Cleanup + + 'OP_DROP ' + // } + + 'OP_ENDIF ' + // require(x > 0); + + 'OP_ROT OP_0 OP_GREATERTHAN ' + // Cleanup + + 'OP_NIP OP_NIP ' + // } + + 'OP_ELSE ' + // function requireNarrowingMultiple(bytes data1, bytes data2) { + + 'OP_DUP OP_4 OP_NUMEQUAL OP_IF ' + // require(data1.length == 20 && data2.length == 10); + + 'OP_OVER OP_SIZE OP_NIP 14 OP_NUMEQUAL OP_3 OP_PICK OP_SIZE OP_NIP OP_10 OP_NUMEQUAL OP_BOOLAND OP_VERIFY ' + // bytes20 narrowed1 = data1; + + 'OP_OVER ' + // bytes10 narrowed2 = data2; + + 'OP_3 OP_PICK ' + // require(narrowed1 == data1); + + 'OP_SWAP OP_3 OP_PICK OP_EQUALVERIFY ' + // require(narrowed2 == data2); + + 'OP_3 OP_ROLL OP_EQUALVERIFY ' + // require(new LockingBytecodeP2SH20(data1).length > 20); + + 'a914 OP_ROT OP_CAT 87 OP_CAT OP_SIZE OP_NIP 14 OP_GREATERTHAN ' + // Cleanup + + 'OP_NIP ' + // } + + 'OP_ELSE ' + // function ifNarrowingMultiple(bytes data1, bytes data2, int x) { + + 'OP_5 OP_NUMEQUALVERIFY ' + // if (data1.length == 20 && data2.length == 10) { + + 'OP_DUP OP_SIZE OP_NIP 14 OP_NUMEQUAL OP_2 OP_PICK OP_SIZE OP_NIP OP_10 OP_NUMEQUAL OP_BOOLAND OP_IF ' + // bytes20 narrowed1 = data1; + + 'OP_DUP ' + // bytes10 narrowed2 = data2; + + 'OP_3DUP ' + // require(narrowed1 == data1); + + 'OP_EQUALVERIFY ' + // require(narrowed2 == data2); + + 'OP_DUP OP_4 OP_PICK OP_EQUALVERIFY ' + // Cleanup + + 'OP_2DROP ' + // } + + 'OP_ENDIF ' + // require(x > 0); + + 'OP_ROT OP_0 OP_GREATERTHAN ' + // Cleanup + + 'OP_NIP OP_NIP ' + // } + + 'OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF', + fingerprint: '6ce346c2b481330bafc5f23000655b8f033cd71635e01987a431885433b9de30', + debug: { + bytecode: '76009c6378827701149d7852798802a9147b7e01877e82770114a0776776519c6378827701149c63787653798875687b00a077776776529c630114527982779d787b87776776539c6378827701149c537900a09a63787653798875687b00a077776776549c6378827701149c537982775a9c9a697853797c537988537a8802a9147b7e01877e82770114a07767559d76827701149c527982775a9c9a63766f88765479886d687b00a077776868686868', + sourceMap: '2:4:7:5;;;;3:16:3:20;:::27:1;;:31::33:0;:8::35:1;4:27:4:31:0;5:28:5:32;;:8::34:1;6:16:6:47:0;:42::46;:16::47:1;;;:::54;;:57::59:0;:8::61:1;2:42:7:5;:4;9::15::0;;;;10:12:10:16;:::23:1;;:27::29:0;:12:::1;:31:13:9:0;11::11:35;12:20:12:28;:32::36;;:12::38:1;10:31:13:9;;14:16:14:17:0;:20::21;:8::23:1;9:44:15:5;;:4;17::21::0;;;;18:16:18:18;:22::26;;:::33:1;;:8::35;19:27:19:31:0;20:28:20:32;:8::34:1;17:44:21:5;:4;23::29::0;;;;24:12:24:16;:::23:1;;:27::29:0;:12:::1;:33::34:0;;:37::38;:33:::1;:12;:40:27:9:0;25:31:25:35;26:20:26:28;:32::36;;:12::38:1;24:40:27:9;;28:16:28:17:0;:20::21;:8::23:1;23:47:29:5;;:4;31::38::0;;;;32:16:32:21;:::28:1;;:32::34:0;:16:::1;:38::43:0;;:::50:1;;:54::56:0;:38:::1;:16;:8::58;33:28:33:33:0;34::34;;35:16:35:25;:29::34;;:8::36:1;36:29:36:34:0;;:8::36:1;37:16:37:48:0;:42::47;:16::48:1;;;:::55;;:58::60:0;:8::62:1;31:64:38:5;:4;40::48::0;;41:12:41:17;:::24:1;;:28::30:0;:12:::1;:34::39:0;;:::46:1;;:50::52:0;:34:::1;:12;:54:46:9:0;42:32:42:37;43::44:38;:::40:1;45:20:45:29:0;:33::38;;:12::40:1;41:54:46:9;;47:16:47:17:0;:20::21;:8::23:1;40:66:48:5;;1:0:49:1;;;;', + logs: [], + requires: [ + { ip: 8, line: 3 }, + { ip: 12, line: 5 }, + { ip: 22, line: 6 }, + { ip: 38, line: 12 }, + { ip: 44, line: 14 }, + { ip: 56, line: 18 }, + { ip: 60, line: 20 }, + { ip: 81, line: 26 }, + { ip: 87, line: 28 }, + { ip: 106, line: 32 }, + { ip: 113, line: 35 }, + { ip: 116, line: 36 }, + { ip: 126, line: 37 }, + { ip: 145, line: 44 }, + { ip: 149, line: 45 }, + { ip: 155, line: 47 }, + ], + sourceTags: '22:22:sc;39:39:sc;44:45:sc;60:60:sc;82:82:sc;87:88:sc;126:126:sc;150:150:sc;155:156:sc', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/cast_hash_checksig.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/cast_hash_checksig.ts new file mode 100644 index 000000000..6f5419a6d --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/cast_hash_checksig.ts @@ -0,0 +1,26 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'CastHashChecksig', + constructorInputs: [], + abi: [{ name: 'hello', inputs: [{ name: 'pk', type: 'pubkey' }, { name: 's', type: 'sig' }] }], + bytecode: + // require((ripemd160(bytes(pk)) == hash160(0x0) == !true)); + 'OP_DUP OP_RIPEMD160 OP_0 OP_HASH160 OP_EQUAL OP_1 OP_NOT OP_NUMEQUALVERIFY ' + // require(checkSig(s, pk)); + + 'OP_CHECKSIG', + debug: { + bytecode: '76a600a98751919dac', + logs: [], + requires: [ + { ip: 7, line: 3 }, + { ip: 9, line: 4 }, + ], + sourceMap: '3:33:3:35;:17::37:1;:49::51:0;:41::52:1;:17;:57::61:0;:56:::1;:8::64;4::4:33', + }, + fingerprint: 'bbf25f5a4cfbc9380707afca6d1f14a29c7971f589949d284859225508f4bad6', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/checkdatasig_in_function.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/checkdatasig_in_function.ts new file mode 100644 index 000000000..e7fd8a8d7 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/checkdatasig_in_function.ts @@ -0,0 +1,41 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [], + abi: [ + { + name: 'spend', + inputs: [{ name: 's', type: 'datasig' }, { name: 'message', type: 'bytes' }, { name: 'pk', type: 'pubkey' }], + }, + ], + bytecode: + // require(verifyData(s, message, pk)); + 'OP_SWAP OP_ROT OP_CHECKDATASIG', + fingerprint: '1a1dd563b4b81503ce471252e609976731a2946d4466485eac35594ff2cc47f8', + debug: { + bytecode: '7c7bba', + sourceMap: '7:30:7:37;:39::41;:8::44:1', + logs: [], + requires: [{ ip: 3, line: 7 }], + functions: [ + { + name: 'verifyData', + inputs: [ + { name: 's', type: 'datasig' }, + { name: 'message', type: 'bytes' }, + { name: 'pk', type: 'pubkey' }, + ], + bytecode: 'ba', + sourceMap: '2:11:2:39:1', + logs: [], + requires: [], + }, + ], + inlineRanges: '2:2:verifyData', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/comments.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/comments.ts new file mode 100644 index 000000000..135d2a764 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/comments.ts @@ -0,0 +1,39 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [{ name: 'x', type: 'int' }], + abi: [{ name: 'hello', inputs: [{ name: 's', type: 'sig' }, { name: 'pk', type: 'pubkey' }] }], + bytecode: + // int i = 400 + x; + '9001 OP_OVER OP_ADD ' + // bytes b = 0x07364897987fe87 + bytes(x); + + '07364897987fe807 OP_2 OP_PICK OP_CAT ' + // int myVariable = 10 - 4; // they can go at the end of the line + + 'OP_10 OP_4 OP_SUB ' + // int myOtherVariable = i + myVariable % 2; + + 'OP_2 OP_PICK OP_SWAP OP_2 OP_MOD OP_ADD ' + // require(myOtherVariable /* And comments can be included within lines */ > i); + + 'OP_2 OP_PICK OP_GREATERTHAN OP_VERIFY ' + // if (x > 10) { + + 'OP_ROT OP_10 OP_GREATERTHAN OP_IF ' + // require(i < 20); + + 'OP_OVER 14 OP_LESSTHAN OP_VERIFY ' + // require(checkSig(s, pk)); + + 'OP_2OVER OP_SWAP OP_CHECKSIGVERIFY ' + // require(b == 0x01); + + 'OP_ELSE OP_DUP OP_1 OP_EQUALVERIFY OP_ENDIF ' + // } + + 'OP_2DROP OP_2DROP OP_1', + fingerprint: '63ee25992293cf9dd8752923c17b8a78840ecea53d75e814643cbb2ac914c751', + debug: { + bytecode: '02900178930807364897987fe80752797e5a549452797c5297935279a0697b5aa0637801149f69707cad67765188686d6d51', + sourceMap: '9:16:9:19;:22::23;:16:::1;10:18:10:35:0;:44::45;;:18::46:1;12:25:12:27:0;:30::31;:25:::1;13:30:13::0;;:34::44;:47::48;:34:::1;:30;14:82:14:83:0;;:16:::1;:8::85;16:12:16:13:0;:16::18;:12:::1;:20:19:9:0;17::17:21;:24::26;:20:::1;:12::28;18:29:18:34:0;;:12::37:1;20::20:31:0;:20::21;:25::29;:12::31:1;;8:37:21:5;;', + logs: [], + requires: [{ ip: 20, line: 14 }, { ip: 28, line: 17 }, { ip: 31, line: 18 }, { ip: 35, line: 20 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/complex_loop.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/complex_loop.ts new file mode 100644 index 000000000..f362178e9 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/complex_loop.ts @@ -0,0 +1,25 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Loopy', + constructorInputs: [], + abi: [{ name: 'doLoop', inputs: [] }], + bytecode: 'OP_0 OP_0 OP_0 OP_0 OP_BEGIN OP_DUP OP_UTXOVALUE OP_4 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_DUP OP_1ADD OP_NIP OP_TXOUTPUTCOUNT OP_2DUP OP_LESSTHAN OP_DUP OP_IF OP_2 OP_PICK OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUAL OP_NOT OP_NIP OP_DUP OP_IF OP_4 OP_PICK OP_3 OP_PICK OP_OUTPUTVALUE OP_ADD OP_5 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_ELSE OP_3 OP_PICK OP_1ADD OP_4 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_ENDIF OP_ENDIF OP_2DROP OP_DUP OP_TXINPUTCOUNT OP_GREATERTHANOREQUAL OP_UNTIL OP_2SWAP OP_GREATERTHAN OP_VERIFY OP_SWAP OP_0 OP_GREATERTHAN OP_NIP', + debug: { + bytecode: '000000006576c6547a757c6b7c6b7c6c6c768b77c46e9f76635279d100879177766354795379cc93557a757c6b7c6b7c6b7c6c6c6c6753798b547a757c6b7c6b7c6c6c68686d76c3a26672a0697c00a077', + sourceMap: '3:23:3:24;4:24:4:25;5:25:5:26;6:16:6:17;8:8:26:39;9:33:9:34;:23::41:1;:12::42;;;;;;;;;;10:16:10:17:0;:::21:1;:12::22;12:20:12:37:0;13:21:13:26;::::1;15:16:15:17:0;:19:25:13;16:31:16:32;;:20::47:1;:51::53:0;:20:::1;;:16::54;18:20:18:21:0;:23:20:17;19:32:19:41;;:55::56;;:44::63:1;:32;:20::64;;;;;;;;;;;;;20:23:22:17:0;21:33:21:43;;:::47:1;:20::48;;;;;;;;;;20:23:22:17;15:19:25:13;8:11:26:9;26:17::18:0;:21::37;8:8::39:1;;28:16:28:36:0;::::1;:8::38;29:16:29:26:0;:29::30;:8::32:1;2:22:30:5', + logs: [ + { ip: 68, line: 24, data: [{ stackIndex: 3, type: 'int', ip: 68 }] }, + ], + requires: [ + { ip: 76, line: 28 }, + { ip: 80, line: 29 }, + ], + sourceTags: '69:69:sc;80:80:sc', + }, + fingerprint: 'fa36d9b24d49525e027ba3cf3accd349e9f84d0c1bbd1141e98d6894173a1a2a', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/compound_assign.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/compound_assign.ts new file mode 100644 index 000000000..d48952c3b --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/compound_assign.ts @@ -0,0 +1,32 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'CompoundAssign', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [] }], + bytecode: + // int x = 10; + 'OP_10 ' + // x += 5; + + 'OP_DUP OP_5 OP_ADD ' + // require(x == 15); + + 'OP_DUP OP_15 OP_NUMEQUALVERIFY ' + // x -= 3; + + 'OP_DUP OP_3 OP_SUB ' + // require(x == 12); + + 'OP_12 OP_NUMEQUAL ' + // Cleanup + + 'OP_NIP OP_NIP', + fingerprint: '3e67ebf63dbbf278589d0fffe8dd94663758334a8bf7858ea9c3613cd472b190', + debug: { + bytecode: '5a765593765f9d7653945c9c7777', + sourceMap: '3:16:3:18;4:8:4:9;:13::14;:8:::1;5:16:5:17:0;:21::23;:8::25:1;7::7:9:0;:13::14;:8:::1;8:21:8:23:0;:8::25:1;2:21:9:5;', + logs: [], + requires: [{ ip: 6, line: 5 }, { ip: 12, line: 8 }], + sourceTags: '12:13:sc', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/correct_pragma.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/correct_pragma.ts new file mode 100644 index 000000000..6120ac887 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/correct_pragma.ts @@ -0,0 +1,37 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [], + abi: [ + { name: 'hello', inputs: [{ name: 's', type: 'sig' }, { name: 'pk', type: 'pubkey' }] }, + { name: 'world', inputs: [{ name: 'a', type: 'int' }] }, + ], + bytecode: + // function hello(sig s, pubkey pk) { + 'OP_DUP OP_0 OP_NUMEQUAL OP_IF ' + // require(checkSig(s, pk)); + + 'OP_SWAP OP_ROT OP_CHECKSIG ' + // Cleanup + + 'OP_NIP ' + // } + + 'OP_ELSE ' + // function world(int a) { + + 'OP_1 OP_NUMEQUALVERIFY ' + // require(a + 5 == 10); + + 'OP_5 OP_ADD OP_10 OP_NUMEQUAL ' + // } + + 'OP_ENDIF', + fingerprint: '88a901256f34c8c655657f1efad4389dc3465a1980090f817f5d1a7df7ffd701', + debug: { + bytecode: '76009c637c7bac7767519d55935a9c68', + sourceMap: '4:4:6:5;;;;5:25:5:26;:28::30;:8::33:1;4:37:6:5;:4;8::10::0;;9:20:9:21;:16:::1;:25::27:0;:8::29:1;3:0:11:1', + logs: [], + requires: [{ ip: 7, line: 5 }, { ip: 15, line: 9 }], + sourceTags: '7:7:sc', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/covenant.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/covenant.ts new file mode 100644 index 000000000..a47946740 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/covenant.ts @@ -0,0 +1,31 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Covenant', + constructorInputs: [ + { + name: 'requiredVersion', + type: 'int', + }, + ], + abi: [{ name: 'spend', inputs: [] }], + bytecode: + // require(tx.version == requiredVersion) + 'OP_TXVERSION OP_NUMEQUALVERIFY ' + // require(tx.bytecode == 0x00) + + 'OP_ACTIVEBYTECODE 00 OP_EQUAL', + debug: { + bytecode: 'c29dc1010087', + logs: [], + requires: [ + { ip: 2, line: 3 }, + { ip: 6, line: 4 }, + ], + sourceMap: '3:16:3:26;:8::47:1;4:16:4:35:0;:39::43;:8::45:1', + }, + fingerprint: '1fd180f7d78e9670d7b2ae95e7af1f7cc533fc42c3cfdc7872619ec5810487d2', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/covenant_all_fields.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/covenant_all_fields.ts new file mode 100644 index 000000000..6b920f1da --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/covenant_all_fields.ts @@ -0,0 +1,88 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Covenant', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [] }], + bytecode: + // injected by InjectLocktimeGuardTraversal because tx.locktime is used + 'OP_TXLOCKTIME OP_CHECKLOCKTIMEVERIFY OP_DROP ' + // require(tx.version == 2) + + 'OP_TXVERSION OP_2 OP_NUMEQUALVERIFY ' + // require(tx.locktime == 0) + + 'OP_TXLOCKTIME OP_0 OP_NUMEQUALVERIFY ' + // require(tx.inputs.length == 1) + + 'OP_TXINPUTCOUNT OP_1 OP_NUMEQUALVERIFY ' + // require(tx.outputs.length == 1) + + 'OP_TXOUTPUTCOUNT OP_1 OP_NUMEQUALVERIFY ' + // require(this.activeInputIndex == 0) + + 'OP_INPUTINDEX OP_0 OP_NUMEQUALVERIFY ' + // require(this.activeBytecode.length == 300) + + 'OP_ACTIVEBYTECODE OP_SIZE OP_NIP 2c01 OP_NUMEQUALVERIFY ' + // require(tx.inputs[0].value == 10000) + + 'OP_0 OP_UTXOVALUE 1027 OP_NUMEQUALVERIFY ' + // require(tx.inputs[0].lockingBytecode.length == 10000) + + 'OP_0 OP_UTXOBYTECODE OP_SIZE OP_NIP 1027 OP_NUMEQUALVERIFY ' + // require(tx.inputs[0].outpointTransactionHash == 0x00...00) + + 'OP_0 OP_OUTPOINTTXHASH 0000000000000000000000000000000000000000000000000000000000000000 OP_EQUALVERIFY ' + // require(tx.inputs[0].outpointIndex == 0) + + 'OP_0 OP_OUTPOINTINDEX OP_0 OP_NUMEQUALVERIFY ' + // require(tx.inputs[0].unlockingBytecode.length == 100) + + 'OP_0 OP_INPUTBYTECODE OP_SIZE OP_NIP 64 OP_NUMEQUALVERIFY ' + // require(tx.inputs[0].sequenceNumber == 0) + + 'OP_0 OP_INPUTSEQUENCENUMBER OP_0 OP_NUMEQUALVERIFY ' + // require(tx.outputs[0].value == 10000) + + 'OP_0 OP_OUTPUTVALUE 1027 OP_NUMEQUALVERIFY ' + // require(tx.outputs[0].lockingBytecode.length == 100) + + 'OP_0 OP_OUTPUTBYTECODE OP_SIZE OP_NIP 64 OP_NUMEQUALVERIFY ' + // require(tx.inputs[0].tokenCategory == 0x000000000000000000000000000000000000000000000000000000000000000) + + 'OP_0 OP_UTXOTOKENCATEGORY 0000000000000000000000000000000000000000000000000000000000000000 OP_EQUALVERIFY ' + // require(tx.inputs[0].nftCommitment == 0x00); + + 'OP_0 OP_UTXOTOKENCOMMITMENT 00 OP_EQUALVERIFY ' + // require(tx.inputs[0].tokenAmount == 100); + + 'OP_0 OP_UTXOTOKENAMOUNT 64 OP_NUMEQUALVERIFY ' + // require(tx.outputs[0].tokenCategory == 0x000000000000000000000000000000000000000000000000000000000000000) + + 'OP_0 OP_OUTPUTTOKENCATEGORY 0000000000000000000000000000000000000000000000000000000000000000 OP_EQUALVERIFY ' + // require(tx.outputs[0].nftCommitment == 0x00); + + 'OP_0 OP_OUTPUTTOKENCOMMITMENT 00 OP_EQUALVERIFY ' + // require(tx.outputs[0].tokenAmount == 100); + + 'OP_0 OP_OUTPUTTOKENAMOUNT 64 OP_NUMEQUAL', + debug: { + bytecode: 'c5b175c2529dc5009dc3519dc4519dc0009dc18277022c019d00c60210279d00c782770210279d00c82000000000000000000000000000000000000000000000000000000000000000008800c9009d00ca827701649d00cb009d00cc0210279d00cd827701649d00ce2000000000000000000000000000000000000000000000000000000000000000008800cf01008800d001649d00d12000000000000000000000000000000000000000000000000000000000000000008800d201008800d301649c', + logs: [], + requires: [ + { + ip: 1, + line: 2, + message: 'Using tx.locktime requires a non-final sequence number on the spending input', + }, + { ip: 5, line: 3 }, + { ip: 8, line: 4 }, + { ip: 11, line: 5 }, + { ip: 14, line: 6 }, + { ip: 17, line: 7 }, + { ip: 22, line: 8 }, + { ip: 26, line: 9 }, + { ip: 32, line: 10 }, + { ip: 36, line: 11 }, + { ip: 40, line: 12 }, + { ip: 46, line: 13 }, + { ip: 50, line: 14 }, + { ip: 54, line: 15 }, + { ip: 60, line: 16 }, + { ip: 64, line: 17 }, + { ip: 68, line: 18 }, + { ip: 72, line: 19 }, + { ip: 76, line: 20 }, + { ip: 80, line: 21 }, + { ip: 85, line: 22 }, + ], + sourceMap: '2:21:2:21;::::1;;3:16:3:26:0;:30::31;:8::33:1;4:16:4:27:0;:31::32;:8::34:1;5:16:5:32:0;:36::37;:8::39:1;6:16:6:33:0;:37::38;:8::40:1;7:16:7:37:0;:41::42;:8::44:1;8:16:8:35:0;:::42:1;;:46::49:0;:8::51:1;9:26:9:27:0;:16::34:1;:38::43:0;:8::45:1;10:26:10:27:0;:16::44:1;:::51;;:55::60:0;:8::62:1;11:26:11:27:0;:16::52:1;:56::121:0;:8::123:1;12:26:12:27:0;:16::42:1;:46::47:0;:8::49:1;13:26:13:27:0;:16::46:1;:::53;;:57::60:0;:8::62:1;14:26:14:27:0;:16::43:1;:47::48:0;:8::50:1;15:27:15:28:0;:16::35:1;:39::44:0;:8::46:1;16:27:16:28:0;:16::45:1;:::52;;:56::59:0;:8::61:1;17:26:17:27:0;:16::42:1;:46::111:0;:8::113:1;18:26:18:27:0;:16::42:1;:46::50:0;:8::52:1;19:26:19:27:0;:16::40:1;:44::47:0;:8::49:1;20:27:20:28:0;:16::43:1;:47::112:0;:8::114:1;21:27:21:28:0;:16::43:1;:47::51:0;:8::53:1;22:27:22:28:0;:16::41:1;:45::48:0;:8::50:1', + sourceTags: '0:2:lg', + }, + fingerprint: '371d30dbd28672395a164baee67b27ad86454fa53daccdb7a770a7916902f607', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/date_literal.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/date_literal.ts new file mode 100644 index 000000000..1cf348e3a --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/date_literal.ts @@ -0,0 +1,23 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [], + abi: [{ name: 'test', inputs: [] }], + bytecode: + // int d = date("2021-02-17T01:30:00"); //YYYY-MM-DDThh:mm:ss + '98712c60 ' + // require(d == 0); + + 'OP_0 OP_NUMEQUAL', + fingerprint: '3584793ef9b31561ca87330a348b586c6352c8fa413985079b7d8ca3aca5bdf0', + debug: { + bytecode: '0498712c60009c', + sourceMap: '5:16:5:43;6:21:6:22;:8::24:1', + logs: [], + requires: [{ ip: 3, line: 6 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/debug_messages.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/debug_messages.ts new file mode 100644 index 000000000..d86000b10 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/debug_messages.ts @@ -0,0 +1,24 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'DebugMessages', + constructorInputs: [], + abi: [ + { name: 'spend', inputs: [{ name: 'value', type: 'int' }] }, + ], + bytecode: 'OP_DUP OP_1 OP_NUMEQUALVERIFY OP_1ADD OP_2 OP_NUMEQUAL', + debug: { + bytecode: '76519d8b529c', + logs: [ + { data: [{ stackIndex: 0, type: 'int', ip: 3 }, 'test'], ip: 3, line: 4 }, + { data: [{ stackIndex: 0, type: 'int', ip: 3 }, 'test2'], ip: 3, line: 5 }, + ], + requires: [{ ip: 2, line: 3, message: 'Wrong value passed' }, { ip: 6, line: 6, message: 'Sum doesn\'t work' }], + sourceMap: '3:12:3:17;:21::22;:4::46:1;6:12:6:21;:25::26:0;:4::48:1', + }, + fingerprint: '2a4f5039ce0a481742e5b1712388a879ec8c5b617c4d4a8d676cc28d029c604f', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/deep_replace.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/deep_replace.ts new file mode 100644 index 000000000..ef8985c15 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/deep_replace.ts @@ -0,0 +1,30 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'DeepReplace', + constructorInputs: [], + abi: [{ name: 'hello', inputs: [] }], + bytecode: + // int a = 1; int b = 2; int c = 3; int d = 4; int e = 5; int f = 6; + 'OP_1 OP_2 OP_3 OP_4 OP_5 OP_6 ' + // if (a < 3) { + + 'OP_5 OP_PICK OP_3 OP_LESSTHAN OP_IF ' + // a = 3 } + + 'OP_3 OP_6 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP ' + + 'OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK ' + + 'OP_FROMALTSTACK OP_FROMALTSTACK OP_ENDIF ' + // require(a > b + c + d + e + f); + + 'OP_2ROT OP_5 OP_ROLL OP_ADD OP_4 OP_ROLL OP_ADD ' + + 'OP_3 OP_ROLL OP_ADD OP_ROT OP_ADD OP_GREATERTHAN', + debug: { + bytecode: '5152535455565579539f6353567a757c6b7c6b7c6b7c6b7c6c6c6c6c6871557a93547a93537a937b93a0', + logs: [], + requires: [{ ip: 42, line: 14 }], + sourceMap: '3:16:3:17;4::4;5::5;6::6;7::7;8::8;10:12:10:13;;:16::17;:12:::1;:19:12:9:0;11:16:11:17;:12::18:1;;;;;;;;;;;;;;;;10:19:12:9;14:16:14:21:0;:24::25;;:20:::1;:28::29:0;;:20:::1;:32::33:0;;:20:::1;:36::37:0;:20:::1;:8::39', + }, + fingerprint: '5ec5f2a100dc5c29c95f58ed51e4da469b74e5932b070e38f3c30c3405f40005', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/deeply_nested-logs.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/deeply_nested-logs.ts new file mode 100644 index 000000000..2c099e5fd --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/deeply_nested-logs.ts @@ -0,0 +1,79 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'TransferWithTimeout', + constructorInputs: [ + { name: 'sender', type: 'pubkey' }, + { name: 'recipient', type: 'pubkey' }, + { name: 'timeout', type: 'int' }, + ], + abi: [ + { name: 'transfer', inputs: [{ name: 'recipientSig', type: 'sig' }] }, + { name: 'timeout', inputs: [{ name: 'senderSig', type: 'sig' }] }, + ], + bytecode: + // function transfer(sig recipientSig) { + 'OP_3 OP_PICK OP_0 OP_NUMEQUAL OP_IF ' + // require(checkSig(recipientSig, recipient)); + + 'OP_4 OP_ROLL OP_ROT OP_CHECKSIG ' + // Cleanup + + 'OP_NIP OP_NIP OP_NIP ' + // } + + 'OP_ELSE ' + // function timeout(sig senderSig) { + + 'OP_3 OP_ROLL OP_1 OP_NUMEQUALVERIFY ' + // require(checkSig(senderSig, sender)); + + 'OP_3 OP_ROLL OP_SWAP OP_CHECKSIGVERIFY ' + // if (timeout > 0) { + + 'OP_OVER OP_0 OP_GREATERTHAN OP_IF ' + // if (timeout < 10) { + + 'OP_OVER OP_10 OP_LESSTHAN OP_IF ' + // require(timeout == 5); + + 'OP_OVER OP_5 OP_NUMEQUALVERIFY ' + // } else { + + 'OP_ELSE ' + // require(timeout == 15); + + 'OP_OVER OP_15 OP_NUMEQUALVERIFY ' + // } + + 'OP_ENDIF ' + // } else { + + 'OP_ELSE ' + // require(timeout == 0); + + 'OP_OVER OP_0 OP_NUMEQUALVERIFY ' + // } + + 'OP_ENDIF OP_2DROP OP_1 OP_ENDIF', + fingerprint: '8c0754826497834b06b82f8e040f49613e1bc193b8b1bb8ab171f8538ddfb47e', + debug: { + bytecode: '5379009c63547a7bac77777767537a519d537a7cad7800a063785a9f6378559d67785f9d686778009d686d5168', + sourceMap: '9:4:12:5;;;;;10:25:10:37;;:39::48;:8::51:1;9:40:12:5;;;:4;15::32::0;;;;16:25:16:34;;:36::42;:8::45:1;17:12:17:19:0;:22::23;:12:::1;:25:26:9:0;18:16:18:23;:26::28;:16:::1;:30:22:13:0;19:24:19:31;:35::36;:16::38:1;22:19:25:13:0;24:24:24:31;:35::37;:16::39:1;22:19:25:13;26:15:29:9:0;27:20:27:27;:31::32;:12::34:1;26:15:29:9;15:36:32:5;;3:0:33:1', + logs: [ + { ip: 12, line: 11, data: ['recipientSig is', { type: 'sig', stackIndex: 4, ip: 8 }] }, + { ip: 35, line: 20, data: ['timeout is', { stackIndex: 1, type: 'int', ip: 35 }] }, + { ip: 35, line: 21, data: ['senderSig is', { type: 'sig', stackIndex: 3, ip: 20 }] }, + { ip: 36, line: 23, data: ['timeout is', { stackIndex: 1, type: 'int', ip: 36 }] }, + { ip: 44, line: 28, data: ['timeout is', { stackIndex: 1, type: 'int', ip: 44 }] }, + { + ip: 45, + line: 31, + data: [ + 'timeout is', + { stackIndex: 1, type: 'int', ip: 45 }, + 'and senderSig is', + { type: 'sig', stackIndex: 3, ip: 20 }, + ], + }, + ], + requires: [ + { ip: 12, line: 10 }, + { ip: 23, line: 16 }, + { ip: 34, line: 19 }, + { ip: 38, line: 24 }, + { ip: 43, line: 27 }, + ], + sourceTags: '9:11:sc', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/deeply_nested.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/deeply_nested.ts new file mode 100644 index 000000000..592826315 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/deeply_nested.ts @@ -0,0 +1,63 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'TransferWithTimeout', + constructorInputs: [ + { name: 'sender', type: 'pubkey' }, + { name: 'recipient', type: 'pubkey' }, + { name: 'timeout', type: 'int' }, + ], + abi: [ + { name: 'transfer', inputs: [{ name: 'recipientSig', type: 'sig' }] }, + { name: 'timeout', inputs: [{ name: 'senderSig', type: 'sig' }] }, + ], + bytecode: + // function transfer(sig recipientSig) { + 'OP_3 OP_PICK OP_0 OP_NUMEQUAL OP_IF ' + // require(checkSig(recipientSig, recipient)); + + 'OP_4 OP_ROLL OP_ROT OP_CHECKSIG ' + // Cleanup + + 'OP_NIP OP_NIP OP_NIP ' + // } + + 'OP_ELSE ' + // function timeout(sig senderSig) { + + 'OP_3 OP_ROLL OP_1 OP_NUMEQUALVERIFY ' + // require(checkSig(senderSig, sender)); + + 'OP_3 OP_ROLL OP_SWAP OP_CHECKSIGVERIFY ' + // if (timeout > 0) { + + 'OP_OVER OP_0 OP_GREATERTHAN OP_IF ' + // if (timeout < 10) { + + 'OP_OVER OP_10 OP_LESSTHAN OP_IF ' + // require(timeout == 5); + + 'OP_OVER OP_5 OP_NUMEQUALVERIFY ' + // } else { + + 'OP_ELSE ' + // require(timeout == 15); + + 'OP_OVER OP_15 OP_NUMEQUALVERIFY ' + // } + + 'OP_ENDIF ' + // } else { + + 'OP_ELSE ' + // require(timeout == 0); + + 'OP_OVER OP_0 OP_NUMEQUALVERIFY ' + // } + + 'OP_ENDIF OP_2DROP OP_1 OP_ENDIF', + fingerprint: '8c0754826497834b06b82f8e040f49613e1bc193b8b1bb8ab171f8538ddfb47e', + debug: { + bytecode: '5379009c63547a7bac77777767537a519d537a7cad7800a063785a9f6378559d67785f9d686778009d686d5168', + sourceMap: '9:4:11:5;;;;;10:25:10:37;;:39::48;:8::51:1;9:40:11:5;;;:4;14::25::0;;;;15:25:15:34;;:36::42;:8::45:1;16:12:16:19:0;:22::23;:12:::1;:25:22:9:0;17:16:17:23;:26::28;:16:::1;:30:19:13:0;18:24:18:31;:35::36;:16::38:1;19:19:21:13:0;20:24:20:31;:35::37;:16::39:1;19:19:21:13;22:15:24:9:0;23:20:23:27;:31::32;:12::34:1;22:15:24:9;14:36:25:5;;3:0:26:1', + logs: [], + requires: [ + { ip: 12, line: 10 }, + { ip: 23, line: 15 }, + { ip: 34, line: 18 }, + { ip: 38, line: 20 }, + { ip: 43, line: 23 }, + ], + sourceTags: '9:11:sc', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/do_while_loop.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/do_while_loop.ts new file mode 100644 index 000000000..7e03e275a --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/do_while_loop.ts @@ -0,0 +1,29 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Loopy', + constructorInputs: [], + abi: [{ name: 'doLoop', inputs: [] }], + bytecode: + // int i = 0; + 'OP_0 ' + // do { + + 'OP_BEGIN ' + // i = i + 1; + + 'OP_DUP OP_1ADD OP_NIP ' + // } while (i < tx.inputs.length); + + 'OP_DUP OP_TXINPUTCOUNT OP_GREATERTHANOREQUAL OP_UNTIL ' + // require(i > 2); + + 'OP_2 OP_GREATERTHAN', + fingerprint: '45d5cf43034d482df71ca54496793bff3f932d37dd77e95367edeb22a98a9154', + debug: { + bytecode: '0065768b7776c3a26652a0', + sourceMap: '3:16:3:17;5:8:7:39;6:16:6:17;:::21:1;:12::22;7:17:7:18:0;:21::37;5:8::39:1;;10:20:10:21:0;:8::23:1', + logs: [{ ip: 9, line: 9, data: [{ stackIndex: 0, type: 'int', ip: 9 }] }], + requires: [{ ip: 11, line: 10 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/do_while_loop_no_introspection.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/do_while_loop_no_introspection.ts new file mode 100644 index 000000000..485a6a804 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/do_while_loop_no_introspection.ts @@ -0,0 +1,23 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Loopy', + constructorInputs: [], + abi: [{ name: 'doLoop', inputs: [] }], + bytecode: 'OP_0 OP_2 OP_BEGIN OP_OVER OP_1ADD OP_ROT OP_DROP OP_SWAP OP_2DUP OP_ADD OP_10 OP_LESSTHAN OP_VERIFY OP_OVER OP_10 OP_GREATERTHANOREQUAL OP_UNTIL OP_2DROP OP_1', + debug: { + bytecode: '005265788b7b757c6e935a9f69785aa2666d51', + sourceMap: '3:16:3:17;4::4;6:8:10:25;7:16:7:17;:::21:1;:12::22;;;9:20:9:25:0;::::1;:28::30:0;:20:::1;:12::32;10:17:10:18:0;:21::23;6:8::25:1;;2:22:11:5;', + logs: [ + { ip: 8, line: 8, data: [{ stackIndex: 1, type: 'int', ip: 8 }] }, + ], + requires: [ + { ip: 12, line: 9 }, + ], + }, + fingerprint: '39b67bb19440620390b83e3bc1e638ea6827998f4a14c2d6e29c2afa9815b2fd', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/do_while_loop_require_inside_loop.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/do_while_loop_require_inside_loop.ts new file mode 100644 index 000000000..807a47786 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/do_while_loop_require_inside_loop.ts @@ -0,0 +1,31 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Loopy', + constructorInputs: [], + abi: [{ name: 'doLoop', inputs: [] }], + bytecode: + // int i = 0; + 'OP_0 ' + // do { + + 'OP_BEGIN ' + // i = i + 1; + + 'OP_DUP OP_1ADD OP_NIP ' + // require(i < 10); + + 'OP_DUP OP_10 OP_LESSTHAN OP_VERIFY ' + // } while (i < tx.inputs.length); + + 'OP_DUP OP_TXINPUTCOUNT OP_GREATERTHANOREQUAL OP_UNTIL ' + // } + + 'OP_DROP OP_1', + fingerprint: 'ce6d7a3b336f2b8c85a25c362635f801df5ebc31a54f6940c02df6966d7dd484', + debug: { + bytecode: '0065768b77765a9f6976c3a2667551', + sourceMap: '3:16:3:17;5:8:8:39;6:16:6:17;:::21:1;:12::22;7:20:7:21:0;:24::26;:20:::1;:12::28;8:17:8:18:0;:21::37;5:8::39:1;;2:22:9:5;', + logs: [], + requires: [{ ip: 8, line: 7 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/double_negation.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/double_negation.ts new file mode 100644 index 000000000..eb39fbb22 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/double_negation.ts @@ -0,0 +1,42 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'DoubleNegation', + constructorInputs: [{ name: 'flag', type: 'bool' }], + abi: [{ name: 'spend', inputs: [{ name: 'target', type: 'int' }] }], + bytecode: + // require(!!flag) - OP_NOT OP_NOT OP_VERIFY is optimised to OP_VERIFY + 'OP_DUP OP_VERIFY ' + // int i = 0; bool done = false; + + 'OP_0 OP_0 ' + // do { i = i + 1; + + 'OP_BEGIN OP_OVER OP_1ADD OP_ROT OP_DROP OP_SWAP ' + // done = i >= target; + + 'OP_OVER OP_4 OP_PICK OP_GREATERTHANOREQUAL OP_NIP ' + // } while (!done) - OP_NOT OP_NOT OP_UNTIL is optimised to OP_UNTIL + + 'OP_DUP OP_UNTIL ' + // if (!!flag) - OP_NOT OP_NOT OP_IF becomes OP_NOT OP_NOTIF, which is optimised to OP_IF + + 'OP_ROT OP_IF ' + // require(i == target); } + + 'OP_OVER OP_3 OP_PICK OP_NUMEQUALVERIFY OP_ENDIF ' + // require(i > 0) + + 'OP_SWAP OP_0 OP_GREATERTHAN ' + // clean up i and done + + 'OP_NIP OP_NIP', + debug: { + bytecode: '7669000065788b7b757c785479a27776667b637853799d687c00a07777', + sourceMap: '4:18:4:22;:8::24:1;6:16:6:17:0;7:20:7:25;9:8:13:24;10:16:10:17;:::21:1;:12::22;;;11:19:11:20:0;:24::30;;:19:::1;:12::31;13:18:13:22:0;9:8::24:1;16:14:16:18:0;:12:18:9;17:20:17:21;:25::31;;:12::33:1;16:20:18:9;20:16:20:17:0;:20::21;:8::23:1;2:31:21:5;', + logs: [], + requires: [ + { ip: 2, line: 4 }, + { ip: 23, line: 17 }, + { ip: 28, line: 20 }, + ], + sourceTags: '27:28:sc', + }, + fingerprint: 'ed0b5bc35f0130fa04d1fce813fbd7c183bb5346006d10c0808f0d9872712ded', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/double_split.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/double_split.ts new file mode 100644 index 000000000..c0987be84 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/double_split.ts @@ -0,0 +1,24 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'DoubleSplit', + constructorInputs: [{ name: 'pkh', type: 'bytes20' }], + abi: [{ name: 'spend', inputs: [] }], + bytecode: 'OP_INPUTINDEX OP_UTXOBYTECODE 17 OP_SPLIT OP_DROP OP_3 OP_SPLIT OP_NIP OP_EQUAL', + debug: { + bytecode: 'c0c701177f75537f7787', + sourceMap: '3:36:3:57;:26::74:1;:81::83:0;:26::84:1;:::87;:94::95:0;:26::96:1;:::99;4:8:4:34', + logs: [], + requires: [ + { + ip: 10, + line: 4, + }, + ], + }, + fingerprint: 'd8574d80ab674df33841526cf2767c09a9dc02d2faea91746465e22e3b81ae3a', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/everything.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/everything.ts new file mode 100644 index 000000000..84c710c8b --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/everything.ts @@ -0,0 +1,68 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [{ name: 'x', type: 'int' }, { name: 'y', type: 'string' }], + abi: [{ name: 'hello', inputs: [{ name: 's', type: 'sig' }, { name: 'pk', type: 'pubkey' }] }], + bytecode: + // int i = 400 + x; + '9001 OP_OVER OP_ADD ' + // bytes b = 0x07364897987fe87 + bytes(y); + + '07364897987fe807 OP_3 OP_PICK OP_CAT ' + // int myVariable = 10 - int(false); // they can go at the end of the line + + 'OP_10 OP_0 OP_SUB ' + // int myOtherVariable = (i + myVariable) % 2; + + 'OP_2 OP_PICK OP_OVER OP_ADD OP_2 OP_MOD ' + // require(myOtherVariable /* And comments can be included within lines */ > i); + + 'OP_DUP OP_4 OP_PICK OP_GREATERTHAN OP_VERIFY ' + // myOtherVariable = i; + + 'OP_3 OP_PICK ' + // myVariable = 10; + + 'OP_10 ' + // require(ripemd160(b) == ripemd160(bytes(myVariable))); + + 'OP_4 OP_ROLL OP_RIPEMD160 OP_OVER OP_RIPEMD160 OP_EQUALVERIFY ' + // require(this.age >= 500); + + 'f401 OP_CHECKSEQUENCEVERIFY OP_DROP ' + // require(y.length < -10); + + 'OP_6 OP_ROLL OP_SIZE OP_NIP 8a OP_LESSTHAN OP_VERIFY ' + // if (i > 400) { + + 'OP_4 OP_PICK 9001 OP_GREATERTHAN OP_IF ' + // i = 400; + + '9001 OP_5 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK ' + // } + + 'OP_ENDIF ' + // if (x > 10) { + + 'OP_5 OP_PICK OP_10 OP_GREATERTHAN OP_IF ' + // require(i < 20); + + 'OP_4 OP_PICK 14 OP_LESSTHAN OP_VERIFY ' + // require(checkSig(s, pk)); + + 'OP_6 OP_PICK OP_8 OP_PICK OP_CHECKSIGVERIFY ' + // } else if (x < 5) { + + 'OP_ELSE OP_5 OP_PICK OP_5 OP_LESSTHAN OP_IF ' + // require(false); + + 'OP_0 OP_VERIFY ' + // require(myVariable == 1); + + 'OP_ELSE OP_DUP OP_1 OP_NUMEQUALVERIFY OP_ENDIF OP_ENDIF ' + // } + + 'OP_2DROP OP_2DROP OP_2DROP OP_2DROP OP_1', + fingerprint: '5c83820f08cc787eb0078463cbf16b3b8c605a21bf42464bf9d4f9f31f254569', + debug: { + bytecode: '02900178930807364897987fe80753797e5a0094527978935297765479a06953795a547aa678a68802f401b275567a8277018a9f695479029001a063029001557a757c6b7c6b7c6b7c6c6c6c6855795aa063547901149f6956795879ad675579559f6300696776519d68686d6d6d6d51', + sourceMap: '9:16:9:19;:22::23;:16:::1;10:18:10:35:0;:44::45;;:18::46:1;12:25:12:27:0;:34::39;:25::40:1;13:31:13:32:0;;:35::45;:31:::1;:49::50:0;:30:::1;14:16:14:31:0;:82::83;;:16:::1;:8::85;16:26:16:27:0;;17:21:17:23;19:26:19:27;;:16::28:1;:48::58:0;:32::60:1;:8::62;20:28:20:31:0;:8::33:1;;21:16:21:17:0;;:::24:1;;:27::30:0;:16:::1;:8::32;23:12:23:13:0;;:16::19;:12:::1;:21:25:9:0;24:16:24:19;:12::20:1;;;;;;;;;;;;;23:21:25:9;27:12:27:13:0;;:16::18;:12:::1;:20:30:9:0;28::28:21;;:24::26;:20:::1;:12::28;29:29:29:30:0;;:32::34;;:12::37:1;30:15:33::0;:19:30:20;;:23::24;:19:::1;:26:32:9:0;31:20:31:25;:12::27:1;33::33:37:0;:20::30;:34::35;:12::37:1;;30:15;8:37:34:5;;;;', + logs: [], + requires: [ + { ip: 22, line: 14 }, + { ip: 31, line: 19 }, + { ip: 33, line: 20 }, + { ip: 41, line: 21 }, + { ip: 71, line: 28 }, + { ip: 76, line: 29 }, + { ip: 84, line: 31 }, + { ip: 88, line: 33 }, + ], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/for_loop_basic.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/for_loop_basic.ts new file mode 100644 index 000000000..cf31a691f --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/for_loop_basic.ts @@ -0,0 +1,22 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'ForLoopBasic', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [] }], + bytecode: 'OP_0 OP_0 OP_BEGIN OP_DUP OP_3 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_2DUP OP_ADD OP_ROT OP_DROP OP_SWAP OP_DUP OP_1ADD OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_3 OP_NUMEQUAL', + debug: { + bytecode: '00006576539f766b636e937b757c768b77686c916675539c', + sourceMap: '3:18:3:19;5:21:5:22;:8:7:9;:24:5:25;:28::29;:24:::1;;;:36:7:9:0;6:18:6:25;::::1;:12::26;;;5:31:5:32:0;:::34:1;;:36:7:9;;:8;;;9:23:9:24:0;:8::26:1', + logs: [], + requires: [ + { ip: 24, line: 9 }, + ], + sourceTags: '14:16:fu;17:20:lc;21:21:sc', + }, + fingerprint: '9e52dba656b0743057d4eda76f051c8a1f4460a2becf8f372c690b1901512b4e', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/for_loop_stack_items.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/for_loop_stack_items.ts new file mode 100644 index 000000000..063af7457 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/for_loop_stack_items.ts @@ -0,0 +1,24 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'ForLoopBasic', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [] }], + bytecode: 'OP_0 OP_1 OP_1 OP_0 OP_BEGIN OP_DUP OP_3 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_3 OP_PICK OP_OVER OP_ADD OP_4 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_DUP OP_1ADD OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_ROT OP_3 OP_NUMEQUALVERIFY OP_SWAP OP_1 OP_NUMEQUALVERIFY OP_1 OP_NUMEQUAL', + debug: { + bytecode: '005151006576539f766b6353797893547a757c6b7c6b7c6c6c768b77686c9166757b539d7c519d519c', + sourceMap: '3:18:3:19;4:16:4:17;5::5;7:21:7:22;:8:9:9;:24:7:25;:28::29;:24:::1;;;:39:9:9:0;8:18:8:21;;:24::25;:18:::1;:12::26;;;;;;;;;;7:31:7:32:0;:::37:1;;:39:9:9;;:8;;;11:16:11:19:0;:23::24;:8::26:1;12:16:12:17:0;:21::22;:8::24:1;13:21:13:22:0;:8::24:1', + logs: [], + requires: [ + { ip: 35, line: 11 }, + { ip: 38, line: 12 }, + { ip: 41, line: 13 }, + ], + sourceTags: '25:27:fu;28:31:lc;32:32:sc', + }, + fingerprint: '05430d75d110ff5d4525f7e34bbf2c86b8d22f4e5758f2e109180b801b21b8ec', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/for_while_nested.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/for_while_nested.ts new file mode 100644 index 000000000..3d00a2526 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/for_while_nested.ts @@ -0,0 +1,35 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'ForWhileNested', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [] }], + bytecode: 'OP_0 OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_2 OP_PICK OP_2 OP_PICK OP_ADD OP_OVER OP_ADD OP_3 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_DUP OP_1ADD OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_OVER OP_1ADD OP_ROT OP_DROP OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_4 OP_NUMEQUAL', + debug: { + bytecode: '00006576529f766b63006576529f766b6352795279937893537a757c6b7c6c768b77686c9166788b7b7577686c916675549c', + sourceMap: '3:18:3:19;5:21:5:22;:8:13:9;:24:5:25;:28::29;:24:::1;;;:42:13:9:0;6:20:6:21;8:12:12:13;:19:8:20;:23::24;:19:::1;;;:26:12:13:0;9:22:9:25;;:28::29;;:22:::1;:32::33:0;:22:::1;:16::34;;;;;;;10:20:10:21:0;:::25:1;:16::26;8:26:12:13;;:12;;5:35:5:36:0;:::40:1;:31;;::13:9;:42;;:8;;;15:23:15:24:0;:8::26:1', + logs: [ + { + ip: 34, + line: 11, + data: [ + 'sum:', + { stackIndex: 2, type: 'int', ip: 34 }, + 'i:', + { stackIndex: 1, type: 'int', ip: 34 }, + 'j:', + { stackIndex: 0, type: 'int', ip: 34 }, + ], + }, + ], + requires: [ + { ip: 50, line: 15 }, + ], + sourceTags: '34:37:lc;38:42:fu;43:46:lc;47:47:sc', + }, + fingerprint: '9eb2ec48e103cb2b0d75a326b533756d8f5edb49b2ea43a5578f5ceebde8c2ce', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/force_cast_smaller_bytes.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/force_cast_smaller_bytes.ts new file mode 100644 index 000000000..1fc969f5e --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/force_cast_smaller_bytes.ts @@ -0,0 +1,23 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [], + abi: [{ name: 'hello', inputs: [] }], + bytecode: + // bytes3 byte_ = unsafe_bytes3(bytes(0x1234)); + '1234 ' + // require(byte_.length == 1); + + 'OP_SIZE OP_NIP OP_1 OP_NUMEQUAL', + fingerprint: '07359fa5600dcaa0a9c582b428cdad5a3a9c63ac13f44b3503eaf31b540edc8e', + debug: { + bytecode: '0212348277519c', + sourceMap: '4:43:4:49;5:16:5:28:1;;:32::33:0;:8::35:1', + logs: [], + requires: [{ ip: 5, line: 5 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/global_constant_arithmetic.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/global_constant_arithmetic.ts new file mode 100644 index 000000000..b11a004dc --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/global_constant_arithmetic.ts @@ -0,0 +1,110 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'GlobalConstantArithmetic', + constructorInputs: [], + abi: [ + { + name: 'spend', + inputs: [ + { name: 'base', type: 'int' }, + { name: 'derived', type: 'int' }, + { name: 'negated', type: 'int' }, + { name: 'complex', type: 'int' }, + { name: 'greeting', type: 'string' }, + { name: 'magic', type: 'bytes4' }, + ], + }, + ], + bytecode: + // Implicit parameter type enforcement + 'OP_5 OP_ROLL OP_SIZE OP_4 OP_EQUALVERIFY ' + // require(base == BASE); + + 'OP_SWAP 14 OP_NUMEQUALVERIFY ' + // require(derived == DERIVED); + + 'OP_SWAP OP_15 OP_NUMEQUALVERIFY ' + // require(negated == NEGATED); + + 'OP_SWAP 9e OP_NUMEQUALVERIFY ' + // require(complex == COMPLEX); + + 'OP_SWAP 0002 OP_NUMEQUALVERIFY ' + // require(greeting == GREETING); + + 'OP_SWAP 68656c6c6f20776f726c64 OP_EQUALVERIFY ' + // require(magic == MAGIC); + + '01020304 OP_EQUAL', + fingerprint: '29ddac97c055a2c032d2164877831a3ba0506b19bf20e0e3511da9dc874a21dc', + debug: { + bytecode: '557a8254887c01149d7c5f9d7c019e9d7c0200029d7c0b68656c6c6f20776f726c6488040102030487', + sourceMap: '9:85:9:97;;;;;10:16:10:20;:24::28:1;:8::30;11:16:11:23:0;:27::34:1;:8::36;12:16:12:23:0;:27::34:1;:8::36;13:16:13:23:0;:27::34:1;:8::36;14:16:14:24:0;:28::36:1;:8::38;15:25:15:30;:8::32', + logs: [], + requires: [ + { ip: 7, line: 10 }, + { ip: 10, line: 11 }, + { ip: 13, line: 12 }, + { ip: 16, line: 13 }, + { ip: 19, line: 14 }, + { ip: 22, line: 15 }, + ], + sourceTags: '0:4:pv', + functions: [ + { + name: 'BASE', + kind: 'constant', + inputs: [], + bytecode: '0114', + sourceMap: '1:20:1:27', + logs: [], + requires: [], + }, + { + name: 'DERIVED', + kind: 'constant', + inputs: [], + bytecode: '5f', + sourceMap: '2:23:2:31', + logs: [], + requires: [], + }, + { + name: 'NEGATED', + kind: 'constant', + inputs: [], + bytecode: '019e', + sourceMap: '3:23:3:35', + logs: [], + requires: [], + }, + { + name: 'COMPLEX', + kind: 'constant', + inputs: [], + bytecode: '020002', + sourceMap: '4:23:4:51', + logs: [], + requires: [], + }, + { + name: 'GREETING', + kind: 'constant', + inputs: [], + bytecode: '0b68656c6c6f20776f726c64', + sourceMap: '5:27:5:50', + logs: [], + requires: [], + }, + { + name: 'MAGIC', + kind: 'constant', + inputs: [], + bytecode: '0401020304', + sourceMap: '6:24:6:39', + logs: [], + requires: [], + }, + ], + inlineRanges: '6:6:BASE;9:9:DERIVED;12:12:NEGATED;15:15:COMPLEX;18:18:GREETING;20:20:MAGIC', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/global_constant_inlined.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/global_constant_inlined.ts new file mode 100644 index 000000000..9e2088d5f --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/global_constant_inlined.ts @@ -0,0 +1,40 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + // A small global constant used repeatedly — inlined as a plain literal at each use site (no + // OP_DEFINE), with source locations mapping to the use sites rather than the declaration. + artifact: { + contractName: 'GlobalConstantInlined', + constructorInputs: [{ name: 'value', type: 'int' }], + abi: [{ name: 'spend', inputs: [] }], + bytecode: + // require(value + ONE + ONE == 3) + 'OP_1ADD OP_1ADD OP_3 OP_NUMEQUAL', + debug: { + bytecode: '8b8b539c', + logs: [], + requires: [ + { ip: 5, line: 5 }, + ], + sourceMap: '5:16:5:27:1;:::33;:37::38:0;:8::40:1', + // Both literal pushes were fused into the OP_1ADDs during optimisation; the ranges track them + inlineRanges: '1:1:ONE;2:2:ONE', + functions: [ + { + // The inlined constant is documented as an id-less frame; both of its literal pushes + // were emitted at the use sites and fused into the OP_1ADDs during optimisation + name: 'ONE', + kind: 'constant', + inputs: [], + bytecode: '51', + sourceMap: '1:19:1:20', + logs: [], + requires: [], + }, + ], + }, + fingerprint: '0d639aa764e1dc4045e25efe7dc27bd247b3cd45dd6c3a878a83bc3015e38a59', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/global_constant_literals.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/global_constant_literals.ts new file mode 100644 index 000000000..18106ac26 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/global_constant_literals.ts @@ -0,0 +1,110 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'GlobalConstantLiterals', + constructorInputs: [], + abi: [ + { + name: 'spend', + inputs: [ + { name: 'enabled', type: 'bool' }, + { name: 'negative', type: 'int' }, + { name: 'interval', type: 'int' }, + { name: 'deadline', type: 'int' }, + { name: 'greeting', type: 'string' }, + { name: 'magic', type: 'bytes4' }, + ], + }, + ], + bytecode: + // Implicit parameter type enforcement + 'OP_0NOTEQUAL OP_5 OP_ROLL OP_SIZE OP_4 OP_EQUALVERIFY ' + // require(enabled == ENABLED); + + 'OP_SWAP OP_1 OP_NUMEQUALVERIFY ' + // require(negative == NEGATIVE); + + 'OP_SWAP 87 OP_NUMEQUALVERIFY ' + // require(interval == INTERVAL); + + 'OP_SWAP 201c OP_NUMEQUALVERIFY ' + // require(deadline == DEADLINE); + + 'OP_SWAP 98712c60 OP_NUMEQUALVERIFY ' + // require(greeting == GREETING); + + 'OP_SWAP 68656c6c6f OP_EQUALVERIFY ' + // require(magic == MAGIC); + + '01020304 OP_EQUAL', + fingerprint: '74578f6eb254c7cb367434c6754d27e684bd1b3c7362fac7a0024397c9f73331', + debug: { + bytecode: '92557a8254887c519d7c01879d7c02201c9d7c0498712c609d7c0568656c6c6f88040102030487', + sourceMap: '9:19:9:31;:92::104;;;;;10:16:10:23;:27::34:1;:8::36;11:16:11:24:0;:28::36:1;:8::38;12:16:12:24:0;:28::36:1;:8::38;13:16:13:24:0;:28::36:1;:8::38;14:16:14:24:0;:28::36:1;:8::38;15:25:15:30;:8::32', + logs: [], + requires: [ + { ip: 8, line: 10 }, + { ip: 11, line: 11 }, + { ip: 14, line: 12 }, + { ip: 17, line: 13 }, + { ip: 20, line: 14 }, + { ip: 23, line: 15 }, + ], + sourceTags: '0:0:pv;1:5:pv', + functions: [ + { + name: 'ENABLED', + kind: 'constant', + inputs: [], + bytecode: '51', + sourceMap: '1:24:1:28', + logs: [], + requires: [], + }, + { + name: 'NEGATIVE', + kind: 'constant', + inputs: [], + bytecode: '0187', + sourceMap: '2:24:2:26', + logs: [], + requires: [], + }, + { + name: 'INTERVAL', + kind: 'constant', + inputs: [], + bytecode: '02201c', + sourceMap: '3:24:3:31', + logs: [], + requires: [], + }, + { + name: 'DEADLINE', + kind: 'constant', + inputs: [], + bytecode: '0498712c60', + sourceMap: '4:24:4:51', + logs: [], + requires: [], + }, + { + name: 'GREETING', + kind: 'constant', + inputs: [], + bytecode: '0568656c6c6f', + sourceMap: '5:27:5:34', + logs: [], + requires: [], + }, + { + name: 'MAGIC', + kind: 'constant', + inputs: [], + bytecode: '0401020304', + sourceMap: '6:24:6:34', + logs: [], + requires: [], + }, + ], + inlineRanges: '7:7:ENABLED;10:10:NEGATIVE;13:13:INTERVAL;16:16:DEADLINE;19:19:GREETING;21:21:MAGIC', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/global_constant_shared.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/global_constant_shared.ts new file mode 100644 index 000000000..0bc9674b6 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/global_constant_shared.ts @@ -0,0 +1,40 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + // A global constant used repeatedly — lowered to a zero-argument VM function definition with a + // kind: 'constant' debug frame; each use compiles to an OP_INVOKE. + artifact: { + contractName: 'GlobalConstantShared', + constructorInputs: [{ name: 'first', type: 'bytes32' }, { name: 'second', type: 'bytes32' }], + abi: [{ name: 'spend', inputs: [] }], + bytecode: + // OP_DEFINE HASH (id 0): the 32-byte literal + '203333333333333333333333333333333333333333333333333333333333333333 OP_0 OP_DEFINE ' + // require(first == HASH); require(second == HASH) + + 'OP_0 OP_INVOKE OP_EQUALVERIFY OP_0 OP_INVOKE OP_EQUAL', + debug: { + bytecode: '212033333333333333333333333333333333333333333333333333333333333333330089008a88008a87', + logs: [], + requires: [ + { ip: 7, line: 5 }, + { ip: 11, line: 6 }, + ], + sourceMap: '1::1:91;;::::1;5:25:5:29;;:8::31;6:26:6:30;;:8::32', + functions: [ + { + id: 0, + name: 'HASH', + kind: 'constant', + inputs: [], + bytecode: '203333333333333333333333333333333333333333333333333333333333333333', + sourceMap: '1:24:1:90', + logs: [], + requires: [], + }, + ], + }, + fingerprint: '6a5509a2ece64c7e47b4e1185da2f8b92fc0e1f75cc818be86b783c7bf134c5e', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_altstack_cleanup.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_altstack_cleanup.ts new file mode 100644 index 000000000..89579712f --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_altstack_cleanup.ts @@ -0,0 +1,36 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'GlobalFunctionAltStackCleanup', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [{ name: 'x', type: 'int' }] }], + bytecode: + // int lo, int hi = orderedPair(1, 2, x); + 'OP_1 OP_2 OP_ROT OP_0 OP_0 OP_ROT OP_0 OP_GREATERTHAN OP_IF OP_3 OP_PICK OP_ROT OP_DROP OP_SWAP OP_2 OP_PICK OP_NIP OP_ELSE OP_NIP OP_OVER OP_SWAP OP_3 OP_PICK OP_NIP OP_ENDIF OP_ROT OP_DROP OP_ROT OP_DROP ' + // require(lo == 1, "lo should be 1"); + + 'OP_SWAP OP_1 OP_NUMEQUALVERIFY ' + // require(hi == 2, "hi should be 2"); + + 'OP_2 OP_NUMEQUAL', + fingerprint: '641437fc5e376f92f1da865a9a0e8a1cc1a48fe845b38ea10afa9ba4ce4e5908', + debug: { + bytecode: '51527b00007b00a06353797b757c5279776777787c537977687b757b757c519d529c', + sourceMap: '16:37:16:38;:40::41;:43::44;:25::45:1;;;;;;;;;;;;;;;;;;;;;;;;;;17:16:17:18:0;:22::23;:8::43:1;18:22:18:23:0;:8::43:1', + logs: [], + requires: [{ ip: 31, line: 17, message: 'lo should be 1' }, { ip: 34, line: 18, message: 'hi should be 2' }], + functions: [ + { + name: 'orderedPair', + inputs: [{ name: 'a', type: 'int' }, { name: 'b', type: 'int' }, { name: 'c', type: 'int' }], + bytecode: '00007b00a06353797b757c5279776777787c537977687b757b75', + sourceMap: '2:13:2:14;3::3;4:8:4:9;:12::13;:8:::1;:15:7:5:0;5:13:5:14;;:8::15:1;;;6:13:6:14:0;;:8::15:1;7:11:10:5:0;8:8:8:15:1;;;9:13:9:14:0;;:8::15:1;7:11:10:5;1:61:12:1;;;', + logs: [], + requires: [], + }, + ], + inlineRanges: '3:28:orderedPair', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_in_control_flow.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_in_control_flow.ts new file mode 100644 index 000000000..b6573cedb --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_in_control_flow.ts @@ -0,0 +1,57 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'GlobalFunctionInControlFlow', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [{ name: 'x', type: 'int' }, { name: 'useLoop', type: 'bool' }] }], + bytecode: + // Implicit parameter type enforcement + 'OP_SWAP OP_0NOTEQUAL ' + // int total = 0; + + 'OP_0 ' + // if (useLoop) { + + 'OP_SWAP OP_IF ' + // for (int i = 0; i < 3; i = i + 1) { + + 'OP_0 OP_BEGIN OP_DUP OP_3 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF ' + // total = total + triple(x); + + 'OP_OVER OP_3 OP_PICK OP_3 OP_MUL OP_ADD OP_ROT OP_DROP OP_SWAP ' + // For loop update + + 'OP_DUP OP_1ADD OP_NIP ' + // Loop condition + + 'OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL ' + // Cleanup + + 'OP_DROP ' + // } else { + + 'OP_ELSE ' + // total = triple(x); + + 'OP_OVER OP_3 OP_MUL OP_NIP ' + // } + + 'OP_ENDIF ' + // require(total == 9); + + 'OP_9 OP_NUMEQUAL ' + // Cleanup + + 'OP_NIP', + fingerprint: '55b4a10e6ce60560acbb358c02189f85620385e0fcfd5013fe236339e8b238bb', + debug: { + bytecode: '7c92007c63006576539f766b637853795395937b757c768b77686c916675677853957768599c77', + sourceMap: '6:26:6:38;;7:20:7:21;8:12:8:19;:21:12:9;9:25:9:26;:12:11:13;:28:9:29;:32::33;:28:::1;;;:46:11:13:0;10:24:10:29;:39::40;;:32::41:1;;:24;:16::42;;;9:39:9:40:0;:::44:1;:35;:46:11:13;;:12;;;12:15:14:9:0;13:27:13:28;:20::29:1;;:12::30;12:15:14:9;15:25:15:26:0;:8::28:1;6:40:16:5', + logs: [], + requires: [{ ip: 38, line: 15 }], + sourceTags: '0:1:pv;22:24:fu;25:28:lc;29:29:sc;38:38:sc', + functions: [ + { + name: 'triple', + inputs: [{ name: 'a', type: 'int' }], + bytecode: '5395', + sourceMap: '2:15:2:16;:11:::1', + logs: [], + requires: [], + }, + ], + inlineRanges: '16:17:triple;32:33:triple', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_inlined.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_inlined.ts new file mode 100644 index 000000000..be0fdc3d4 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_inlined.ts @@ -0,0 +1,48 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + // A single-use global function — inlined at the call site, splicing its console.log and require + // metadata into the contract's debug info (same-file bodies keep their own source lines). + artifact: { + contractName: 'GlobalFunctionInlined', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [{ name: 'n', type: 'int' }] }], + bytecode: + // require(checked(n) == n), with checked(x) spliced in: + // console.log ... require(x > 0, "positive") ... return x + 'OP_DUP OP_DUP OP_0 OP_GREATERTHAN OP_VERIFY OP_NUMEQUAL', + debug: { + bytecode: '767600a0699c', + logs: [ + { ip: 1, line: 9, data: ['checking', { stackIndex: 0, type: 'int', ip: 1 }] }, + ], + requires: [ + { ip: 4, line: 9, message: 'positive' }, + { ip: 6, line: 9 }, + ], + // The emitted body ops (ips 1-4) and the merged require/log entries above all map to the + // call site; the function's own lines live on its frame below, tied together by the range + sourceMap: '9:24:9:25;:16::26:1;;;;:8::33', + inlineRanges: '1:4:checked', + functions: [ + { + // The inlined function is documented as an id-less frame carrying its compiled body + // and frame-local debug info (ips from 0) + name: 'checked', + inputs: [{ name: 'x', type: 'int' }], + bytecode: '7600a069', + sourceMap: '3:12:3:13;:16::17;:12:::1;:4::31', + logs: [ + { ip: 0, line: 2, data: ['checking', { stackIndex: 0, type: 'int', ip: 0 }] }, + ], + requires: [ + { ip: 3, line: 3, message: 'positive' }, + ], + }, + ], + }, + fingerprint: 'a19e54aee90995fe784da8e5501a95020d91aa1ccf17ac1f3c7a3e7be0813a73', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_multi_param.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_multi_param.ts new file mode 100644 index 000000000..b0d4a1762 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_multi_param.ts @@ -0,0 +1,67 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'GlobalFunctionMultiParam', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [{ name: 'x', type: 'int' }, { name: 'y', type: 'int' }] }], + bytecode: + // require(sub(x, y) == 7); + 'OP_SWAP OP_SUB OP_7 OP_NUMEQUAL', + fingerprint: 'e2fc400768dc40234ade786da44ad2a623d43f73ee5b70af21da0647616b162f', + debug: { + bytecode: '7c94579c', + sourceMap: '7:23:7:24;:16::25:1;:29::30:0;:8::32:1', + logs: [], + requires: [{ ip: 4, line: 7 }], + functions: [ + { + name: 'sub', + inputs: [{ name: 'a', type: 'int' }, { name: 'b', type: 'int' }], + bytecode: '94', + sourceMap: '2:11:2:16:1', + logs: [], + requires: [], + }, + ], + inlineRanges: '1:1:sub', + }, + }, + }, + { + // A multi-parameter global function — locks in the parameter stack-seeding and argument order + // (the contract OP_SWAPs x and y into place; the body computes a - b directly). + compilerOptions: { disableInlining: true }, + artifact: { + contractName: 'GlobalFunctionMultiParam', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [{ name: 'x', type: 'int' }, { name: 'y', type: 'int' }] }], + bytecode: + // OP_DEFINE sub (id 0): return a - b + '94 OP_0 OP_DEFINE ' + // require(sub(x, y) == 7) + + 'OP_SWAP OP_0 OP_INVOKE OP_7 OP_NUMEQUAL', + debug: { + bytecode: '019400897c008a579c', + logs: [], + requires: [ + { ip: 8, line: 7 }, + ], + sourceMap: '1::3:1;;::::1;7:23:7:24:0;:16::25:1;;:29::30:0;:8::32:1', + functions: [ + { + id: 0, + name: 'sub', + inputs: [{ name: 'a', type: 'int' }, { name: 'b', type: 'int' }], + bytecode: '94', + sourceMap: '2:11:2:16:1', + logs: [], + requires: [], + }, + ], + }, + fingerprint: '8fc72a3f89ee3238266d6dd9ad3919f7238c8d6a31296cc8925968a31c78c7dc', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_multi_return.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_multi_return.ts new file mode 100644 index 000000000..3b8a955a7 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_multi_return.ts @@ -0,0 +1,72 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'GlobalFunctionMultiReturn', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [{ name: 'x', type: 'int' }] }], + bytecode: + // int q, int r = divmod(x, 3); + 'OP_3 OP_2DUP OP_DIV OP_ROT OP_ROT OP_MOD ' + // require(q == 4); + + 'OP_SWAP OP_4 OP_NUMEQUALVERIFY ' + // require(r == 1); + + 'OP_1 OP_NUMEQUAL', + fingerprint: '04958e32271966d1b4d4365481f1d3210fde12595fa9261b454f430bf8afe521', + debug: { + bytecode: '536e967b7b977c549d519c', + sourceMap: '7:33:7:34;:23::35:1;;;;;8:16:8:17:0;:21::22;:8::24:1;9:21:9:22:0;:8::24:1', + logs: [], + requires: [{ ip: 8, line: 8 }, { ip: 11, line: 9 }], + functions: [ + { + name: 'divmod', + inputs: [{ name: 'a', type: 'int' }, { name: 'b', type: 'int' }], + bytecode: '6e967b7b97', + sourceMap: '2:11:2:16;::::1;:18::19:0;:22::23;:18:::1', + logs: [], + requires: [], + }, + ], + inlineRanges: '1:5:divmod', + }, + }, + }, + { + // A multi-return function — locks in the calling convention: return values are left on the stack + // in declared order (last value on top) and bound by an N-ary tuple destructuring at the call site. + compilerOptions: { disableInlining: true }, + artifact: { + contractName: 'GlobalFunctionMultiReturn', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [{ name: 'x', type: 'int' }] }], + bytecode: + // OP_DEFINE divmod (id 0): return a / b, a % b — leaves [quotient, remainder], remainder on top + '6e967b7b97 OP_0 OP_DEFINE ' + // int q, int r = divmod(x, 3); require(q == 4); require(r == 1) + + 'OP_3 OP_0 OP_INVOKE OP_SWAP OP_4 OP_NUMEQUALVERIFY OP_1 OP_NUMEQUAL', + debug: { + bytecode: '056e967b7b97008953008a7c549d519c', + logs: [], + requires: [ + { ip: 8, line: 8 }, + { ip: 11, line: 9 }, + ], + sourceMap: '1::3:1;;::::1;7:33:7:34:0;:23::35:1;;8:16:8:17:0;:21::22;:8::24:1;9:21:9:22:0;:8::24:1', + functions: [ + { + id: 0, + name: 'divmod', + inputs: [{ name: 'a', type: 'int' }, { name: 'b', type: 'int' }], + bytecode: '6e967b7b97', + sourceMap: '2:11:2:16;::::1;:18::19:0;:22::23;:18:::1', + logs: [], + requires: [], + }, + ], + }, + fingerprint: 'f747468c9408ec52949a22dc2f271a944ee5793eabaa913c9c2b1b4c3fbd0a56', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_multi_return_three.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_multi_return_three.ts new file mode 100644 index 000000000..7a9731dfb --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_multi_return_three.ts @@ -0,0 +1,38 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'GlobalFunctionMultiReturnThree', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [{ name: 'x', type: 'int' }] }], + bytecode: + // int p, int q, int r = spread(x); + 'OP_DUP OP_DUP OP_1ADD OP_ROT OP_2 OP_ADD ' + // require(p == 5); + + 'OP_ROT OP_5 OP_NUMEQUALVERIFY ' + // require(q == 6); + + 'OP_SWAP OP_6 OP_NUMEQUALVERIFY ' + // require(r == 7); + + 'OP_7 OP_NUMEQUAL', + fingerprint: 'aa5e498180e729156632201aff442d09e0c613298bf26169ff93956605eb2cfb', + debug: { + bytecode: '76768b7b52937b559d7c569d579c', + sourceMap: '7:30:7:39:1;;;;;;8:16:8:17:0;:21::22;:8::24:1;9:16:9:17:0;:21::22;:8::24:1;10:21:10:22:0;:8::24:1', + logs: [], + requires: [{ ip: 8, line: 8 }, { ip: 11, line: 9 }, { ip: 14, line: 10 }], + functions: [ + { + name: 'spread', + inputs: [{ name: 'a', type: 'int' }], + bytecode: '76768b7b5293', + sourceMap: '2:11:2:15;;:14::19:1;:21::22:0;:25::26;:21:::1', + logs: [], + requires: [], + }, + ], + inlineRanges: '0:5:spread', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_nested.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_nested.ts new file mode 100644 index 000000000..4b03c133d --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_nested.ts @@ -0,0 +1,41 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'GlobalFunctionNested', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [{ name: 'x', type: 'int' }] }], + bytecode: + // require(doubleIncremented(x) == 8); + 'OP_1ADD OP_2 OP_MUL OP_8 OP_NUMEQUAL', + fingerprint: '01c59dd1621703390bdc5e526ab56f2a37ed8cef6d3a61ff6218a80d51831703', + debug: { + bytecode: '8b5295589c', + sourceMap: '11:16:11:36:1;;;:40::41:0;:8::43:1', + logs: [], + requires: [{ ip: 5, line: 11 }], + functions: [ + { + name: 'addOne', + inputs: [{ name: 'a', type: 'int' }], + bytecode: '8b', + sourceMap: '2:11:2:16:1', + logs: [], + requires: [], + }, + { + name: 'doubleIncremented', + inputs: [{ name: 'a', type: 'int' }], + bytecode: '8b5295', + sourceMap: '6:11:6:20:1;:23::24:0;:11:::1', + logs: [], + requires: [], + inlineRanges: '0:0:addOne', + }, + ], + inlineRanges: '0:2:doubleIncremented', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_simple.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_simple.ts new file mode 100644 index 000000000..c275e05c7 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_simple.ts @@ -0,0 +1,66 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'GlobalFunctionSimple', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [{ name: 'x', type: 'int' }] }], + bytecode: + // require(double(x) == 6); + 'OP_2 OP_MUL OP_6 OP_NUMEQUAL', + fingerprint: '36698e75d5530f81a95354d9c1c11e7de7c8867595b28ca69cad14e24647ccf3', + debug: { + bytecode: '5295569c', + sourceMap: '7:16:7:25:1;;:29::30:0;:8::32:1', + logs: [], + requires: [{ ip: 4, line: 7 }], + functions: [ + { + name: 'double', + inputs: [{ name: 'a', type: 'int' }], + bytecode: '5295', + sourceMap: '2:15:2:16;:11:::1', + logs: [], + requires: [], + }, + ], + inlineRanges: '0:1:double', + }, + }, + }, + { + // A single global function — the basic OP_DEFINE / OP_INVOKE calling convention. + compilerOptions: { disableInlining: true }, + artifact: { + contractName: 'GlobalFunctionSimple', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [{ name: 'x', type: 'int' }] }], + bytecode: + // OP_DEFINE double (id 0): return a * 2 + '5295 OP_0 OP_DEFINE ' + // require(double(x) == 6) + + 'OP_0 OP_INVOKE OP_6 OP_NUMEQUAL', + debug: { + bytecode: '0252950089008a569c', + logs: [], + requires: [ + { ip: 7, line: 7 }, + ], + sourceMap: '1::3:1;;::::1;7:16:7:25;;:29::30:0;:8::32:1', + functions: [ + { + id: 0, + name: 'double', + inputs: [{ name: 'a', type: 'int' }], + bytecode: '5295', + sourceMap: '2:15:2:16;:11:::1', + logs: [], + requires: [], + }, + ], + }, + fingerprint: 'ef6dd7819e66a430286fe16f3d6dad7e026cf1970eda6bc620be7e7a3bdd2a4d', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_void.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_void.ts new file mode 100644 index 000000000..cbab13fed --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_void.ts @@ -0,0 +1,68 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'GlobalFunctionVoid', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [{ name: 'x', type: 'int' }] }], + bytecode: + // requirePositive(x); + 'OP_DUP OP_0 OP_GREATERTHAN OP_VERIFY ' + // require(x < 100); + + '64 OP_LESSTHAN', + fingerprint: '0c69be9a969ec46cf4dbb0c1b6581f833ff8f2117e03eaae0548312df8b74d99', + debug: { + bytecode: '7600a06901649f', + sourceMap: '7:24:7:25;:8::26:1;;;8:20:8:23:0;:8::25:1', + logs: [], + requires: [{ ip: 3, line: 7 }, { ip: 6, line: 8 }], + functions: [ + { + name: 'requirePositive', + inputs: [{ name: 'a', type: 'int' }], + bytecode: '00a069', + sourceMap: '2:16:2:17;:12:::1;:4::19', + logs: [], + requires: [{ ip: 2, line: 2 }], + }, + ], + inlineRanges: '1:3:requirePositive', + }, + }, + }, + { + // A void global function called as a statement — no return value, and the void stack-cleanup path. + compilerOptions: { disableInlining: true }, + artifact: { + contractName: 'GlobalFunctionVoid', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [{ name: 'x', type: 'int' }] }], + bytecode: + // OP_DEFINE requirePositive (id 0): require(a > 0) + '00a069 OP_0 OP_DEFINE ' + // requirePositive(x); require(x < 100) + + 'OP_DUP OP_0 OP_INVOKE 64 OP_LESSTHAN', + debug: { + bytecode: '0300a069008976008a01649f', + logs: [], + requires: [ + { ip: 8, line: 8 }, + ], + sourceMap: '1::3:1;;::::1;7:24:7:25:0;:8::26:1;;8:20:8:23:0;:8::25:1', + functions: [ + { + id: 0, + name: 'requirePositive', + inputs: [{ name: 'a', type: 'int' }], + bytecode: '00a069', + sourceMap: '2:16:2:17;:12:::1;:4::19', + logs: [], + requires: [{ ip: 2, line: 2 }], + }, + ], + }, + fingerprint: '4d5e07b068e501eb26e61aab0d53214aa42590858253b1106d6074d494fde557', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/hodl_vault.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/hodl_vault.ts new file mode 100644 index 000000000..943f563e5 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/hodl_vault.ts @@ -0,0 +1,58 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'HodlVault', + constructorInputs: [ + { name: 'ownerPk', type: 'pubkey' }, + { name: 'oraclePk', type: 'pubkey' }, + { name: 'minBlock', type: 'int' }, + { name: 'priceTarget', type: 'int' }, + ], + abi: [ + { + name: 'spend', + inputs: [ + { name: 'ownerSig', type: 'sig' }, + { name: 'oracleSig', type: 'datasig' }, + { name: 'oracleMessage', type: 'bytes8' }, + ], + }, + ], + bytecode: + // Implicit type enforcement for oracleMessage: require(oracleMessage.length == 8) + 'OP_6 OP_ROLL OP_SIZE OP_8 OP_EQUALVERIFY ' + // bytes4 blockHeightBin, bytes4 priceBin = oracleMessage.split(4); + + 'OP_DUP OP_4 OP_SPLIT ' + // int blockHeight = int(blockHeightBin); + + 'OP_SWAP OP_BIN2NUM ' + // int price = int(priceBin); + + 'OP_SWAP OP_BIN2NUM ' + // require(blockHeight >= minBlock); + + 'OP_OVER OP_6 OP_ROLL OP_GREATERTHANOREQUAL OP_VERIFY ' + // require(tx.time >= blockHeight); + + 'OP_SWAP OP_CHECKLOCKTIMEVERIFY OP_DROP ' + // require(price >= priceTarget); + + 'OP_4 OP_ROLL OP_GREATERTHANOREQUAL OP_VERIFY ' + // require(checkDataSig(oracleSig, oracleMessage, oraclePk)); + + 'OP_4 OP_ROLL OP_SWAP OP_3 OP_ROLL OP_CHECKDATASIGVERIFY ' + // require(checkSig(ownerSig, ownerPk)); + + 'OP_CHECKSIG', + debug: { + bytecode: '567a82588876547f7c817c8178567aa2697cb175547aa269547a7c537abbac', + logs: [], + requires: [ + { ip: 20, line: 23 }, + { ip: 22, line: 24 }, + { ip: 27, line: 27 }, + { ip: 33, line: 30 }, + { ip: 35, line: 35 }, + ], + sourceMap: '15:8:15:28;;;;;18:49:18:62;:69::70;:49::71:1;19:30:19:44:0;:26::45:1;20:24:20:32:0;:20::33:1;23:16:23:27:0;:31::39;;:16:::1;:8::41;24:27:24:38:0;:8::40:1;;27:25:27:36:0;;:16:::1;:8::38;31:12:31:21:0;;32::32:25;33::33:20;;30:8:34:11:1;35::35:45', + sourceTags: '0:4:pv', + }, + fingerprint: 'd87449bc71344f12ed3c9ce3f69f844cd1699df29553ec3884c1fcc92a2cfccf', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/if_statement.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/if_statement.ts new file mode 100644 index 000000000..db677aea7 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/if_statement.ts @@ -0,0 +1,45 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'IfStatement', + constructorInputs: [{ name: 'x', type: 'int' }, { name: 'y', type: 'int' }], + abi: [{ name: 'hello', inputs: [{ name: 'a', type: 'int' }, { name: 'b', type: 'int' }] }], + bytecode: + // int d = a + b + 'OP_2OVER OP_ADD ' + // d = d - a + + 'OP_DUP OP_4 OP_PICK OP_SUB ' + // if (d == x - 2) { + + 'OP_DUP OP_3 OP_ROLL OP_2 OP_SUB OP_NUMEQUAL OP_IF ' + // int c = d + b + + 'OP_DUP OP_5 OP_PICK OP_ADD ' + // d = a + c + + 'OP_4 OP_PICK OP_OVER OP_ADD OP_ROT OP_DROP OP_SWAP ' + // require(c > d) + + 'OP_2DUP OP_LESSTHAN OP_VERIFY ' + // } else { + + 'OP_DROP OP_ELSE ' + // require(d == a) } + + 'OP_DUP OP_4 OP_PICK OP_NUMEQUALVERIFY OP_ENDIF ' + // d = d + a + + 'OP_DUP OP_4 OP_ROLL OP_ADD ' + // require(d == y) + + 'OP_3 OP_ROLL OP_NUMEQUAL ' + + 'OP_NIP OP_NIP OP_NIP', + debug: { + bytecode: '70937654799476537a52949c6376557993547978937b757c6e9f6975677654799d6876547a93537a9c777777', + logs: [], + requires: [ + { ip: 28, line: 8 }, + { ip: 34, line: 10 }, + { ip: 43, line: 13 }, + ], + sourceMap: '3:16:3:21;::::1;4:12:4:13:0;:16::17;;:12:::1;5::5:13:0;:17::18;;:21::22;:17:::1;:12;:24:9:9:0;6:20:6:21;:24::25;;:20:::1;7:16:7:17:0;;:20::21;:16:::1;:12::22;;;8:20:8:25:0;::::1;:12::27;5:24:9:9;9:15:11::0;10:20:10:21;:25::26;;:12::28:1;9:15:11:9;12:12:12:13:0;:16::17;;:12:::1;13:21:13:22:0;;:8::24:1;2:33:14:5;;', + sourceTags: '27:27:sc;41:43:sc', + }, + fingerprint: 'b5f6c8b6bd5a7e4bfa2a596b5639fac59338e9cbf93673abc8c32fd4565f2846', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/if_statement_number_units-logs.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/if_statement_number_units-logs.ts new file mode 100644 index 000000000..e87c0baaf --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/if_statement_number_units-logs.ts @@ -0,0 +1,39 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [], + abi: [{ name: 'hello', inputs: [{ name: 'a', type: 'int' }, { name: 'b', type: 'int' }] }], + bytecode: + // if (a == b - 2 minutes) { + 'OP_2DUP OP_SWAP 78 OP_SUB OP_NUMEQUAL OP_IF ' + // require(false); + + 'OP_0 OP_VERIFY ' + // } else if (b == 2 weeks) + + 'OP_ELSE OP_OVER 007512 OP_NUMEQUAL ' + // require(a == 20 seconds); + + 'OP_IF OP_DUP 14 OP_NUMEQUALVERIFY ' + // else { + + 'OP_ELSE ' + // require(true == !!!false); + + 'OP_1 OP_0 OP_NOT OP_NOT OP_NOT OP_NUMEQUALVERIFY ' + // } + + 'OP_ENDIF OP_ENDIF OP_2DROP OP_1', + fingerprint: '3e2c04970e538e423ce77e999ec3d3fe25e286141f4a57aa6ce77c0d1719ccc9', + debug: { + bytecode: '6e7c0178949c6300696778030075129c637601149d6751009191919d68686d51', + sourceMap: '3:12:3:18;;:21::30;:17:::1;:12;:32:5:9:0;4:20:4:25;:12::27:1;5:15:9:9:0;:19:5:20;:24::31;:19:::1;6:12:6:37:0;:20::21;:25::35;:12::37:1;7:13:9:9:0;8:20:8:24;:31::36;:30:::1;:29;:28;:12::38;7:13:9:9;5:15;2:33:12:5;', + logs: [ + { + ip: 25, + line: 11, + data: [{ stackIndex: 0, type: 'int', ip: 25 }, { stackIndex: 1, type: 'int', ip: 25 }], + }, + ], + requires: [{ ip: 7, line: 4 }, { ip: 15, line: 6 }, { ip: 22, line: 8 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/if_statement_number_units.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/if_statement_number_units.ts new file mode 100644 index 000000000..8cd822a99 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/if_statement_number_units.ts @@ -0,0 +1,33 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [], + abi: [{ name: 'hello', inputs: [{ name: 'a', type: 'int' }, { name: 'b', type: 'int' }] }], + bytecode: + // if (a == b - 2 minutes) { + 'OP_2DUP OP_SWAP 78 OP_SUB OP_NUMEQUAL OP_IF ' + // require(false); + + 'OP_0 OP_VERIFY ' + // } else if (b == 2 weeks) + + 'OP_ELSE OP_OVER 007512 OP_NUMEQUAL ' + // require(a == 20 seconds); + + 'OP_IF OP_DUP 14 OP_NUMEQUALVERIFY ' + // else { + + 'OP_ELSE ' + // require(true == !!!false); + + 'OP_1 OP_0 OP_NOT OP_NOT OP_NOT OP_NUMEQUALVERIFY ' + // } + + 'OP_ENDIF OP_ENDIF OP_2DROP OP_1', + fingerprint: '3e2c04970e538e423ce77e999ec3d3fe25e286141f4a57aa6ce77c0d1719ccc9', + debug: { + bytecode: '6e7c0178949c6300696778030075129c637601149d6751009191919d68686d51', + sourceMap: '3:12:3:18;;:21::30;:17:::1;:12;:32:5:9:0;4:20:4:25;:12::27:1;5:15:9:9:0;:19:5:20;:24::31;:19:::1;6:12:6:37:0;:20::21;:25::35;:12::37:1;7:13:9:9:0;8:20:8:24;:31::36;:30:::1;:29;:28;:12::38;7:13:9:9;5:15;2:33:10:5;', + logs: [], + requires: [{ ip: 7, line: 4 }, { ip: 15, line: 6 }, { ip: 22, line: 8 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/increment_decrement.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/increment_decrement.ts new file mode 100644 index 000000000..59d1b4593 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/increment_decrement.ts @@ -0,0 +1,32 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'IncrementDecrement', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [] }], + bytecode: + // int x = 5; + 'OP_5 ' + // x++; + + 'OP_DUP OP_1ADD ' + // require(x == 6); + + 'OP_DUP OP_6 OP_NUMEQUALVERIFY ' + // x--; + + 'OP_DUP OP_1SUB ' + // require(x == 5); + + 'OP_5 OP_NUMEQUAL ' + // Cleanup + + 'OP_NIP OP_NIP', + fingerprint: '5ccf48b6901709f59aaa5fab621021cf8598fb6fdd591f7f714902070c84bb85', + debug: { + bytecode: '55768b76569d768c559c7777', + sourceMap: '3:16:3:17;4:8:4:9;:::11:1;5:16:5:17:0;:21::22;:8::24:1;7::7:9:0;:::11:1;8:21:8:22:0;:8::24:1;2:21:9:5;', + logs: [], + requires: [{ ip: 5, line: 5 }, { ip: 10, line: 8 }], + sourceTags: '10:11:sc', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/int_to_byte.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/int_to_byte.ts new file mode 100644 index 000000000..ba8bda48f --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/int_to_byte.ts @@ -0,0 +1,26 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'IntToByte', + constructorInputs: [], + abi: [{ name: 'hello', inputs: [{ name: 'a', type: 'int' }, { name: 'b', type: 'bytes1' }] }], + bytecode: + // Implicit parameter type enforcement + 'OP_SWAP OP_SIZE OP_1 OP_EQUALVERIFY ' + // byte c = toPaddedBytes(a, 1); + + 'OP_SWAP OP_1 OP_NUM2BIN ' + // require(b == c); + + 'OP_EQUAL', + fingerprint: 'cbccef08f54b0452cabaf643501a4903dab79b0d676896399771131fea3a0ad1', + debug: { + bytecode: '7c8251887c518087', + sourceMap: '2:26:2:32;;;;3:31:3;:34::35;:17::36:1;4:8:4:24', + logs: [], + requires: [{ ip: 8, line: 4 }], + sourceTags: '0:3:pv', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/integer_formatting.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/integer_formatting.ts new file mode 100644 index 000000000..0425e700d --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/integer_formatting.ts @@ -0,0 +1,21 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'IntegerFormatting', + constructorInputs: [], + abi: [ + { name: 'test', inputs: [] }, + ], + bytecode: '0010a5d4e800 0010a5d4e800 0010a5d4e800 0010a5d4e800 0010a5d4e800 OP_4 OP_ROLL OP_OVER OP_NUMEQUALVERIFY OP_3 OP_ROLL OP_OVER OP_NUMEQUALVERIFY OP_ROT OP_OVER OP_NUMEQUALVERIFY OP_NUMEQUAL', + debug: { + bytecode: '060010a5d4e800060010a5d4e800060010a5d4e800060010a5d4e800060010a5d4e800547a789d537a789d7b789d9c', + logs: [], + requires: [{ ip: 8, line: 10 }, { ip: 12, line: 11 }, { ip: 15, line: 12 }, { ip: 17, line: 13 }], + sourceMap: '3:26:3:30;4::4;5::5:43;6:23:6:30;8:22:8:35;10:16:10:27;;:31::38;:8::40:1;11:16:11:27:0;;:31::38;:8::40:1;12:16:12:27:0;:31::38;:8::40:1;13::13:37', + }, + fingerprint: '0b4ca541ca3bfd698bda9798bc6ce5565513848fe325360248ec00266d84c874', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/invert.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/invert.ts new file mode 100644 index 000000000..e70fcaa6a --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/invert.ts @@ -0,0 +1,25 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [] }], + bytecode: + // bytes4 x = 0x00000000; + '00000000 ' + // bytes4 y = ~x; + + 'OP_INVERT ' + // require(y == 0xffffffff); + + 'ffffffff OP_EQUAL', + fingerprint: '75fe92c7557ba121b07d4d7bf705fdcdb925d98a7ccd669e499eadeacba041a9', + debug: { + bytecode: '04000000008304ffffffff87', + sourceMap: '3:19:3:29;4::4:21:1;6:21:6:31:0;:8::33:1', + logs: [], + requires: [{ ip: 4, line: 6 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/log_intermediate_results.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/log_intermediate_results.ts new file mode 100644 index 000000000..16084574d --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/log_intermediate_results.ts @@ -0,0 +1,38 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'LogIntermediateResults', + constructorInputs: [{ name: 'owner', type: 'pubkey' }], + abi: [{ name: 'test_log_intermediate_result', inputs: [] }], + bytecode: 'OP_HASH256 OP_SIZE OP_NIP 20 OP_NUMEQUAL', + debug: { + bytecode: 'aa827701209c', + sourceMap: '3:29:5:47:1;6:16:6:33;;:37::39:0;:8::74:1', + logs: [ + { + ip: 1, + line: 4, + data: [ + { + stackIndex: 0, + type: 'bytes32', + ip: 1, + transformations: 'OP_SHA256', + }, + ], + }, + ], + requires: [ + { + ip: 6, + line: 6, + message: 'doubleHash should be 32 bytes', + }, + ], + }, + fingerprint: '47bb8f4ee4f62d7ecefc7f43fd7b61d2da71767df35d3dfd90149719287a0860', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/mecenas.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/mecenas.ts new file mode 100644 index 000000000..4a9c8e095 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/mecenas.ts @@ -0,0 +1,76 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Mecenas', + constructorInputs: [ + { name: 'recipient', type: 'bytes20' }, + { name: 'funder', type: 'bytes20' }, + { name: 'pledge', type: 'int' }, + { name: 'period', type: 'int' }, + ], + abi: [ + { name: 'receive', inputs: [] }, + { name: 'reclaim', inputs: [{ name: 'pk', type: 'pubkey' }, { name: 's', type: 'sig' }] }, + ], + bytecode: + // function receive + 'OP_4 OP_PICK OP_0 OP_NUMEQUAL OP_IF ' + // require(this.age >= period) + + 'OP_3 OP_ROLL OP_CHECKSEQUENCEVERIFY OP_DROP ' + // require(tx.inputs.length == 1) + + 'OP_TXINPUTCOUNT OP_1 OP_NUMEQUALVERIFY ' + // require(tx.outputs[0].lockingBytecode == new LockingBytecodeP2PKH(recipient)) + + 'OP_0 OP_OUTPUTBYTECODE 76a914 OP_ROT OP_CAT 88ac OP_CAT OP_EQUALVERIFY ' + // int minerFee = 1000 + + 'e803 ' + // int currentValue = tx.inputs[this.activeInputIndex].value + + 'OP_INPUTINDEX OP_UTXOVALUE ' + // int changeValue = currentValue - pledge - minerFee + + 'OP_DUP OP_4 OP_PICK OP_SUB OP_2 OP_PICK OP_SUB ' + // if (changeValue <= pledge + minerFee) { + + 'OP_DUP OP_5 OP_PICK OP_4 OP_PICK OP_ADD OP_LESSTHANOREQUAL OP_IF ' + // require(tx.outputs[0].value == currentValue - minerFee) + + 'OP_0 OP_OUTPUTVALUE OP_2OVER OP_SWAP OP_SUB OP_NUMEQUALVERIFY ' + // } else { + + 'OP_ELSE ' + // require(tx.outputs[0].value == pledge) + + 'OP_0 OP_OUTPUTVALUE OP_5 OP_PICK OP_NUMEQUALVERIFY ' + // require( + // tx.outputs[1].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode + // ) + + 'OP_1 OP_OUTPUTBYTECODE OP_INPUTINDEX OP_UTXOBYTECODE OP_EQUALVERIFY ' + // require(tx.outputs[1].value == changeValue) } + + 'OP_1 OP_OUTPUTVALUE OP_OVER OP_NUMEQUALVERIFY ' + // Cleanup + + 'OP_ENDIF OP_2DROP OP_2DROP OP_2DROP OP_1 OP_ELSE ' + // function reclaim + + 'OP_4 OP_ROLL OP_1 OP_NUMEQUALVERIFY ' + // require(hash160(pk) == funder) + + 'OP_4 OP_PICK OP_HASH160 OP_ROT OP_EQUALVERIFY ' + // require(checkSig(s, pk)) + + 'OP_4 OP_ROLL OP_4 OP_ROLL OP_CHECKSIG ' + // Cleanup + + 'OP_NIP OP_NIP OP_NIP OP_ENDIF', + debug: { + bytecode: '5479009c63537ab275c3519d00cd0376a9147b7e0288ac7e8802e803c0c676547994527994765579547993a16300cc707c949d6700cc55799d51cdc0c78851cc789d686d6d6d5167547a519d5479a97b88547a547aac77777768', + logs: [], + requires: [ + { ip: 11, line: 3 }, + { ip: 15, line: 7 }, + { ip: 23, line: 10 }, + { ip: 47, line: 19 }, + { ip: 53, line: 21 }, + { ip: 58, line: 22 }, + { ip: 62, line: 23 }, + { ip: 77, line: 28 }, + { ip: 83, line: 29 }, + ], + sourceMap: '2:4:25:5;;;;;3:28:3:34;;:8::36:1;;7:16:7:32:0;:36::37;:8::39:1;10:27:10:28:0;:16::45:1;:49::84:0;:74::83;:49::84:1;;;:8::86;12:23:12:27:0;13:37:13:58;:27::65:1;14:26:14:38:0;:41::47;;:26:::1;:50::58:0;;:26:::1;18:12:18:23:0;:27::33;;:36::44;;:27:::1;:12;:46:20:9:0;19:31:19:32;:20::39:1;:43::66:0;;::::1;:12::68;20:15:24:9:0;21:31:21:32;:20::39:1;:43::49:0;;:12::51:1;22:31:22:32:0;:20::49:1;:63::84:0;:53::101:1;:12::103;23:31:23:32:0;:20::39:1;:43::54:0;:12::56:1;20:15:24:9;2:23:25:5;;;;:4;27::30::0;;;;28:24:28:26;;:16::27:1;:31::37:0;:8::39:1;29:25:29:26:0;;:28::30;;:8::33:1;27:39:30:5;;;1:0:31:1', + sourceTags: '79:81:sc', + }, + fingerprint: '82af4e70abe6257185f9fa2b9b65377949794a7f3f862a65eb3c61ec6bbff28a', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/multifunction.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/multifunction.ts new file mode 100644 index 000000000..78524d83b --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/multifunction.ts @@ -0,0 +1,39 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'MultiFunction', + constructorInputs: [{ name: 'sender', type: 'pubkey' }, { name: 'recipient', type: 'pubkey' }, { name: 'timeout', type: 'int' }], + abi: [ + { name: 'transfer', inputs: [{ name: 'recipientSig', type: 'sig' }] }, + { name: 'timeout', inputs: [{ name: 'senderSig', type: 'sig' }] }, + ], + bytecode: + // function transfer + 'OP_3 OP_PICK OP_0 OP_NUMEQUAL OP_IF ' + // require(checkSig(recipientSig, recipient)) + + 'OP_4 OP_ROLL OP_ROT OP_CHECKSIG ' + + 'OP_NIP OP_NIP OP_NIP OP_ELSE ' + // function timeout + + 'OP_3 OP_ROLL OP_1 OP_NUMEQUALVERIFY ' + // require(checkSig(senderSig, sender)) + + 'OP_3 OP_ROLL OP_SWAP OP_CHECKSIGVERIFY ' + // require(tx.time >= timeout) + + 'OP_SWAP OP_CHECKLOCKTIMEVERIFY OP_2DROP OP_1 ' + + 'OP_ENDIF', + debug: { + bytecode: '5379009c63547a7bac77777767537a519d537a7cad7cb16d5168', + logs: [], + requires: [ + { ip: 12, line: 7 }, + { ip: 23, line: 11 }, + { ip: 25, line: 12 }, + ], + sourceMap: '6:4:8:5;;;;;7:25:7:37;;:39::48;:8::51:1;6:40:8:5;;;:4;10::13::0;;;;11:25:11:34;;:36::42;:8::45:1;12:27:12:34:0;:8::36:1;10:36:13:5;;1:0:14:1', + sourceTags: '9:11:sc', + }, + fingerprint: '4367893ec13aecfe4624b6d5b12681b9782de30dd657d1313eed269faba937e4', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/multifunction_if_statements.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/multifunction_if_statements.ts new file mode 100644 index 000000000..b7aa92c1a --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/multifunction_if_statements.ts @@ -0,0 +1,71 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'MultiFunctionIfStatements', + constructorInputs: [{ name: 'x', type: 'int' }, { name: 'y', type: 'int' }], + abi: [ + { name: 'transfer', inputs: [{ name: 'a', type: 'int' }, { name: 'b', type: 'int' }] }, + { name: 'timeout', inputs: [{ name: 'b', type: 'int' }] }, + ], + bytecode: + // function transfer + 'OP_2 OP_PICK OP_0 OP_NUMEQUAL OP_IF ' + // int d = a + b + + 'OP_3 OP_PICK OP_5 OP_PICK OP_ADD ' + // d = d - a + + 'OP_DUP OP_5 OP_PICK OP_SUB ' + // if (d == x && bool(x)) { + + 'OP_DUP OP_3 OP_PICK OP_NUMEQUAL OP_3 OP_ROLL OP_0NOTEQUAL OP_BOOLAND OP_IF ' + // int c = d + b + + 'OP_DUP OP_6 OP_PICK OP_ADD ' + // d = a + c + + 'OP_5 OP_PICK OP_OVER OP_ADD OP_ROT OP_DROP OP_SWAP ' + // require(c > d) + + 'OP_2DUP OP_LESSTHAN OP_VERIFY ' + // } else { + + 'OP_DROP OP_ELSE ' + // d = a } + + 'OP_4 OP_PICK OP_NIP OP_ENDIF ' + // d = d + a + + 'OP_DUP OP_5 OP_ROLL OP_ADD ' + // require(d == y) + + 'OP_3 OP_ROLL OP_NUMEQUALVERIFY ' + + 'OP_2DROP OP_2DROP OP_1 OP_ELSE ' + // function timeout + + 'OP_ROT OP_1 OP_NUMEQUALVERIFY ' + // int d = b + + 'OP_2 OP_PICK ' + // d = d + 2 + + 'OP_DUP OP_2 OP_ADD ' + // if (d == x) { + + 'OP_DUP OP_3 OP_ROLL OP_NUMEQUAL OP_IF ' + // int c = d + b + + 'OP_DUP OP_4 OP_PICK OP_ADD ' + // d = c + d + + 'OP_2DUP OP_ADD OP_ROT OP_DROP OP_SWAP ' + // require(c > d) } + + 'OP_2DUP OP_LESSTHAN OP_VERIFY ' + + 'OP_DROP OP_ENDIF ' + // d = b + + '' + // require(d == y) + + 'OP_2SWAP OP_NUMEQUAL ' + + 'OP_NIP OP_NIP OP_ENDIF', + debug: { + bytecode: '5279009c635379557993765579947653799c537a929a6376567993557978937b757c6e9f6975675479776876557a93537a9d6d6d51677b519d527976529376537a9c63765479936e937b757c6e9f697568729c777768', + logs: [], + requires: [ + { ip: 38, line: 8 }, + { ip: 51, line: 13 }, + { ip: 80, line: 22 }, + { ip: 85, line: 25 }, + ], + sourceMap: '2:4:14:5;;;;;3:16:3:17;;:20::21;;:16:::1;4:12:4:13:0;:16::17;;:12:::1;5::5:13:0;:17::18;;:12:::1;:27::28:0;;:22::29:1;:12;:31:9:9:0;6:20:6:21;:24::25;;:20:::1;7:16:7:17:0;;:20::21;:16:::1;:12::22;;;8:20:8:25:0;::::1;:12::27;5:31:9:9;9:15:11::0;10:16:10:17;;:12::18:1;9:15:11:9;12:12:12:13:0;:16::17;;:12:::1;13:21:13:22:0;;:8::24:1;2:36:14:5;;;:4;16::26::0;;;17:16:17:17;;18:12:18:13;:16::17;:12:::1;19::19:13:0;:17::18;;:12:::1;:20:23:9:0;20::20:21;:24::25;;:20:::1;21:16:21:21:0;::::1;:12::22;;;22:20:22:25:0;::::1;:12::27;19:20:23:9;;24:12:25:22:0;25:8::24:1;16:28:26:5;;1:0:27:1', + sourceTags: '37:37:sc;79:79:sc;83:84:sc', + }, + fingerprint: 'f57b39c9be272a31d1aedd678087b7dd327095fde647570149d237fb34401469', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/multiline_array_multisig.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/multiline_array_multisig.ts new file mode 100644 index 000000000..f0c0962bb --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/multiline_array_multisig.ts @@ -0,0 +1,31 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [], + abi: [{ name: 'cms', inputs: [{ name: 's', type: 'sig' }, { name: 'pk', type: 'pubkey' }] }], + bytecode: + // require(checkMultiSig( + 'OP_0 ' + // s, sig(0x00) + + 'OP_SWAP 00 ' + // ], [ + + 'OP_2 ' + // pk, pubkey(0x00) + + 'OP_4 OP_ROLL 00 ' + // ] + + 'OP_2 ' + // )); + + 'OP_CHECKMULTISIG', + fingerprint: '4a9d07771fb134fdd0b1b9ca1aa0e33a2621d9492e9d2bf9b8258fbd3f531296', + debug: { + bytecode: '007c010052547a010052ae', + sourceMap: '3:16:9:9;5::5:17;:23::27;4:12:6:13:1;7:16:7:18:0;;:27::31;6:15:8:13:1;3:8:9:11', + logs: [], + requires: [{ ip: 9, line: 3 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/multiline_statements.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/multiline_statements.ts new file mode 100644 index 000000000..104434604 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/multiline_statements.ts @@ -0,0 +1,23 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'MultilineStatements', + constructorInputs: [{ name: 'x', type: 'int' }, { name: 'y', type: 'string' }], + abi: [{ name: 'spend', inputs: [{ name: 'a', type: 'int' }, { name: 'b', type: 'string' }] }], + bytecode: 'OP_ROT OP_SWAP OP_2 OP_SUB OP_NUMEQUAL OP_2 OP_PICK OP_2 OP_PICK OP_EQUAL OP_BOOLAND OP_IF OP_0 OP_VERIFY OP_ELSE OP_OVER 48656c6c6f20 OP_2 OP_PICK OP_CAT OP_EQUAL OP_IF OP_DUP 576f726c64 OP_EQUALVERIFY OP_ELSE OP_1 OP_0 OP_NOT OP_NOT OP_NOT OP_NUMEQUALVERIFY OP_ENDIF OP_ENDIF OP_2DROP OP_1', + debug: { + bytecode: '7b7c52949c52795279879a63006967780648656c6c6f2052797e87637605576f726c64886751009191919d68686d51', + logs: [], + requires: [ + { ip: 15, line: 11 }, + { ip: 26, line: 14 }, + { ip: 33, line: 18 }, + ], + sourceMap: '9:12:9:13;:17::18;:21::22;:17:::1;:12;10::10:13:0;;:17::18;;:12:::1;9;10:20:12:9:0;11::11:25;:12::27:1;12:15:19:9:0;:19:12:20;:24::32;13:10:13:11;;12:24:::1;:19;13:13:17:9:0;15:16:15:17;:21::28;14:12:16:14:1;17:15:19:9:0;18:20:18:24;:31::36;:30:::1;:29;:28;:12::38;17:15:19:9;12;8:6:20:5;', + }, + fingerprint: 'd2aa37425c07883d3c7df823103f88a6a061d3efca1e6317b9e72e0ad4bd3611', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/multiplication.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/multiplication.ts new file mode 100644 index 000000000..e3b4618ef --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/multiplication.ts @@ -0,0 +1,25 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [{ name: 'x', type: 'int' }], + abi: [{ name: 'hello', inputs: [] }], + bytecode: + // int myVariable = 10 - 4; + 'OP_10 OP_4 OP_SUB ' + // int myOtherVariable = 20 * myVariable % 2; + + '14 OP_MUL OP_2 OP_MOD ' + // require(myOtherVariable > x); + + 'OP_LESSTHAN', + fingerprint: '74e026fb37d71dac744827708122e9b318eda1b8beca52c272c4a6f888cc1617', + debug: { + bytecode: '5a549401149552979f', + sourceMap: '3:25:3:27;:30::31;:25:::1;4:30:4:32:0;:::45:1;:48::49:0;:30:::1;5:8:5:37', + logs: [], + requires: [{ ip: 9, line: 5 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/num2bin.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/num2bin.ts new file mode 100644 index 000000000..44755a7de --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/num2bin.ts @@ -0,0 +1,23 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [], + abi: [{ name: 'hello', inputs: [] }], + bytecode: + // bytes2 byte_ = toPaddedBytes(10, 2); + 'OP_10 OP_2 OP_NUM2BIN ' + // require(int(byte_) == 10); + + 'OP_BIN2NUM OP_10 OP_NUMEQUAL', + fingerprint: 'df5031d5e4eae8bc40921091e25734aeee6cb7e35b9f40103770e7394e721f63', + debug: { + bytecode: '5a5280815a9c', + sourceMap: '3:37:3:39;:41::42;:23::43:1;4:16:4:26;:30::32:0;:8::34:1', + logs: [], + requires: [{ ip: 6, line: 4 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/num2bin_variable.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/num2bin_variable.ts new file mode 100644 index 000000000..3ff3781fe --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/num2bin_variable.ts @@ -0,0 +1,21 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Num2Bin', + constructorInputs: [], + abi: [ + { name: 'spend', inputs: [{ name: 'size', type: 'int' }] }, + ], + bytecode: 'OP_10 OP_SWAP OP_NUM2BIN OP_BIN2NUM OP_10 OP_NUMEQUAL', + debug: { + bytecode: '5a7c80815a9c', + logs: [], + requires: [{ ip: 6, line: 4 }], + sourceMap: '3:36:3:38;:40::44;:22::45:1;4:16:4:26;:30::32:0;:8::34:1', + }, + fingerprint: '7ebb008689b080dce1061a508173a617c58b68202899cfd17a32f1a5decd4bb6', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/p2palindrome.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/p2palindrome.ts new file mode 100644 index 000000000..d1fb7c1ac --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/p2palindrome.ts @@ -0,0 +1,21 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'P2Palindrome', + constructorInputs: [], + abi: [ + { name: 'spend', inputs: [{ name: 'palindrome', type: 'string' }] }, + ], + bytecode: 'OP_DUP OP_REVERSEBYTES OP_EQUAL', + debug: { + bytecode: '76bc87', + logs: [], + requires: [{ ip: 3, line: 3 }], + sourceMap: '3:16:3:26;:::36:1;:8::52', + }, + fingerprint: '4e9480ee14cf131a78be8da27e585e3d62c0e9cfa75d8338f2d51a67d84df0c7', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/p2pkh-logs.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/p2pkh-logs.ts new file mode 100644 index 000000000..37aac3b25 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/p2pkh-logs.ts @@ -0,0 +1,33 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'P2PKH', + constructorInputs: [{ name: 'pkh', type: 'bytes20' }], + abi: [{ name: 'spend', inputs: [{ name: 'pk', type: 'pubkey' }, { name: 's', type: 'sig' }] }], + bytecode: + // require(hash160(pk) == pkh); + 'OP_OVER OP_HASH160 OP_EQUALVERIFY ' + // require(checkSig(s, pk)); + + 'OP_CHECKSIG', + fingerprint: '07f5c2c2cf10439f063f3b92b9420b110614fb57b5c5015120bfca2688fedcc7', + debug: { + bytecode: '78a988ac', + sourceMap: '3:24:3:26;:16::27:1;:8::36;5::5:33', + logs: [ + { + ip: 4, + line: 4, + data: [ + { stackIndex: 0, type: 'pubkey', ip: 4 }, + { type: 'bytes20', stackIndex: 1, ip: 3 }, + { stackIndex: 1, type: 'sig', ip: 4 }, + ], + }, + ], + requires: [{ ip: 3, line: 3 }, { ip: 5, line: 5 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/p2pkh.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/p2pkh.ts new file mode 100644 index 000000000..247c93148 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/p2pkh.ts @@ -0,0 +1,26 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'P2PKH', + constructorInputs: [{ name: 'pkh', type: 'bytes20' }], + abi: [{ name: 'spend', inputs: [{ name: 'pk', type: 'pubkey' }, { name: 's', type: 'sig' }] }], + bytecode: + // require(hash160(pk) == pkh) + 'OP_OVER OP_HASH160 OP_EQUALVERIFY ' + // require(checkSig(s, pk)) + + 'OP_CHECKSIG', + debug: { + bytecode: '78a988ac', + logs: [], + requires: [ + { ip: 3, line: 3 }, + { ip: 5, line: 4 }, + ], + sourceMap: '3:24:3:26;:16::27:1;:8::36;4::4:33', + }, + fingerprint: '07f5c2c2cf10439f063f3b92b9420b110614fb57b5c5015120bfca2688fedcc7', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/p2pkh_with_assignment.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/p2pkh_with_assignment.ts new file mode 100644 index 000000000..fc2ca5fea --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/p2pkh_with_assignment.ts @@ -0,0 +1,25 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'P2PKH', + constructorInputs: [{ name: 'pkh', type: 'bytes20' }], + abi: [{ name: 'spend', inputs: [{ name: 'pk', type: 'pubkey' }, { name: 's', type: 'sig' }] }], + bytecode: + // bytes20 passedPkh = hash160(pk); + 'OP_OVER OP_HASH160 ' + // require(passedPkh == pkh); + + 'OP_EQUALVERIFY ' + // require(checkSig(s, pk)); + + 'OP_CHECKSIG', + fingerprint: '07f5c2c2cf10439f063f3b92b9420b110614fb57b5c5015120bfca2688fedcc7', + debug: { + bytecode: '78a988ac', + sourceMap: '5:36:5:38;:28::39:1;6:8:6:34;7::7:33', + logs: [], + requires: [{ ip: 3, line: 6 }, { ip: 5, line: 7 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/p2pkh_with_cast.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/p2pkh_with_cast.ts new file mode 100644 index 000000000..0ede1036a --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/p2pkh_with_cast.ts @@ -0,0 +1,26 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'P2PKH', + constructorInputs: [{ name: 'pkh', type: 'bytes20' }], + abi: [{ name: 'spend', inputs: [{ name: 'pk', type: 'pubkey' }, { name: 's', type: 'bytes65' }] }], + bytecode: + // Implicit parameter type enforcement + 'OP_ROT OP_SIZE 41 OP_EQUALVERIFY ' + // require(hash160(pk) == pkh); + + 'OP_2 OP_PICK OP_HASH160 OP_ROT OP_EQUALVERIFY ' + // require(checkSig(sig(s), pk)); + + 'OP_SWAP OP_CHECKSIG', + fingerprint: '3493678eec6aa5f1faf62552956f93d338eabc655157679978c406971658eaba', + debug: { + bytecode: '7b820141885279a97b887cac', + sourceMap: '4:30:4:39;;;;5:24:5:26;;:16::27:1;:31::34:0;:8::36:1;6:33:6:35:0;:8::38:1', + logs: [], + requires: [{ ip: 9, line: 5 }, { ip: 12, line: 6 }], + sourceTags: '0:3:pv', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/reassignment.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/reassignment.ts new file mode 100644 index 000000000..7ba3242c1 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/reassignment.ts @@ -0,0 +1,39 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Reassignment', + constructorInputs: [{ name: 'x', type: 'int' }, { name: 'y', type: 'string' }], + abi: [{ name: 'hello', inputs: [{ name: 'pk', type: 'pubkey' }, { name: 's', type: 'sig' }] }], + bytecode: + // int myVariable = 10 - 4 + 'OP_10 OP_4 OP_SUB ' + // int myOtherVariable = 20 + myVariable % 2 + + '14 OP_SWAP OP_2 OP_MOD OP_ADD ' + // require(myOtherVariable > x) + + 'OP_LESSTHAN OP_VERIFY ' + // string hw = "Hello World" + + '48656c6c6f20576f726c64 ' + // hw = hw + y + + 'OP_DUP OP_ROT OP_CAT ' + // require(ripemd160(pk) == ripemd160(hw)) + + 'OP_2 OP_PICK OP_RIPEMD160 OP_SWAP OP_RIPEMD160 OP_EQUALVERIFY ' + // require(checkSig(s, pk)) + + 'OP_ROT OP_ROT OP_CHECKSIG ' + + 'OP_NIP', + debug: { + bytecode: '5a549401147c5297939f690b48656c6c6f20576f726c64767b7e5279a67ca6887b7bac77', + logs: [], + requires: [ + { ip: 11, line: 5 }, + { ip: 21, line: 10 }, + { ip: 25, line: 11 }, + ], + sourceMap: '3:25:3:27;:30::31;:25:::1;4:30:4:32:0;:35::45;:48::49;:35:::1;:30;5:16:5:35;:8::37;7:20:7:33:0;8:13:8:15;:18::19;:13:::1;10:26:10:28:0;;:16::29:1;:43::45:0;:33::46:1;:8::48;11:25:11:26:0;:28::30;:8::33:1;2:37:12:5', + sourceTags: '23:23:sc', + }, + fingerprint: '7dfcd01f0ecb5e1dec3d2fb363b5af79eb84a15395e1b57fe9c383cb3861d634', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/simple_cast.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/simple_cast.ts new file mode 100644 index 000000000..8d3a5d1f9 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/simple_cast.ts @@ -0,0 +1,36 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [{ name: 'x', type: 'int' }, { name: 'y', type: 'string' }], + abi: [{ name: 'hello', inputs: [{ name: 's', type: 'sig' }, { name: 'pk', type: 'pubkey' }] }], + bytecode: + // int myVariable = 10 - int(true); + 'OP_10 OP_1SUB ' + // int myOtherVariable = 20 + myVariable % 2; + + '14 OP_SWAP OP_2 OP_MOD OP_ADD ' + // require(myOtherVariable > x); + + 'OP_LESSTHAN OP_VERIFY ' + // string hw = "Hello World"; + + '48656c6c6f20576f726c64 ' + // hw = hw + y; + + 'OP_DUP OP_ROT OP_CAT ' + // require(ripemd160(pk) == ripemd160(bytes(hw) + bytes(pk))); + + 'OP_3 OP_PICK OP_RIPEMD160 OP_SWAP OP_4 OP_PICK OP_CAT OP_RIPEMD160 OP_EQUALVERIFY ' + // require(checkSig(s, pk)); + + 'OP_SWAP OP_ROT OP_CHECKSIG ' + // Cleanup + + 'OP_NIP', + fingerprint: '31251ad758a9a5e8869e01e1e32d8df2ff1e9ed9b26d3dc3cc07280d437bdac2', + debug: { + bytecode: '5a8c01147c5297939f690b48656c6c6f20576f726c64767b7e5379a67c54797ea6887c7bac77', + sourceMap: '3:25:3:27;:::39:1;4:30:4:32:0;:35::45;:48::49;:35:::1;:30;5:16:5:35;:8::37;7:20:7:33:0;8:13:8:15;:18::19;:13:::1;10:26:10:28:0;;:16::29:1;:49::51:0;:61::63;;:43::64:1;:33::65;:8::67;11:25:11:26:0;:28::30;:8::33:1;2:37:12:5', + logs: [], + requires: [{ ip: 10, line: 5 }, { ip: 23, line: 10 }, { ip: 27, line: 11 }], + sourceTags: '25:25:sc', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/simple_checkdatasig.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/simple_checkdatasig.ts new file mode 100644 index 000000000..0fa49d8b0 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/simple_checkdatasig.ts @@ -0,0 +1,21 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [{ name: 's', type: 'datasig' }, { name: 'pk', type: 'pubkey' }], + abi: [{ name: 'cds', inputs: [{ name: 'data', type: 'bytes' }] }], + bytecode: + // require(checkDataSig(s, data, pk)); + 'OP_ROT OP_ROT OP_CHECKDATASIG', + fingerprint: 'ae21c0109dbe19b96e779f3e6d9b4dad3016ee311dfee3fce8af717c8d81bfff', + debug: { + bytecode: '7b7bba', + sourceMap: '3:32:3:36;:38::40;:8::43:1', + logs: [], + requires: [{ ip: 5, line: 3 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/simple_constant.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/simple_constant.ts new file mode 100644 index 000000000..27c74a10b --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/simple_constant.ts @@ -0,0 +1,23 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [], + abi: [{ name: 'hello', inputs: [] }], + bytecode: + // string constant m = "hello"; + '68656c6c6f ' + // require(m == "hello"); + + '68656c6c6f OP_EQUAL', + fingerprint: '96cc3caea92277647f1a513b9ca649fe93845566c0a29e69583a1e4bf67dbd71', + debug: { + bytecode: '0568656c6c6f0568656c6c6f87', + sourceMap: '3:28:3:35;4:21:4:28;:8::30:1', + logs: [], + requires: [{ ip: 3, line: 4 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/simple_covenant.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/simple_covenant.ts new file mode 100644 index 000000000..b0c70b7d9 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/simple_covenant.ts @@ -0,0 +1,21 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [], + abi: [{ name: 'covenant', inputs: [] }], + bytecode: + // require(tx.version == 2); + 'OP_TXVERSION OP_2 OP_NUMEQUAL', + fingerprint: '23acf2123933daf216d03e942aa31471f3a3e07987eeb591b5a3892c5a443579', + debug: { + bytecode: 'c2529c', + sourceMap: '3:16:3:26;:30::31;:8::33:1', + logs: [], + requires: [{ ip: 3, line: 3 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/simple_functions.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/simple_functions.ts new file mode 100644 index 000000000..183b1b2b9 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/simple_functions.ts @@ -0,0 +1,37 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [], + abi: [ + { name: 'hello', inputs: [{ name: 's', type: 'sig' }, { name: 'pk', type: 'pubkey' }] }, + { name: 'world', inputs: [{ name: 'a', type: 'int' }] }, + ], + bytecode: + // function hello(sig s, pubkey pk) { + 'OP_DUP OP_0 OP_NUMEQUAL OP_IF ' + // require(checkSig(s, pk)); + + 'OP_SWAP OP_ROT OP_CHECKSIG ' + // Cleanup + + 'OP_NIP ' + // } + + 'OP_ELSE ' + // function world(int a) { + + 'OP_1 OP_NUMEQUALVERIFY ' + // require(a + 5 == 10); + + 'OP_5 OP_ADD OP_10 OP_NUMEQUAL ' + // } + + 'OP_ENDIF', + fingerprint: '88a901256f34c8c655657f1efad4389dc3465a1980090f817f5d1a7df7ffd701', + debug: { + bytecode: '76009c637c7bac7767519d55935a9c68', + sourceMap: '2:4:4:5;;;;3:25:3:26;:28::30;:8::33:1;2:37:4:5;:4;6::8::0;;7:20:7:21;:16:::1;:25::27:0;:8::29:1;1:0:9:1', + logs: [], + requires: [{ ip: 7, line: 3 }, { ip: 15, line: 7 }], + sourceTags: '7:7:sc', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/simple_if_statement.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/simple_if_statement.ts new file mode 100644 index 000000000..8e54dc864 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/simple_if_statement.ts @@ -0,0 +1,33 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [{ name: 'x', type: 'int' }, { name: 'y', type: 'string' }], + abi: [{ name: 'hello', inputs: [{ name: 'a', type: 'int' }, { name: 'b', type: 'string' }] }], + bytecode: + // if (a == x - 2) { + 'OP_ROT OP_SWAP OP_2 OP_SUB OP_NUMEQUAL OP_IF ' + // require(false); + + 'OP_0 OP_VERIFY ' + // } else if (b == "Hello " + y) + + 'OP_ELSE OP_OVER 48656c6c6f20 OP_2 OP_PICK OP_CAT OP_EQUAL ' + // require(y == "World"); + + 'OP_IF OP_DUP 576f726c64 OP_EQUALVERIFY ' + // else { + + 'OP_ELSE ' + // require(true == !!!false); + + 'OP_1 OP_0 OP_NOT OP_NOT OP_NOT OP_NUMEQUALVERIFY ' + // } + + 'OP_ENDIF OP_ENDIF OP_2DROP OP_1', + fingerprint: 'b3c859908dd64be7dcc8a9382cf371527c12d56973f4a291d97692d8c2215cb0', + debug: { + bytecode: '7b7c52949c63006967780648656c6c6f2052797e87637605576f726c64886751009191919d68686d51', + sourceMap: '3:12:3:13;:17::18;:21::22;:17:::1;:12;:24:5:9:0;4:20:4:25;:12::27:1;5:15:9:9:0;:19:5:20;:24::32;:35::36;;:24:::1;:19;6:12:6:34:0;:20::21;:25::32;:12::34:1;7:13:9:9:0;8:20:8:24;:31::36;:30:::1;:29;:28;:12::38;7:13:9:9;5:15;2:36:10:5;', + logs: [], + requires: [{ ip: 9, line: 4 }, { ip: 20, line: 6 }, { ip: 27, line: 8 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/simple_multisig.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/simple_multisig.ts new file mode 100644 index 000000000..a061a15ac --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/simple_multisig.ts @@ -0,0 +1,21 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [], + abi: [{ name: 'cms', inputs: [{ name: 's', type: 'sig' }, { name: 'pk', type: 'pubkey' }] }], + bytecode: + // require(checkMultiSig([s, sig(0x00)], [pk, pubkey(0x00)])); + 'OP_0 OP_SWAP 00 OP_2 OP_4 OP_ROLL 00 OP_2 OP_CHECKMULTISIG', + fingerprint: '4a9d07771fb134fdd0b1b9ca1aa0e33a2621d9492e9d2bf9b8258fbd3f531296', + debug: { + bytecode: '007c010052547a010052ae', + sourceMap: '3:16:3:65;:31::32;:38::42;:30::44:1;:47::49:0;;:58::62;:46::64:1;:8::67', + logs: [], + requires: [{ ip: 9, line: 3 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/simple_splice.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/simple_splice.ts new file mode 100644 index 000000000..580b086b1 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/simple_splice.ts @@ -0,0 +1,25 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [{ name: 'b', type: 'bytes' }], + abi: [{ name: 'spend', inputs: [] }], + bytecode: + // bytes x = b.split(5)[1]; + 'OP_DUP OP_5 OP_SPLIT OP_NIP ' + // require(x != b); + + 'OP_2DUP OP_EQUAL OP_NOT OP_VERIFY ' + // require (b.split(4)[0] != x); + + 'OP_SWAP OP_4 OP_SPLIT OP_DROP OP_EQUAL OP_NOT', + fingerprint: '02695f7d89ef8e5ca54c56e0b66c9313a0108f34fa5b0c4cebcfa2969f662cc4', + debug: { + bytecode: '76557f776e8791697c547f758791', + sourceMap: '3:18:3:19;:26::27;:18::28:1;:::31;4:16:4:22:0;::::1;;:8::24;5:17:5:18:0;:25::26;:17::27:1;:::30;:::35;:8::37', + logs: [], + requires: [{ ip: 8, line: 4 }, { ip: 15, line: 5 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/simple_variables.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/simple_variables.ts new file mode 100644 index 000000000..acff9379e --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/simple_variables.ts @@ -0,0 +1,36 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [{ name: 'x', type: 'int' }, { name: 'y', type: 'string' }], + abi: [{ name: 'hello', inputs: [{ name: 's', type: 'sig' }, { name: 'pk', type: 'pubkey' }] }], + bytecode: + // int myVariable = 10 - 4; + 'OP_10 OP_4 OP_SUB ' + // int myOtherVariable = 20 + myVariable % 2; + + '14 OP_SWAP OP_2 OP_MOD OP_ADD ' + // require(myOtherVariable > x); + + 'OP_LESSTHAN OP_VERIFY ' + // string hw = "Hello World"; + + '48656c6c6f20576f726c64 ' + // hw = hw + y; + + 'OP_DUP OP_ROT OP_CAT ' + // require(ripemd160(pk) == ripemd160(hw)); + + 'OP_3 OP_PICK OP_RIPEMD160 OP_SWAP OP_RIPEMD160 OP_EQUALVERIFY ' + // require(checkSig(s, pk)); + + 'OP_SWAP OP_ROT OP_CHECKSIG ' + // Cleanup + + 'OP_NIP', + fingerprint: '020af19263094d41bde5bc002db29a858c512709a15b7df1319bb75cbeacb289', + debug: { + bytecode: '5a549401147c5297939f690b48656c6c6f20576f726c64767b7e5379a67ca6887c7bac77', + sourceMap: '3:25:3:27;:30::31;:25:::1;4:30:4:32:0;:35::45;:48::49;:35:::1;:30;5:16:5:35;:8::37;7:20:7:33:0;8:13:8:15;:18::19;:13:::1;10:26:10:28:0;;:16::29:1;:43::45:0;:33::46:1;:8::48;11:25:11:26:0;:28::30;:8::33:1;2:37:12:5', + logs: [], + requires: [{ ip: 11, line: 5 }, { ip: 21, line: 10 }, { ip: 25, line: 11 }], + sourceTags: '23:23:sc', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/simulating_state.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/simulating_state.ts new file mode 100644 index 000000000..bfb52fd4d --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/simulating_state.ts @@ -0,0 +1,87 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'SimulatingState', + constructorInputs: [ + { name: 'recipient', type: 'bytes20' }, + { name: 'funder', type: 'bytes20' }, + { name: 'pledgePerBlock', type: 'int' }, + { name: 'initialBlock', type: 'bytes8' }, + ], + abi: [ + { name: 'receive', inputs: [] }, + { name: 'reclaim', inputs: [{ name: 'pk', type: 'pubkey' }, { name: 's', type: 'sig' }] }, + ], + bytecode: + // function receive() { + 'OP_4 OP_PICK OP_0 OP_NUMEQUAL OP_IF ' + // bytes25 recipientLockingBytecode = new LockingBytecodeP2PKH(recipient); + + '76a914 OP_SWAP OP_CAT 88ac OP_CAT ' + // require(tx.outputs[0].lockingBytecode == recipientLockingBytecode); + + 'OP_0 OP_OUTPUTBYTECODE OP_EQUALVERIFY ' + // int initial = int(initialBlock); + + 'OP_ROT OP_BIN2NUM ' + // require(tx.time >= initial); + + 'OP_DUP OP_CHECKLOCKTIMEVERIFY OP_DROP ' + // int passedBlocks = tx.locktime - initial; + + 'OP_TXLOCKTIME OP_SWAP OP_SUB ' + // int pledge = passedBlocks * pledgePerBlock; + + 'OP_ROT OP_MUL ' + // int minerFee = 1000; + + 'e803 ' + // int currentValue = tx.inputs[this.activeInputIndex].value; + + 'OP_INPUTINDEX OP_UTXOVALUE ' + // int changeValue = currentValue - pledge - minerFee; + + 'OP_DUP OP_3 OP_PICK OP_SUB OP_2 OP_PICK OP_SUB ' + // if (changeValue <= pledge + minerFee) { + + 'OP_DUP OP_4 OP_PICK OP_4 OP_PICK OP_ADD OP_LESSTHANOREQUAL OP_IF ' + // require(tx.outputs[0].value == currentValue - minerFee); + + 'OP_0 OP_OUTPUTVALUE OP_2OVER OP_SWAP OP_SUB OP_NUMEQUALVERIFY ' + // } else { + + 'OP_ELSE ' + // require(tx.outputs[0].value == pledge); + + 'OP_0 OP_OUTPUTVALUE OP_4 OP_PICK OP_NUMEQUALVERIFY ' + // require(tx.outputs[1].value == changeValue); + + 'OP_1 OP_OUTPUTVALUE OP_OVER OP_NUMEQUALVERIFY ' + // bytes newContract = 0x08 + toPaddedBytes(tx.locktime, 8) + this.activeBytecode.split(9)... + + 'OP_8 OP_TXLOCKTIME OP_8 OP_NUM2BIN OP_CAT OP_ACTIVEBYTECODE OP_9 OP_SPLIT OP_NIP OP_CAT ' + // bytes23 newContractLock = new LockingBytecodeP2SH20(hash160(newContract)); + + 'a914 OP_OVER OP_HASH160 OP_CAT 87 OP_CAT ' + // require(tx.outputs[1].lockingBytecode == newContractLock); + + 'OP_1 OP_OUTPUTBYTECODE OP_OVER OP_EQUALVERIFY ' + // Cleanup + + 'OP_2DROP ' + // } + + 'OP_ENDIF OP_2DROP OP_2DROP OP_2DROP OP_1 OP_ELSE ' + // function reclaim(pubkey pk, sig s) { + + 'OP_4 OP_ROLL OP_1 OP_NUMEQUALVERIFY ' + // require(hash160(pk) == funder); + + 'OP_4 OP_PICK OP_HASH160 OP_ROT OP_EQUALVERIFY ' + // require(checkSig(s, pk)); + + 'OP_4 OP_ROLL OP_4 OP_ROLL OP_CHECKSIG ' + // Cleanup + + 'OP_NIP OP_NIP OP_NIP ' + // } + + 'OP_ENDIF', + fingerprint: 'b9b8bcda69ec19cdbdf4a9f9fadcc2834ed3302298c5b3c2086b55fc95db983d', + debug: { + bytecode: '5479009c630376a9147c7e0288ac7e00cd887b8176b175c57c947b9502e803c0c676537994527994765479547993a16300cc707c949d6700cc54799d51cc789d58c558807ec1597f777e02a91478a97e01877e51cd78886d686d6d6d5167547a519d5479a97b88547a547aac77777768', + sourceMap: '7:4:46:5;;;;;9:43:9:78;:68::77;:43::78:1;;;10:27:10:28:0;:16::45:1;:8::75;13:26:13:38:0;:22::39:1;14:27:14:34:0;:8::36:1;;17:27:17:38:0;:41::48;:27:::1;18:36:18:50:0;:21:::1;21:23:21:27:0;22:37:22:58;:27::65:1;23:26:23:38:0;:41::47;;:26:::1;:50::58:0;;:26:::1;28:12:28:23:0;:27::33;;:36::44;;:27:::1;:12;:46:30:9:0;29:31:29:32;:20::39:1;:43::66:0;;::::1;:12::68;30:15:45:9:0;32:31:32:32;:20::39:1;:43::49:0;;:12::51:1;33:31:33:32:0;:20::39:1;:43::54:0;:12::56:1;39:32:39:36:0;:53::64;:66::67;:39::68:1;:32;:71::90:0;:97::98;:71::99:1;:::102;:32;43:38:43:85:0;:72::83;:64::84:1;:38::85;;;44:31:44:32:0;:20::49:1;:53::68:0;:12::70:1;30:15:45:9;;7:23:46:5;;;;:4;48::51::0;;;;49:24:49:26;;:16::27:1;:31::37:0;:8::39:1;50:25:50:26:0;;:28::30;;:8::33:1;48:39:51:5;;;1:0:52:1', + logs: [], + requires: [ + { ip: 16, line: 10 }, + { ip: 20, line: 14 }, + { ip: 50, line: 29 }, + { ip: 56, line: 32 }, + { ip: 60, line: 33 }, + { ip: 80, line: 44 }, + { ip: 96, line: 49 }, + { ip: 102, line: 50 }, + ], + sourceTags: '77:77:sc;98:100:sc', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/slice.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/slice.ts new file mode 100644 index 000000000..81cc393e1 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/slice.ts @@ -0,0 +1,25 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Slice', + constructorInputs: [{ name: 'pkh', type: 'bytes20' }], + abi: [{ name: 'spend', inputs: [] }], + bytecode: 'OP_INPUTINDEX OP_UTXOBYTECODE 17 OP_SPLIT OP_DROP OP_3 OP_SPLIT OP_NIP OP_EQUAL', + debug: { + bytecode: 'c0c701177f75537f7787', + sourceMap: '3:36:3:57;:26::74:1;:84::86:0;:26::87:1;;:81::82:0;:26::87:1;;4:8:4:34', + logs: [], + requires: [ + { + ip: 10, + line: 4, + message: undefined, + }, + ], + }, + fingerprint: 'd8574d80ab674df33841526cf2767c09a9dc02d2faea91746465e22e3b81ae3a', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/slice_optimised.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/slice_optimised.ts new file mode 100644 index 000000000..f57698f4e --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/slice_optimised.ts @@ -0,0 +1,25 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Slice', + constructorInputs: [{ name: 'data', type: 'bytes32' }], + abi: [{ name: 'spend', inputs: [] }], + bytecode: '14 OP_SPLIT OP_DROP OP_0 14 OP_NUM2BIN OP_EQUAL', + debug: { + bytecode: '01147f750001148087', + sourceMap: '3:36:3:38;:22::39:1;;4:37:4:38:0;:40::42;:23::43:1;:8::45', + logs: [], + requires: [ + { + ip: 8, + line: 4, + message: undefined, + }, + ], + }, + fingerprint: '779b8278e2831727686ad5c96b85bb01bab5f2692edd34d703b9d437da77ac03', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/slice_variable_parameter.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/slice_variable_parameter.ts new file mode 100644 index 000000000..6c961430c --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/slice_variable_parameter.ts @@ -0,0 +1,25 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Slice', + constructorInputs: [{ name: 'pkh', type: 'bytes20' }], + abi: [{ name: 'spend', inputs: [] }], + bytecode: + // int x = 3; + 'OP_3 ' + // bytes actualPkh = tx.inputs[this.activeInputIndex].lockingBytecode.slice(x, 23); + + 'OP_INPUTINDEX OP_UTXOBYTECODE 17 OP_SPLIT OP_DROP OP_SWAP OP_SPLIT OP_NIP ' + // require(pkh == actualPkh); + + 'OP_EQUAL', + fingerprint: '44e982ad69fd5e934e67da5c478f09dcfd07e925b6817e033c3ac2c1a379be56', + debug: { + bytecode: '53c0c701177f757c7f7787', + sourceMap: '3:16:3:17;4:36:4:57;:26::74:1;:84::86:0;:26::87:1;;:81::82:0;:26::87:1;;5:8:5:34', + logs: [], + requires: [{ ip: 11, line: 5 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/split_or_slice_signature.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/split_or_slice_signature.ts new file mode 100644 index 000000000..d90745ff6 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/split_or_slice_signature.ts @@ -0,0 +1,27 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [{ name: 'signature', type: 'sig' }], + abi: [{ name: 'spend', inputs: [] }], + bytecode: + // bytes hashtype1 = signature.split(64)[1]; + 'OP_DUP 40 OP_SPLIT OP_NIP ' + // bytes1 hashtype2 = signature.slice(64, 65); + + 'OP_SWAP 41 OP_SPLIT OP_DROP 40 OP_SPLIT OP_NIP ' + // require(hashtype1 == 0x01); + + 'OP_SWAP OP_1 OP_EQUALVERIFY ' + // require(hashtype2 == 0x01); + + 'OP_1 OP_EQUAL', + fingerprint: '8ff426163800a551833469b6746f32eee36dda458516a3684dce740263312a1b', + debug: { + bytecode: '7601407f777c01417f7501407f777c51885187', + sourceMap: '4:26:4:35;:42::44;:26::45:1;:::48;5:27:5:36:0;:47::49;:27::50:1;;:43::45:0;:27::50:1;;6:16:6:25:0;:29::33;:8::35:1;7:29:7:33:0;:8::35:1', + logs: [], + requires: [{ ip: 14, line: 6 }, { ip: 17, line: 7 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/split_size.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/split_size.ts new file mode 100644 index 000000000..ff0195029 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/split_size.ts @@ -0,0 +1,28 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'SplitSize', + constructorInputs: [{ name: 'b', type: 'bytes' }], + abi: [{ name: 'spend', inputs: [] }], + bytecode: + // bytes x = b.split(b.length / 2)[1] + 'OP_DUP OP_DUP OP_SIZE OP_NIP OP_2 OP_DIV OP_SPLIT OP_NIP ' + // require(x != b) + + 'OP_2DUP OP_EQUAL OP_NOT OP_VERIFY ' + // bytes x = b.split(b.length / 2)[1] + + 'OP_SWAP OP_4 OP_SPLIT OP_DROP OP_EQUAL OP_NOT', + debug: { + bytecode: '7676827752967f776e8791697c547f758791', + logs: [], + requires: [ + { ip: 12, line: 4 }, + { ip: 19, line: 5 }, + ], + sourceMap: '3:18:3:27;;:26::34:1;;:37::38:0;:26:::1;:18::39;:::42;4:16:4:22:0;::::1;;:8::24;5:16:5:17:0;:24::25;:16::26:1;:::29;:::34;:8::36', + }, + fingerprint: '49d50376d8aa76534f29c049b987238d577ce332202a2f0da0cb74d94a027b79', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/split_typed.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/split_typed.ts new file mode 100644 index 000000000..64941c7f7 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/split_typed.ts @@ -0,0 +1,23 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'SplitTyped', + constructorInputs: [{ name: 'b', type: 'bytes' }], + abi: [{ name: 'spend', inputs: [] }], + bytecode: + // bytes4 x = b.split(4)[0]; + 'OP_DUP OP_4 OP_SPLIT OP_DROP ' + // require(x != b); + + 'OP_EQUAL OP_NOT', + fingerprint: 'f7275cb70302001a89ebddc0a65f7ead064a0f0a8887a7b8317a912696d96654', + debug: { + bytecode: '76547f758791', + sourceMap: '3:19:3:20;:27::28;:19::29:1;:::32;4:16:4:22;:8::24', + logs: [], + requires: [{ ip: 7, line: 4 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/string_concatenation.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/string_concatenation.ts new file mode 100644 index 000000000..23b618ce2 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/string_concatenation.ts @@ -0,0 +1,21 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [], + abi: [{ name: 'hello', inputs: [{ name: 'who', type: 'string' }] }], + bytecode: + // require(("hello " + who).length + 2 > 5); + '68656c6c6f20 OP_SWAP OP_CAT OP_SIZE OP_NIP OP_2 OP_ADD OP_5 OP_GREATERTHAN', + fingerprint: '142914f037f5f8f71cb7a5cad4be959f39db80fc6411fd1c0ddd02b14a620eb4', + debug: { + bytecode: '0668656c6c6f207c7e8277529355a0', + sourceMap: '3:17:3:25;:28::31;:17:::1;:16::39;;:42::43:0;:16:::1;:46::47:0;:8::49:1', + logs: [], + requires: [{ ip: 9, line: 3 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/string_with_escaped_characters.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/string_with_escaped_characters.ts new file mode 100644 index 000000000..6d2b921ea --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/string_with_escaped_characters.ts @@ -0,0 +1,31 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [{ name: 'x', type: 'int' }], + abi: [{ name: 'hello', inputs: [] }], + bytecode: + // int myVariable = 10 - 4; + 'OP_10 OP_4 OP_SUB ' + // int myOtherVariable = 20 + myVariable % 2; + + '14 OP_SWAP OP_2 OP_MOD OP_ADD ' + // require(myOtherVariable > x); + + 'OP_LESSTHAN OP_VERIFY ' + // string x1 = "Hello \n \\ ' '' \" World"; + + '48656c6c6f205c6e205c5c202720272720205c2220576f726c64 ' + // string x2 = 'Hello \n \\ " " \' World'; + + '48656c6c6f205c6e205c5c20222022205c2720576f726c64 ' + // require(ripemd160(x1) == hash160(x2)); + + 'OP_SWAP OP_RIPEMD160 OP_SWAP OP_HASH160 OP_EQUAL', + fingerprint: '71ca7d617d2d655b8d2f6dfdd9ef77cb2b1e5ca855b85b22bf791965b02f6517', + debug: { + bytecode: '5a549401147c5297939f691a48656c6c6f205c6e205c5c202720272720205c2220576f726c641848656c6c6f205c6e205c5c20222022205c2720576f726c647ca67ca987', + sourceMap: '3:25:3:27;:30::31;:25:::1;4:30:4:32:0;:35::45;:48::49;:35:::1;:30;5:16:5:35;:8::37;7:20:7:48:0;8::8:46;9:26:9:28;:16::29:1;:41::43:0;:33::44:1;:8::46', + logs: [], + requires: [{ ip: 10, line: 5 }, { ip: 18, line: 9 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/sum_input_amount.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/sum_input_amount.ts new file mode 100644 index 000000000..79a939efe --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/sum_input_amount.ts @@ -0,0 +1,36 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Loopy', + constructorInputs: [], + abi: [{ name: 'doLoop', inputs: [] }], + bytecode: + // int sum = 0; + 'OP_0 ' + // int i = 0; + + 'OP_0 ' + // do { + + 'OP_BEGIN ' + // sum = tx.inputs[i].value; + + 'OP_DUP OP_UTXOVALUE OP_ROT OP_DROP OP_SWAP ' + // i = i + 1; + + 'OP_DUP OP_1ADD OP_NIP ' + // } while (i < tx.inputs.length); + + 'OP_DUP OP_TXINPUTCOUNT OP_GREATERTHANOREQUAL OP_UNTIL ' + // require(sum > 2000); + + 'OP_SWAP d007 OP_GREATERTHAN ' + // Cleanup + + 'OP_NIP', + fingerprint: '8c4abe3e5702b4328f4d4c094e77bb69228aba3e7f7a2b51a84f9e5f119fd68d', + debug: { + bytecode: '00006576c67b757c768b7776c3a2667c02d007a077', + sourceMap: '3:18:3:19;4:16:4:17;6:8:9:39;7:28:7:29;:18::36:1;:12::37;;;8:16:8:17:0;:::21:1;:12::22;9:17:9:18:0;:21::37;6:8::39:1;;12:16:12:19:0;:22::26;:8::28:1;2:22:13:5', + logs: [{ ip: 15, line: 11, data: [{ stackIndex: 1, type: 'int', ip: 15 }] }], + requires: [{ ip: 18, line: 12 }], + sourceTags: '18:18:sc', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/token_category_comparison.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/token_category_comparison.ts new file mode 100644 index 000000000..fdbf15106 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/token_category_comparison.ts @@ -0,0 +1,21 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [], + abi: [{ name: 'send', inputs: [] }], + bytecode: + // require(tx.inputs[1].tokenCategory == 0x); + 'OP_1 OP_UTXOTOKENCATEGORY OP_0 OP_EQUAL', + fingerprint: '40bc2bb628f3d2be50aa34a77c44dafd73f3a8a826bf32ef747bd77a583ea187', + debug: { + bytecode: '51ce0087', + sourceMap: '3:26:3:27;:16::42:1;:46::48:0;:8::50:1', + logs: [], + requires: [{ ip: 4, line: 3 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/trailing_comma.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/trailing_comma.ts new file mode 100644 index 000000000..51fabc3ff --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/trailing_comma.ts @@ -0,0 +1,50 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Contract', + constructorInputs: [{ name: 'ownerPk', type: 'pubkey' }, { name: 'oraclePk', type: 'pubkey' }], + abi: [ + { + name: 'spend', + inputs: [ + { name: 'ownerSig', type: 'sig' }, + { name: 'oracleMsgSig', type: 'datasig' }, + { name: 'oracleTxSig', type: 'sig' }, + ], + }, + ], + bytecode: + // bytes oracleMessage = bytes('Spend') + bytes(12,); + '5370656e64 OP_12 OP_CAT ' + // oracleMsgSig, + + 'OP_4 OP_ROLL ' + // oracleMessage, + + 'OP_SWAP ' + // oraclePk, + + 'OP_3 OP_PICK ' + // )); + + 'OP_CHECKDATASIGVERIFY ' + // require(checkMultiSig([ + + 'OP_0 ' + // ownerSig, + + 'OP_3 OP_ROLL ' + // oracleTxSig, + + 'OP_4 OP_ROLL ' + // ], [ + + 'OP_2 ' + // ownerPk, + + 'OP_2ROT OP_SWAP ' + // ])); + + 'OP_2 OP_CHECKMULTISIG', + fingerprint: '653df414836629dae799aa48f05529eb33462214bbb29534483bc72aabb4bceb', + debug: { + bytecode: '055370656e645c7e547a7c5379bb00537a547a52717c52ae', + sourceMap: '10:36:10:43;:53::55;:30::57:1;12:12:12:24:0;;13::13:25;14::14:20;;11:8:15:11:1;16:16:22:10:0;17:12:17:20;;18::18:23;;16:30:19:9:1;20:12:21:20:0;;19:11:22:9:1;16:8::12', + logs: [], + requires: [{ ip: 10, line: 11 }, { ip: 21, line: 16 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_modifiers.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_modifiers.ts new file mode 100644 index 000000000..bbeb92c9b --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_modifiers.ts @@ -0,0 +1,95 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'TupleModifiers', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [{ name: 'n', type: 'int' }] }], + bytecode: + // int unused incremented, int constant doubled = pair(n); + // pair is inlined; the unused first target is nipped from under `doubled` + 'OP_DUP OP_DUP OP_1ADD OP_SWAP OP_2 OP_MUL OP_NIP ' + // bytes payload, bytes unused padding = 0x1234.split(1); + // the unused last target is dropped straight off the top of the stack + + '1234 OP_1 OP_SPLIT OP_DROP ' + // int acc = n; + + 'OP_ROT ' + // if (acc > 0) { + + 'OP_DUP OP_0 OP_GREATERTHAN OP_IF ' + // (acc, int unused scratch) = pair(acc); + // the unused declaration is dropped instead of parked, then the reassignment + // value folds into acc's slot + + 'OP_DUP OP_DUP OP_1ADD OP_SWAP OP_2 OP_MUL OP_DROP OP_NIP OP_ENDIF ' + // require(acc + doubled >= 0); + + 'OP_ROT OP_ADD OP_0 OP_GREATERTHANOREQUAL OP_VERIFY ' + // require(payload == 0x12); + + '12 OP_EQUAL', + fingerprint: '77f06b3b6cfe6d5e97768ab4c1649870173bfaedd960771ceec13a89356867cb', + debug: { + bytecode: '76768b7c529577021234517f757b7600a06376768b7c52957577687b9300a269011287', + sourceMap: '12:60:12:61;:55::62:1;;;;;:8::63;15:46:15:52:0;:59::60;:46::61:1;:8::62;19:18:19:19:0;' + + '20:12:20:15;:18::19;:12:::1;:21:22:9:0;21:45:21:48;:40::49:1;;;;;:12::50;;20:21:22:9;' + + '24:22:24:29:0;:16:::1;:33::34:0;:16:::1;:8::36;25:27:25:31:0;:8::33:1', + logs: [], + requires: [{ ip: 29, line: 24 }, { ip: 32, line: 25 }], + functions: [ + { + name: 'pair', + inputs: [{ name: 'x', type: 'int' }], + bytecode: '768b7c5295', + sourceMap: '6:11:6:12;:::16:1;:18::19:0;:22::23;:18:::1', + logs: [], + requires: [], + }, + ], + inlineRanges: '1:5:pair;17:21:pair', + }, + }, + }, + { + // Same contract without inlining: the unused-target drops behave identically around the + // OP_INVOKE call sites + compilerOptions: { disableInlining: true }, + artifact: { + contractName: 'TupleModifiers', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [{ name: 'n', type: 'int' }] }], + bytecode: + // OP_DEFINE pair (id 0) + '768b7c5295 OP_0 OP_DEFINE ' + // int unused incremented, int constant doubled = pair(n); + + 'OP_DUP OP_0 OP_INVOKE OP_NIP ' + // bytes payload, bytes unused padding = 0x1234.split(1); + + '1234 OP_1 OP_SPLIT OP_DROP ' + // int acc = n; + + 'OP_ROT ' + // if (acc > 0) { (acc, int unused scratch) = pair(acc); } + + 'OP_DUP OP_0 OP_GREATERTHAN OP_IF OP_DUP OP_0 OP_INVOKE OP_DROP OP_NIP OP_ENDIF ' + // require(acc + doubled >= 0); + + 'OP_ROT OP_ADD OP_0 OP_GREATERTHANOREQUAL OP_VERIFY ' + // require(payload == 0x12); + + '12 OP_EQUAL', + fingerprint: '9ded2366c857eb6d3a9405021cac16d4b2b52f740c7ed24176253851727990c4', + debug: { + bytecode: '05768b7c5295008976008a77021234517f757b7600a06376008a7577687b9300a269011287', + sourceMap: '5::7:1;;::::1;12:60:12:61:0;:55::62:1;;:8::63;15:46:15:52:0;:59::60;:46::61:1;:8::62;' + + '19:18:19:19:0;20:12:20:15;:18::19;:12:::1;:21:22:9:0;21:45:21:48;:40::49:1;;:12::50;;' + + '20:21:22:9;24:22:24:29:0;:16:::1;:33::34:0;:16:::1;:8::36;25:27:25:31:0;:8::33:1', + logs: [], + requires: [{ ip: 26, line: 24 }, { ip: 29, line: 25 }], + functions: [ + { + id: 0, + name: 'pair', + inputs: [{ name: 'x', type: 'int' }], + bytecode: '768b7c5295', + sourceMap: '6:11:6:12;:::16:1;:18::19:0;:22::23;:18:::1', + logs: [], + requires: [], + }, + ], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_reassignment.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_reassignment.ts new file mode 100644 index 000000000..de47583e3 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_reassignment.ts @@ -0,0 +1,46 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + // Tuple destructuring into existing variables: top-level renames (with the small helpers + // inlined at the call sites), pure and mixed reassignment in loops, and the interleaved + // order that parks a declaration value on the altstack mid-fold. + artifact: { + contractName: 'TupleReassignment', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [{ name: 'seed', type: 'int' }] }], + bytecode: 'OP_DUP OP_1ADD OP_2DUP OP_SWAP OP_2DUP OP_SWAP OP_0 OP_BEGIN OP_DUP OP_4 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_OVER OP_4 OP_PICK OP_SWAP OP_5 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_ROT OP_DROP OP_SWAP OP_DUP OP_1ADD OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_0 OP_BEGIN OP_DUP OP_4 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_OVER OP_4 OP_PICK OP_OVER OP_2 OP_PICK OP_MUL OP_1ADD OP_SWAP OP_ROT OP_6 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_3 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_DUP OP_0 OP_GREATERTHANOREQUAL OP_VERIFY OP_OVER OP_1ADD OP_ROT OP_DROP OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_OVER OP_4 OP_PICK OP_OVER OP_2 OP_PICK OP_MUL OP_1ADD OP_SWAP OP_ROT OP_6 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_TOALTSTACK OP_ROT OP_DROP OP_SWAP OP_FROMALTSTACK OP_DUP OP_0 OP_GREATERTHANOREQUAL OP_VERIFY OP_OVER OP_1ADD OP_ROT OP_DROP OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_2DUP OP_ROT OP_ROT OP_ADD OP_ROT OP_ADD OP_ADD OP_0 OP_GREATERTHANOREQUAL OP_VERIFY OP_2DROP OP_2DROP OP_1', + debug: { + bytecode: '768b6e7c6e7c006576549f766b637854797c557a757c6b7c6b7c6b7c6c6c6c7b757c768b77686c916675006576549f766b63785479785279958b7c7b567a757c6b7c6b7c6b7c6b7c6c6c6c6c537a757c6b7c6c7600a269788b7b7577686c916675006576529f766b63785479785279958b7c7b567a757c6b7c6b7c6b7c6b7c6c6c6c6c6b7b757c6c7600a269788b7b7577686c9166756e7b7b937b939300a2696d6d51', + sourceMap: '17:16:18:20;18:::24:1;21:22:21:26:0;:17::27:1;24:26:24:30:0;:21::31:1;27::27:22:0;:8:29:9;:24:27:25;:28::29;:24:::1;;;:42:29:9:0;28:26:28:27;:29::30;;:21::31:1;:12::32;;;;;;;;;;;;;;;;27:35:27:36:0;:::40:1;:31;:42:29:9;;:8;;;33:21:33:22:0;:8:36:9;:24:33:25;:28::29;:24:::1;;;:42:36:9:0;34:34:34:35;:37::38;;:29::39:1;;;;;;;:12::40;;;;;;;;;;;;;;;;;;;;;;;35:20:35:22:0;:26::27;:20:::1;:12::29;33:35:33:36:0;:::40:1;:31;;::36:9;:42;;:8;;;40:21:40:22:0;:8:43:9;:24:40:25;:28::29;:24:::1;;;:42:43:9:0;41:34:41:35;:37::38;;:29::39:1;;;;;;;:12::40;;;;;;;;;;;;;;;;;;;;;42:20:42:22:0;:26::27;:20:::1;:12::29;40:35:40:36:0;:::40:1;:31;;::43:9;:42;;:8;;;46:24:46:28:0;48:16:48:17;:20::21;:16:::1;:24::25:0;:16:::1;:::29;:33::34:0;:16:::1;:8::36;16:29:49:5;;', + logs: [], + requires: [ + { ip: 86, line: 35 }, + { ip: 139, line: 42 }, + { ip: 159, line: 48 }, + ], + sourceTags: '34:36:fu;37:40:lc;41:41:sc;87:91:fu;92:95:lc;96:96:sc;140:144:fu;145:148:lc;149:149:sc', + functions: [ + { + name: 'swap', + inputs: [{ name: 'x', type: 'int' }, { name: 'y', type: 'int' }], + bytecode: '7c', + sourceMap: '7:14:7:15', + logs: [], + requires: [], + }, + { + name: 'step', + inputs: [{ name: 'x', type: 'int' }, { name: 'y', type: 'int' }], + bytecode: '785279958b7c7b', + sourceMap: '12:11:12:12;:15::16;;:11:::1;:::20;:22::23:0;:25::26', + logs: [], + requires: [], + }, + ], + inlineRanges: '3:3:swap;5:5:swap;17:17:swap;53:59:step;108:114:step;151:151:swap', + }, + fingerprint: '3ab69a954ec4bf9ceeb58eceb8ead206195032edf6627155eb82f528403c94dd', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_reassignment_after_final_read.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_reassignment_after_final_read.ts new file mode 100644 index 000000000..21bc2d29d --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_reassignment_after_final_read.ts @@ -0,0 +1,76 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'ReassignAfterFinalRead', + constructorInputs: [], + abi: [ + { name: 'ifBranch', inputs: [{ name: 'x', type: 'int' }] }, + { name: 'whileLoop', inputs: [{ name: 'x', type: 'int' }] }, + ], + bytecode: + // function ifBranch(int x) { + 'OP_DUP OP_0 OP_NUMEQUAL OP_IF ' + // int p = 10; + + 'OP_10 ' + // int q = 0; + + 'OP_0 ' + // require(p == 10); + + 'OP_OVER OP_10 OP_NUMEQUALVERIFY ' + // if (x == 1) { + + 'OP_3 OP_PICK OP_1 OP_NUMEQUAL OP_IF ' + // (p, q) = pair(x); + + 'OP_3 OP_PICK OP_DUP OP_2 OP_MUL OP_SWAP OP_1ADD OP_ROT OP_DROP OP_SWAP OP_ROT OP_DROP OP_SWAP ' + // } + + 'OP_ENDIF ' + // require(q == 0 || q == 2); + + 'OP_DUP OP_0 OP_NUMEQUAL OP_SWAP OP_2 OP_NUMEQUAL OP_BOOLOR ' + // Cleanup + + 'OP_NIP OP_NIP OP_NIP ' + // } + + 'OP_ELSE ' + // function whileLoop(int x) { + + 'OP_1 OP_NUMEQUALVERIFY ' + // int p = 10; + + 'OP_10 ' + // int q = 0; + + 'OP_0 ' + // require(p == 10); + + 'OP_OVER OP_10 OP_NUMEQUALVERIFY ' + // while (x == 1) { + + 'OP_BEGIN OP_2 OP_PICK OP_1 OP_NUMEQUAL OP_DUP OP_TOALTSTACK OP_IF ' + // (p, q) = pair(x); + + 'OP_2 OP_PICK OP_DUP OP_2 OP_MUL OP_SWAP OP_1ADD OP_ROT OP_DROP OP_SWAP OP_ROT OP_DROP OP_SWAP ' + // x = x + 1; + + 'OP_2 OP_PICK OP_1ADD OP_3 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK ' + // Loop condition + + 'OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL ' + // require(q == 0 || q == 2); + + 'OP_DUP OP_0 OP_NUMEQUAL OP_SWAP OP_2 OP_NUMEQUAL OP_BOOLOR ' + // Cleanup + + 'OP_NIP OP_NIP ' + // } + + 'OP_ENDIF', + fingerprint: '510ff239ab3ec7cc5d4301e1ef31a179c01bc4b6e73e800f03bd2674c9996ae3', + debug: { + bytecode: '76009c635a00785a9d5379519c6353797652957c8b7b757c7b757c6876009c7c529c9b77777767519d5a00785a9d655279519c766b6352797652957c8b7b757c7b757c52798b537a757c6b7c6c686c916676009c7c529c9b777768', + sourceMap: '11:4:19:5;;;;12:16:12:18;13::13:17;14::14;:21::23;:8::25:1;15:12:15:13:0;;:17::18;:12:::1;:20:17:9:0;16:26:16:27;;:21::28:1;;;;;:12::29;;;;;;15:20:17:9;18:16:18:17:0;:21::22;:16:::1;:26::27:0;:31::32;:26:::1;:8::34;11:29:19:5;;;:4;21::30::0;;22:16:22:18;23::23:17;24::24;:21::23;:8::25:1;25::28:9:0;:15:25:16;;:20::21;:15:::1;;;:23:28:9:0;26:26:26:27;;:21::28:1;;;;;:12::29;;;;;;27:16:27:17:0;;:::21:1;:12::22;;;;;;;25:23:28:9;;:8;;29:16:29:17:0;:21::22;:16:::1;:26::27:0;:31::32;:26:::1;:8::34;21:30:30:5;;10:0:31:1', + logs: [], + requires: [{ ip: 8, line: 14 }, { ip: 35, line: 18 }, { ip: 45, line: 24 }, { ip: 88, line: 29 }], + sourceTags: '35:37:sc;77:80:lc;88:89:sc', + functions: [ + { + name: 'pair', + inputs: [{ name: 'n', type: 'int' }], + bytecode: '7652957c8b', + sourceMap: '7:11:7:12;:15::16;:11:::1;:18::19:0;:::23:1', + logs: [], + requires: [], + }, + ], + inlineRanges: '16:20:pair;56:60:pair', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_reassignment_branches.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_reassignment_branches.ts new file mode 100644 index 000000000..406602dec --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_reassignment_branches.ts @@ -0,0 +1,74 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + // Tuple destructuring into existing variables inside branches. Scoped reassignment values are + // folded into the existing slots; a declaration value above a reassignment value is parked on + // the altstack while the fold runs (OP_TOALTSTACK ... OP_FROMALTSTACK in reassignmentFirst). + artifact: { + contractName: 'TupleReassignmentBranches', + constructorInputs: [], + abi: [ + { name: 'declarationFirst', inputs: [{ name: 'a', type: 'int' }] }, + { name: 'reassignmentFirst', inputs: [{ name: 'a', type: 'int' }] }, + ], + bytecode: + // OP_DEFINE branchPair (id 0) — called from both functions, too large to inline + '7653957857979378529693768b7c52957b94 OP_0 OP_DEFINE ' + // function declarationFirst + + 'OP_DUP OP_0 OP_NUMEQUAL OP_IF ' + // int total = 0 + + 'OP_0 ' + // if (a > 10) + + 'OP_2 OP_PICK OP_10 OP_GREATERTHAN OP_IF ' + // int d, total = branchPair(a) — call, then fold total's value (top) into its slot; + // d's value stays in place (declarations-first needs no parking) + + 'OP_2 OP_PICK OP_0 OP_INVOKE OP_ROT OP_DROP OP_SWAP ' + // require(d != 0) + + 'OP_DUP OP_0 OP_NUMNOTEQUAL OP_VERIFY ' + // scope cleanup (drop d) + + 'OP_DROP OP_ENDIF ' + // require(total >= 0) + cleanup + + 'OP_0 OP_GREATERTHANOREQUAL OP_NIP OP_NIP ' + // function reassignmentFirst + + 'OP_ELSE OP_1 OP_NUMEQUALVERIFY ' + // int total = 0 + + 'OP_0 ' + // if (a > 10) + + 'OP_OVER OP_10 OP_GREATERTHAN OP_IF ' + // (total, int extra) = branchPair(a + 1) — call, park extra's value on the altstack, + // fold total's value into its slot (OP_NIP), restore extra's value + + 'OP_OVER OP_1ADD OP_0 OP_INVOKE OP_TOALTSTACK OP_NIP OP_FROMALTSTACK ' + // require(extra != 0) + + 'OP_DUP OP_0 OP_NUMNOTEQUAL OP_VERIFY ' + // scope cleanup (drop extra) + + 'OP_DROP OP_ENDIF ' + // require(total >= 0) + cleanup + + 'OP_0 OP_GREATERTHANOREQUAL OP_NIP OP_ENDIF', + debug: { + bytecode: '127653957857979378529693768b7c52957b94008976009c630052795aa0635279008a7b757c76009e69756800a2777767519d00785aa063788b008a6b776c76009e69756800a27768', + sourceMap: '7::10:1;;::::1;14:4:21:5:0;;;;15:20:15:21;16:12:16:13;;:16::18;:12:::1;:20:19:9:0;17:38:17:39;;:27::40:1;;:12::41;;;18:20:18:21:0;:25::26;:20:::1;:12::28;16:20:19:9;;20:25:20:26:0;:8::28:1;14:37:21:5;;:4;25::32::0;;26:20:26:21;27:12:27:13;:16::18;:12:::1;:20:30:9:0;28:44:28:45;:::49:1;:33::50;;:12::51;;;29:20:29:25:0;:29::30;:20:::1;:12::32;27:20:30:9;;31:25:31:26:0;:8::28:1;25:38:32:5;12:0:33:1', + logs: [], + requires: [ + { ip: 23, line: 18 }, + { ip: 28, line: 20 }, + { ip: 48, line: 29 }, + { ip: 53, line: 31 }, + ], + sourceTags: '24:24:sc;28:29:sc;49:49:sc;53:53:sc', + functions: [ + { + id: 0, + name: 'branchPair', + inputs: [{ name: 'x', type: 'int' }], + bytecode: '7653957857979378529693768b7c52957b94', + sourceMap: '8:12:8:13;:16::17;:12:::1;:21::22:0;:25::26;:21:::1;:12::27;:31::32:0;:35::36;:31:::1;:12::37;9:11:9:12:0;:::16:1;:18::19:0;:22::23;:18:::1;:26::27:0;:18:::1', + logs: [], + requires: [], + }, + ], + }, + fingerprint: 'da4982948327a26f2c708b13af6977715747aa0b5c64bc00b04e3ddbc11ca67d', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_unpacking.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_unpacking.ts new file mode 100644 index 000000000..61e046c57 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_unpacking.ts @@ -0,0 +1,27 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [], + abi: [{ name: 'split', inputs: [] }], + bytecode: + // string s1 = "hello"; + '68656c6c6f ' + // string s2 = "there"; + + '7468657265 ' + // string hello, string there = (s1+s2).split(5); + + 'OP_CAT OP_5 OP_SPLIT ' + // require(hello == there); + + 'OP_EQUAL', + fingerprint: '0d6e8f27e53878ab15977e6a093b90a8fd56869d924697254575b181712ce989', + debug: { + bytecode: '0568656c6c6f0574686572657e557f87', + sourceMap: '3:20:3:27;4::4;5:38:5:43:1;:51::52:0;:37::53:1;6:8:6:32', + logs: [], + requires: [{ ip: 6, line: 6 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_unpacking_parameter.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_unpacking_parameter.ts new file mode 100644 index 000000000..c9879a7b3 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_unpacking_parameter.ts @@ -0,0 +1,26 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [], + abi: [{ name: 'split', inputs: [{ name: 'b', type: 'bytes32' }] }], + bytecode: + // Implicit parameter type enforcement + 'OP_SIZE 20 OP_EQUALVERIFY ' + // bytes16 x, bytes16 y = b.split(16); + + 'OP_16 OP_SPLIT ' + // require(x == y); + + 'OP_EQUAL', + fingerprint: 'a99422ca4eb162381e18295a9ac1ff2aca6f511a7e72d12342ef4f61904a3094', + debug: { + bytecode: '82012088607f87', + sourceMap: '2:19:2:28;;;3:40:3:42;:32::43:1;4:8:4:24', + logs: [], + requires: [{ ip: 6, line: 4 }], + sourceTags: '0:2:pv', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_unpacking_single_side_type.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_unpacking_single_side_type.ts new file mode 100644 index 000000000..df79ddecb --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_unpacking_single_side_type.ts @@ -0,0 +1,23 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [], + abi: [{ name: 'split', inputs: [{ name: 'b', type: 'bytes' }] }], + bytecode: + // bytes16 x, bytes y = b.split(16); + 'OP_16 OP_SPLIT ' + // require(x == y); + + 'OP_EQUAL', + fingerprint: '470f52cf7c1f819e3a5492c7964c2b737a9c3586aba1e257e7bb8b65d341fa48', + debug: { + bytecode: '607f87', + sourceMap: '3:37:3:39;:29::40:1;4:8:4:24', + logs: [], + requires: [{ ip: 3, line: 4 }], + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/type_enforcement.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/type_enforcement.ts new file mode 100644 index 000000000..7daa585ee --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/type_enforcement.ts @@ -0,0 +1,86 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'TypeEnforcement', + constructorInputs: [], + abi: [{ + name: 'spend', inputs: [ + { name: 'nonEnforcedInt', type: 'int' }, + { name: 'enforcedBool', type: 'bool' }, + { name: 'enforcedBytes', type: 'bytes4' }, + { name: 'nonEnforcedBytes', type: 'bytes' }, + ], + }], + bytecode: + // Implicit type enforcement for enforcedBool: enforcedBool = bool(enforcedBool) + 'OP_SWAP OP_0NOTEQUAL ' + // Implicit type enforcement for enforcedBytes: require(enforcedBytes.length == 4) + + 'OP_ROT OP_SIZE OP_4 OP_EQUALVERIFY ' + // if(enforcedBool == true) ) + + 'OP_OVER OP_1 OP_NUMEQUAL OP_IF ' + // require(nonEnforcedInt > 6) + + 'OP_2 OP_PICK OP_6 OP_GREATERTHAN OP_VERIFY ' + // Cleanup + + 'OP_ENDIF ' + // if(enforcedBool == false) { + + 'OP_SWAP OP_0 OP_NUMEQUAL OP_IF ' + // require(enforcedBytes == nonEnforcedBytes) + + 'OP_DUP OP_3 OP_PICK OP_EQUALVERIFY ' + // Cleanup + + 'OP_ENDIF OP_2DROP OP_DROP OP_1', + debug: { + bytecode: '7c927b82548878519c63527956a069687c009c6376537988686d7551', + sourceMap: '4:8:4:25;;5::5:28;;;;8:12:8:24;:28::32;:12:::1;:34:10:9:0;9:20:9:34;;:37::38;:20:::1;:12::40;8:34:10:9;12:12:12:24:0;:28::33;:12:::1;:35:14:9:0;13:20:13:33;:37::53;;:12::55:1;12:35:14:9;7:6:15:5;;', + sourceTags: '0:1:pv;2:5:pv', + logs: [], + requires: [ + { ip: 14, line: 9 }, + { ip: 23, line: 13 }, + ], + }, + fingerprint: 'afc9b61abaaef4b60d435309b83b2cbd5750e2c1755ca903cfd3f438f7dc6126', + }, + }, + { + compilerOptions: { + enforceFunctionParameterTypes: false, + }, + artifact: { + contractName: 'TypeEnforcement', + constructorInputs: [], + abi: [{ + name: 'spend', inputs: [ + { name: 'nonEnforcedInt', type: 'int' }, + { name: 'enforcedBool', type: 'bool' }, + { name: 'enforcedBytes', type: 'bytes4' }, + { name: 'nonEnforcedBytes', type: 'bytes' }, + ], + }], + bytecode: + // if(enforcedBool == true) + 'OP_OVER OP_1 OP_NUMEQUAL OP_IF ' + // require(nonEnforcedInt > 6) + + 'OP_DUP OP_6 OP_GREATERTHAN OP_VERIFY ' + // Cleanup + + 'OP_ENDIF ' + // if(enforcedBool == false) { + + 'OP_SWAP OP_0 OP_NUMEQUAL OP_IF ' + // require(enforcedBytes == nonEnforcedBytes) + + 'OP_OVER OP_3 OP_PICK OP_EQUALVERIFY ' + // Cleanup + + 'OP_ENDIF OP_2DROP OP_DROP OP_1', + debug: { + bytecode: '78519c637656a069687c009c6378537988686d7551', + sourceMap: '8:12:8:24;:28::32;:12:::1;:34:10:9:0;9:20:9:34;:37::38;:20:::1;:12::40;8:34:10:9;12:12:12:24:0;:28::33;:12:::1;:35:14:9:0;13:20:13:33;:37::53;;:12::55:1;12:35:14:9;7:6:15:5;;', + logs: [], + requires: [ + { ip: 7, line: 9 }, + { ip: 16, line: 13 }, + ], + }, + fingerprint: '606e540c38f161868964b683aeb0ddf93094dc36607397ef9b9f507f9028bc37', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/unsafe_bool_cast.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/unsafe_bool_cast.ts new file mode 100644 index 000000000..e4635dd67 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/unsafe_bool_cast.ts @@ -0,0 +1,14 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [{ name: 'x', type: 'int' }], + abi: [{ name: 'test', inputs: [] }], + bytecode: '', + fingerprint: '4ae81572f06e1b88fd5ced7a1a000945432e83e1551e6f721ee9c00b8cc33260', + debug: { bytecode: '', sourceMap: '', logs: [], requires: [{ ip: 1, line: 3 }] }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/unsafe_int_cast.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/unsafe_int_cast.ts new file mode 100644 index 000000000..8597c9635 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/unsafe_int_cast.ts @@ -0,0 +1,24 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Test', + constructorInputs: [{ name: 'x', type: 'bytes4' }], + abi: [{ name: 'test', inputs: [{ name: 'y', type: 'bytes4' }] }], + bytecode: + // Implicit parameter type enforcement + 'OP_SWAP OP_SIZE OP_4 OP_EQUALVERIFY ' + // require(unsafe_int(x) > unsafe_int(y)); + + 'OP_GREATERTHAN', + fingerprint: 'f46c0c6d3ac4de281708e6abe5b28f03eb7f3878ba6554c53a102e2bca1d4676', + debug: { + bytecode: '7c825488a0', + sourceMap: '2:18:2:26;;;;3:8:3:47:1', + logs: [], + requires: [{ ip: 6, line: 3 }], + sourceTags: '0:3:pv', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/unused_modifier.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/unused_modifier.ts new file mode 100644 index 000000000..01be0fd12 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/unused_modifier.ts @@ -0,0 +1,93 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'UnusedModifier', + constructorInputs: [{ name: 'salt', type: 'int' }], + abi: [ + { + name: 'spend', + inputs: [{ name: 'a', type: 'int' }, { name: 'b', type: 'int' }, { name: 'zeroPadding', type: 'bytes' }], + }, + ], + bytecode: + // contract UnusedModifier(int unused salt) { + 'OP_DROP ' + // function spend(int a, int b, bytes unused zeroPadding) { + + 'OP_ROT OP_DROP ' + // int unused scratch = a + b; + + 'OP_2DUP OP_ADD OP_DROP ' + // int constant unused magic = 42; + + '2a OP_DROP ' + // require(pad(a, 100) + b == 5); + + '64 OP_DROP OP_ADD OP_5 OP_NUMEQUAL', + fingerprint: '63c5ea2b3fc243e80628e119b8ffa9b873bf2a5aed664c8529b58d6f79116f7c', + debug: { + bytecode: '757b756e9375012a7501647593559c', + sourceMap: '5:24:5:39;6:33:6:57;;7:29:7:34;::::1;:8::35;8:36:8:38:0;:8::39:1;9:23:9:26:0;:16::27:1;:::31;:35::36:0;:8::38:1', + logs: [], + requires: [{ ip: 14, line: 9 }], + functions: [ + { + name: 'pad', + inputs: [{ name: 'value', type: 'int' }, { name: 'padding', type: 'int' }], + bytecode: '75', + sourceMap: '1:24:1:42', + logs: [], + requires: [], + }, + ], + inlineRanges: '10:10:pad', + }, + }, + }, + { + // The `unused` modifier — unused parameters keep their slot in constructorInputs / abi / frame + // inputs, but are dropped from the stack: constructor and contract function parameters in the + // contract prologue (rolled up first if buried), locals right after their initialiser, and + // global-function parameters in the function-body prologue. + compilerOptions: { disableInlining: true }, + artifact: { + contractName: 'UnusedModifier', + constructorInputs: [{ name: 'salt', type: 'int' }], + abi: [{ + name: 'spend', + inputs: [{ name: 'a', type: 'int' }, { name: 'b', type: 'int' }, { name: 'zeroPadding', type: 'bytes' }], + }], + bytecode: + // OP_DEFINE pad (id 0): drop unused param `padding`, leaving `value` as the return value + '75 OP_0 OP_DEFINE ' + // drop unused constructor param `salt` (top of stack) + + 'OP_DROP ' + // roll up and drop unused function param `zeroPadding` + + 'OP_ROT OP_DROP ' + // int unused scratch = a + b — initialiser is evaluated, then dropped + + 'OP_2DUP OP_ADD OP_DROP ' + // int constant unused magic = 42 — dropped as well + + '2a OP_DROP ' + // require(pad(a, 100) + b == 5) + + '64 OP_0 OP_INVOKE OP_ADD OP_5 OP_NUMEQUAL', + debug: { + bytecode: '01750089757b756e9375012a750164008a93559c', + logs: [], + requires: [ + { ip: 18, line: 9 }, + ], + sourceMap: '1::3:1;;::::1;5:24:5:39:0;6:33:6:57;;7:29:7:34;::::1;:8::35;8:36:8:38:0;:8::39:1;9:23:9:26:0;:16::27:1;;:::31;:35::36:0;:8::38:1', + functions: [ + { + id: 0, + name: 'pad', + inputs: [{ name: 'value', type: 'int' }, { name: 'padding', type: 'int' }], + bytecode: '75', + sourceMap: '1:24:1:42', + logs: [], + requires: [], + }, + ], + }, + fingerprint: '4fcac7e0c885a2d3d6a344866c39c4febdffcaf9bb658ac08a87ed7dea9808b6', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/while_loop.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/while_loop.ts new file mode 100644 index 000000000..79812f925 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/while_loop.ts @@ -0,0 +1,24 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'Loopy', + constructorInputs: [], + abi: [{ name: 'doLoop', inputs: [] }], + bytecode: 'OP_0 OP_BEGIN OP_DUP OP_TXINPUTCOUNT OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_DUP OP_1ADD OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_2 OP_GREATERTHAN', + debug: { + bytecode: '006576c39f766b63768b77686c916652a0', + sourceMap: '3:16:3:17;5:8:7:9;:15:5:16;:19::35;:15:::1;;;:37:7:9:0;6:16:6:17;:::21:1;:12::22;5:37:7:9;;:8;;10:20:10:21:0;:8::23:1', + logs: [ + { ip: 15, line: 9, data: [{ stackIndex: 0, type: 'int', ip: 15 }] }, + ], + requires: [ + { ip: 17, line: 10 }, + ], + sourceTags: '11:14:lc', + }, + fingerprint: '00fc9253e439b8a2f39ba60d1d173ff4d19f557802b40b750eb6df5f92b1001e', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/while_loop_basic.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/while_loop_basic.ts new file mode 100644 index 000000000..09635a1c1 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/while_loop_basic.ts @@ -0,0 +1,22 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'WhileLoopBasic', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [] }], + bytecode: 'OP_0 OP_BEGIN OP_DUP OP_3 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_DUP OP_1ADD OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_3 OP_NUMEQUAL', + debug: { + bytecode: '006576539f766b63768b77686c9166539c', + sourceMap: '3:16:3:17;5:8:7:9;:15:5:16;:19::20;:15:::1;;;:22:7:9:0;6:16:6:17;:::21:1;:12::22;5:22:7:9;;:8;;9:21:9:22:0;:8::24:1', + logs: [], + requires: [ + { ip: 17, line: 9 }, + ], + sourceTags: '11:14:lc', + }, + fingerprint: '5a456e72142ae6beb6f64a3af7edfe9e14295b17724c4dfec0d84940c545d457', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/while_loop_nested.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/while_loop_nested.ts new file mode 100644 index 000000000..b2d0036a1 --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/while_loop_nested.ts @@ -0,0 +1,46 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'WhileLoopNested', + constructorInputs: [], + abi: [{ name: 'spend', inputs: [] }], + bytecode: + // int i = 0; + 'OP_0 ' + // int total = 0; + + 'OP_0 ' + // while (i < 2) { + + 'OP_BEGIN OP_OVER OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF ' + // int j = 0; + + 'OP_0 ' + // while (j < 2) { + + 'OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF ' + // total = total + 1; + + 'OP_OVER OP_1ADD OP_ROT OP_DROP OP_SWAP ' + // j = j + 1; + + 'OP_DUP OP_1ADD OP_NIP ' + // Loop condition + + 'OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL ' + // i = i + 1; + + 'OP_2 OP_PICK OP_1ADD OP_3 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK ' + // Cleanup + + 'OP_DROP ' + // Loop condition + + 'OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL ' + // require(i == 2); + + 'OP_SWAP OP_2 OP_NUMEQUALVERIFY ' + // require(total == 4); + + 'OP_4 OP_NUMEQUAL', + fingerprint: '1305b87f8914755fb2e52934624cd80fc3bf61117d6957730845fcbc2002e8b4', + debug: { + bytecode: '00006578529f766b63006576529f766b63788b7b757c768b77686c916652798b537a757c6b7c6c75686c91667c529d549c', + sourceMap: '3:16:3:17;4:20:4:21;6:8:15:9;:15:6:16;:19::20;:15:::1;;;:22:15:9:0;7:20:7:21;9:12:12:13;:19:9:20;:23::24;:19:::1;;;:26:12:13:0;10:24:10:29;:::33:1;:16::34;;;11:20:11:21:0;:::25:1;:16::26;9:26:12:13;;:12;;14:16:14:17:0;;:::21:1;:12::22;;;;;;;6:22:15:9;;;:8;;17:16:17:17:0;:21::22;:8::24:1;18:25:18:26:0;:8::28:1', + logs: [], + requires: [{ ip: 46, line: 17 }, { ip: 49, line: 18 }], + sourceTags: '25:28:lc;39:39:sc;40:43:lc', + }, + }, + }, +]; diff --git a/packages/cashc/test/generation/generation.test.ts b/packages/cashc/test/generation/generation.test.ts index 4e4168817..2846c45ff 100644 --- a/packages/cashc/test/generation/generation.test.ts +++ b/packages/cashc/test/generation/generation.test.ts @@ -1,17 +1,46 @@ -/* generation.test.ts - * - * - This file is used to test the IR and target code generation - */ - +import fs from 'fs'; import { URL } from 'url'; -import { compileFile } from '../../src/index.js'; -import { fixtures } from './fixtures.js'; +import { CompilerOptions } from '@cashscript/utils'; +import { compileFile, DEFAULT_COMPILER_OPTIONS, InternalCompilerOptions } from '../../src/internal.js'; +import { version } from '../../src/index.js'; +import { loadFixtureModules } from './fixture-utils.js'; + +const fixtureModules = await loadFixtureModules(); describe('Code generation & target code optimisation', () => { - fixtures.forEach((fixture) => { - it(`should compile ${fixture.fn} to correct Script and artifact`, () => { - const artifact = compileFile(new URL(`../valid-contract-files/${fixture.fn}`, import.meta.url), fixture.compilerOptions); - expect(artifact).toEqual({ ...fixture.artifact, updatedAt: expect.any(String) }); + it('should have a fixture module for every valid contract file', () => { + const cashFiles = fs.readdirSync(new URL('../valid-contract-files', import.meta.url)) + .filter((fn) => fn.endsWith('.cash')) + .map((fn) => `valid-contract-files/${fn}`); + + const coveredCashFiles = fixtureModules + .map((fixtureModule) => fixtureModule.cashFile) + .filter((fn) => fn.startsWith('valid-contract-files/')); + + expect(coveredCashFiles.sort()).toEqual(cashFiles.sort()); + }); + + fixtureModules.forEach(({ cashFile, fixtures }) => { + fixtures.forEach((fixture) => { + const variant = fixture.compilerOptions ? ` (${JSON.stringify(fixture.compilerOptions)})` : ''; + it(`should compile ${cashFile} to correct Script and artifact${variant}`, () => { + const sourceFile = new URL(`../${cashFile}`, import.meta.url); + const artifact = compileFile(sourceFile, fixture.compilerOptions); + + expect(artifact).toEqual({ + ...fixture.artifact, + source: fs.readFileSync(sourceFile, { encoding: 'utf-8' }), + compiler: { name: 'cashc', version, options: artifactCompilerOptions(fixture.compilerOptions) }, + updatedAt: expect.any(String), + }); + }); }); }); }); + +// The artifact records the merged public compiler options (internal-only options are stripped) +function artifactCompilerOptions(compilerOptions: InternalCompilerOptions = {}): CompilerOptions { + const mergedOptions: InternalCompilerOptions = { ...DEFAULT_COMPILER_OPTIONS, ...compilerOptions }; + delete mergedOptions.disableInlining; + return mergedOptions; +} diff --git a/packages/cashc/test/global-definitions.test.ts b/packages/cashc/test/global-definitions.test.ts new file mode 100644 index 000000000..8560e86cb --- /dev/null +++ b/packages/cashc/test/global-definitions.test.ts @@ -0,0 +1,558 @@ +/* global-definitions.test.ts + * + * - This file tests the compilation behaviour of user-defined global functions and constants: + * dead-code elimination of unreachable definitions, the byte-size-driven decisions to inline + * definitions at their call sites or to share them as OP_DEFINE / OP_INVOKE definitions, the + * lowering of constants to zero-argument functions, and stable VM function-ID assignment. + * - Compile errors are tested with the fixture files in ./compiler, and the exact compiled output + * is locked in by the fixture modules in generation/fixtures. + */ + +import { compileString } from '../src/internal.js'; + +const countOp = (bytecode: string, opcode: string): number => [...bytecode.matchAll(new RegExp(opcode, 'g'))].length; + +const longHex = (byte: string): string => `0x${byte.repeat(32)}`; + +describe('Dead-code elimination', () => { + it('does not define a global function that is never invoked', () => { + const code = ` + function used(int a) returns (int) { return a + 1; } + function notUsed(int a) returns (int) { return a * 2; } + + contract Test() { + function spend(int x) { + require(used(x) == 6); + } + }`; + + const artifact = compileString(code, { disableInlining: true }); + expect(countOp(artifact.bytecode, 'OP_DEFINE')).toEqual(1); + expect(artifact.bytecode).toContain('OP_INVOKE'); + }); + + it('eliminates functions that are only reachable through other dead functions', () => { + const code = ` + function used(int a) returns (int) { return a + 1; } + function deadCaller(int a) returns (int) { return deadLeaf(a); } + function deadLeaf(int a) returns (int) { return a * 2; } + + contract Test() { + function spend(int x) { + require(used(x) == 6); + } + }`; + + // Only `used` is reachable; both `deadCaller` and the function it calls (`deadLeaf`) are dropped. + const artifact = compileString(code, { disableInlining: true }); + expect(countOp(artifact.bytecode, 'OP_DEFINE')).toEqual(1); + }); + + it('keeps a function that is only reachable transitively', () => { + const code = ` + function outer(int a) returns (int) { return inner(a) + 1; } + function inner(int a) returns (int) { return a * 2; } + + contract Test() { + function spend(int x) { + require(outer(x) == 7); + } + }`; + + // `outer` is called directly and `inner` only through `outer` — both must be defined. + const artifact = compileString(code, { disableInlining: true }); + expect(countOp(artifact.bytecode, 'OP_DEFINE')).toEqual(2); + }); + + it('keeps a recursive function without looping forever', () => { + const code = ` + function f(int n) returns (int) { return f(n); } + + contract Test() { + function spend(int x) { + require(f(x) == 0); + } + }`; + + const artifact = compileString(code, { disableInlining: true }); + expect(countOp(artifact.bytecode, 'OP_DEFINE')).toEqual(1); + }); + + it('keeps mutually recursive functions that are reachable', () => { + const code = ` + function a(int n) returns (int) { return b(n); } + function b(int n) returns (int) { return a(n); } + + contract Test() { + function spend(int x) { + require(a(x) == 0); + } + }`; + + const artifact = compileString(code, { disableInlining: true }); + expect(countOp(artifact.bytecode, 'OP_DEFINE')).toEqual(2); + }); + + it('eliminates a mutually recursive cycle that is never reached', () => { + const code = ` + function used(int n) returns (int) { return n + 1; } + function deadA(int n) returns (int) { return deadB(n); } + function deadB(int n) returns (int) { return deadA(n); } + + contract Test() { + function spend(int x) { + require(used(x) == 1); + } + }`; + + // deadA <-> deadB form a cycle but neither is reachable, so both are dropped. + const artifact = compileString(code, { disableInlining: true }); + expect(countOp(artifact.bytecode, 'OP_DEFINE')).toEqual(1); + }); + + it('eliminates an unused imported function', () => { + // math.cash exports both `addOne` and `double`; only `double` is used here, so `addOne` is dropped. + const code = 'import "./math.cash";\ncontract Test() { function spend(int x) { require(double(x) == 8); } }'; + const mathSource = ` + function addOne(int a) returns (int) { return a + 1; } + function double(int a) returns (int) { return a * 2; } + `; + + const artifact = compileString(code, { files: { './math.cash': mathSource }, disableInlining: true }); + expect(countOp(artifact.bytecode, 'OP_DEFINE')).toEqual(1); + }); + + it('does not define an unused constant', () => { + const contract = ` + contract Unused() { + function spend() { + require(true); + } + }`; + + const withConstant = compileString(`bytes32 constant UNUSED = ${longHex('11')};\n${contract}`); + expect(withConstant.bytecode).toEqual(compileString(contract).bytecode); + }); + + it('does not define a constant referenced only by console.log', () => { + const contract = (message: string): string => ` + contract ConsoleOnly() { + function spend() { + console.log(${message}); + require(true); + } + }`; + + const withConstant = compileString(`string constant MESSAGE = "debug" + " only";\n${contract('MESSAGE')}`); + expect(withConstant.bytecode).toEqual(compileString(contract('"debug only"')).bytecode); + }); +}); + +describe('Inlining and shared definitions', () => { + it('inlines a single-use function', () => { + const code = ` + function triple(int x) returns (int) { return x * 3; } + contract C() { function spend(int n) { require(triple(n) == 12); } }`; + + const { bytecode } = compileString(code); + expect(bytecode).not.toContain('OP_DEFINE'); + expect(bytecode).not.toContain('OP_INVOKE'); + }); + + it('inlines a small multi-use function when that is no larger', () => { + const code = ` + function identity(int x) returns (int) { return x; } + contract C() { function spend(int n) { require(identity(n) + identity(n) == 12); } }`; + + expect(compileString(code).bytecode).not.toContain('OP_DEFINE'); + }); + + it('shares a large multi-use function with OP_DEFINE and OP_INVOKE', () => { + const code = ` + function big(int x) returns (int) { return (x * 7 + 3) * (x + 11) - 5; } + contract C() { function spend(int n) { require(big(n) + big(n + 1) > 0); } }`; + + const { bytecode } = compileString(code); + expect(bytecode).toContain('OP_DEFINE'); + expect(bytecode).toContain('OP_INVOKE'); + }); + + it('ignores call sites inside eliminated functions when deciding to inline', () => { + const code = ` + function big(int x) returns (int) { return (x * 7 + 3) * (x + 11) - 5; } + function notUsed(int x) returns (int) { return big(x) + big(x + 1) + big(x + 2); } + contract C() { function spend(int n) { require(big(n) > 0); } }`; + + // big is multi-use on paper, but all extra call sites live in the eliminated function + // notUsed — only the single reachable call counts, so big is inlined + const { bytecode } = compileString(code); + expect(bytecode).not.toContain('OP_DEFINE'); + expect(bytecode).not.toContain('OP_INVOKE'); + }); + + it('inlines nested single-use functions in callee-first order', () => { + const code = ` + function outer(int x) returns (int) { return inner(x) + 1; } + function inner(int x) returns (int) { return x * 2; } + contract C() { function spend(int n) { require(outer(n) == 7); } }`; + + const { bytecode } = compileString(code); + expect(bytecode).not.toContain('OP_DEFINE'); + expect(bytecode).not.toContain('OP_INVOKE'); + }); + + it('keeps every member of a mutually recursive component defined', () => { + const code = ` + function even(int n) returns (bool) { return odd(n); } + function odd(int n) returns (bool) { return even(n); } + contract C() { function spend(int n) { require(even(n)); } }`; + + const { bytecode } = compileString(code); + expect(countOp(bytecode, 'OP_DEFINE')).toBe(2); + expect(bytecode).toContain('OP_INVOKE'); + }); + + it('inlines a large constant when it is used once', () => { + const contract = (value: string): string => ` + contract OneUse(bytes32 candidate) { + function spend() { + require(candidate == ${value}); + } + }`; + + const artifact = compileString(`bytes32 constant HASH = ${longHex('22')};\n${contract('HASH')}`); + expect(artifact.bytecode).toEqual(compileString(contract(longHex('22'))).bytecode); + expect(artifact.bytecode).not.toContain('OP_DEFINE'); + }); + + it('lists inlined functions as id-less frames after the defined ones', () => { + const code = ` + function wrapper(int x) returns (int) { return shared(x) + shared(x + 1); } + function shared(int x) returns (int) { return (x * 7 + 3) * (x + 11) - 5; } + contract C() { + function spend(int n) { require(wrapper(n) > 0); } + }`; + + // wrapper is single-use and inlined: it keeps a debug frame documenting its compiled body, + // but no id or define site + const artifact = compileString(code); + expect(artifact.debug?.functions?.map(({ name, id }) => ({ name, id }))).toEqual([ + { name: 'shared', id: 0 }, + { name: 'wrapper', id: undefined }, + ]); + expect(countOp(artifact.bytecode, 'OP_0 OP_INVOKE')).toBe(2); + }); + + it('attributes merged debug info to the call site and keeps own lines on the frame', () => { + const code = ` + function checkSmall(int x) { + require(x < 100, "too big"); + } + contract C() { + function spend(int n) { + checkSmall(n); + require(n > 0); + } + }`; + + // Merged entries uniformly take the call-site line (like the source map); the function's + // own line is retained on its frame for future source attribution + const artifact = compileString(code); + expect(artifact.bytecode).not.toContain('OP_DEFINE'); + expect(artifact.debug?.requires).toContainEqual(expect.objectContaining({ + line: 7, + message: 'too big', + })); + expect(artifact.debug?.functions?.[0].requires).toContainEqual(expect.objectContaining({ + line: 3, + message: 'too big', + })); + }); + + it('attributes debug info of an inlined imported function to the call site', () => { + const importedSource = 'function assertPositive(int value) {\n require(value > 0, "must be positive");\n}'; + const source = ` + import "./helpers.cash"; + contract C() { + function spend(int n) { + assertPositive(n); + require(n < 100); + } + }`; + + // Lines from another file cannot appear in the contract's source map, so the merged require + // is attributed to the call-site line; the function's frame retains its source provenance + const artifact = compileString(source, { files: { './helpers.cash': importedSource } }); + expect(artifact.bytecode).not.toContain('OP_DEFINE'); + expect(artifact.debug?.requires).toContainEqual(expect.objectContaining({ + line: 5, + message: 'must be positive', + })); + expect(artifact.debug?.functions).toContainEqual(expect.objectContaining({ + name: 'assertPositive', + sourceFile: 'helpers.cash', + source: importedSource, + requires: [expect.objectContaining({ line: 2, message: 'must be positive' })], + })); + expect(artifact.debug?.functions?.[0].id).toBeUndefined(); + expect(artifact.debug?.inlineRanges).toMatch(/^\d+:\d+:assertPositive$/); + }); + + it('attributes debug info spliced into a defined body to the call site within that body', () => { + const importedSource = 'function assertSmall(int value) {\n require(value < 1000, "value too large");\n}'; + const source = ` + import "./helpers.cash"; + contract C() { + function spend(int n) { require(big(n) + big(n + 1) > 0); } + } + function big(int x) returns (int) { + assertSmall(x); + require(x > 0, "must be positive"); + return (x * 7 + 3) * (x + 11) - 5; + }`; + + // assertSmall is inlined into the shared function big, so its require joins big's + // frame-local requires, attributed to the call-site line inside big + const artifact = compileString(source, { files: { './helpers.cash': importedSource } }); + expect(artifact.debug?.functions?.map(({ name, id }) => ({ name, id }))).toEqual([ + { name: 'big', id: 0 }, + { name: 'assertSmall', id: undefined }, + ]); + expect(artifact.debug?.functions?.[0].requires).toContainEqual(expect.objectContaining({ + line: 7, + message: 'value too large', + })); + expect(artifact.debug?.functions?.[0].inlineRanges).toMatch(/^\d+:\d+:assertSmall$/); + }); + + it('can disable inlining for callers that need the shared-definition form', () => { + const code = ` + function triple(int x) returns (int) { return x * 3; } + contract C() { function spend(int n) { require(triple(n) == 12); } }`; + + const { bytecode } = compileString(code, { disableInlining: true }); + expect(bytecode).toContain('OP_DEFINE'); + expect(bytecode).toContain('OP_INVOKE'); + }); +}); + +describe('Global constants', () => { + it('compiles a constant identically to an equivalent zero-argument function', () => { + const contract = (reference: string): string => ` + contract Repeated(bytes32 first, bytes32 second) { + function spend() { + require(first == ${reference} && second == ${reference}); + } + }`; + + const constant = compileString(`bytes32 constant HASH = ${longHex('33')};\n${contract('HASH')}`); + const fn = compileString(`function HASH() returns (bytes32) { return ${longHex('33')}; }\n${contract('HASH()')}`); + + expect(countOp(constant.bytecode, 'OP_DEFINE')).toBe(1); + expect(countOp(constant.bytecode, 'OP_INVOKE')).toBe(2); + expect(constant.bytecode).toEqual(fn.bytecode); + }); + + it('retains debug source provenance for imported constants', () => { + const importedSource = `bytes32 constant IMPORTED_HASH = ${longHex('44')};`; + const source = ` + import "./constants.cash"; + contract Imported(bytes32 first, bytes32 second) { + function spend() { + require(first == IMPORTED_HASH && second == IMPORTED_HASH); + } + }`; + + const artifact = compileString(source, { files: { './constants.cash': importedSource } }); + expect(countOp(artifact.bytecode, 'OP_DEFINE')).toBe(1); + expect(artifact.debug?.functions?.[0]).toMatchObject({ + name: 'IMPORTED_HASH', + kind: 'constant', + source: importedSource, + sourceFile: 'constants.cash', + }); + }); + + it('folds constant definitions to literals at compile time', () => { + const contract = ` + contract Computed(int number, int derived, string text, bytes4 data) { + function spend() { + require(number == NUMBER); + require(derived == DERIVED); + require(text == TEXT); + require(data == DATA); + } + }`; + + const computed = compileString(` + int constant NUMBER = (10 + 10) * 5 - 30 / 4 % 5; + int constant DERIVED = -NUMBER * 2 + 4; + string constant TEXT = "debug" + " " + "only"; + bytes4 constant DATA = 0x0102 + 0x0304; + ${contract}`); + + const literal = compileString(` + int constant NUMBER = 98; + int constant DERIVED = -192; + string constant TEXT = "debug only"; + bytes4 constant DATA = 0x01020304; + ${contract}`); + + expect(computed.bytecode).toEqual(literal.bytecode); + }); + + it('folds references to imported constants', () => { + const importedSource = 'int constant BASE = 20 + 1;'; + const source = ` + import "./constants.cash"; + int constant DERIVED = BASE * 2; + contract Imported(int number) { + function spend() { + require(number == DERIVED); + } + }`; + + const contract = ` + contract Imported(int number) { + function spend() { + require(number == 42); + } + }`; + + const artifact = compileString(source, { files: { './constants.cash': importedSource } }); + expect(artifact.bytecode).toEqual(compileString(contract).bytecode); + }); +}); + +describe('Stable function ID assignment', () => { + it('reordering function declarations does not change the bytecode', () => { + const ordered = ` + function a(int n) returns (int) { return n + 1; } + function b(int n) returns (int) { return n * 2; } + + contract Test() { + function spend(int x) { + require(b(x) + a(x) == 10); + } + }`; + + const reordered = ` + function b(int n) returns (int) { return n * 2; } + function a(int n) returns (int) { return n + 1; } + + contract Test() { + function spend(int x) { + require(b(x) + a(x) == 10); + } + }`; + + // functionIds follow call order (b, then a) rather than declaration order, so swapping the two + // declarations produces byte-identical output. + expect(compileString(reordered, { disableInlining: true }).bytecode) + .toEqual(compileString(ordered, { disableInlining: true }).bytecode); + }); + + it('renaming a function does not change the bytecode', () => { + const original = ` + function apple(int n) returns (int) { return n + 1; } + function mango(int n) returns (int) { return n * 2; } + + contract Test() { + function spend(int x) { + require(mango(x) + apple(x) == 10); + } + }`; + + const renamed = ` + function zebra(int n) returns (int) { return n + 1; } + function mango(int n) returns (int) { return n * 2; } + + contract Test() { + function spend(int x) { + require(mango(x) + zebra(x) == 10); + } + }`; + + expect(compileString(renamed, { disableInlining: true }).bytecode) + .toEqual(compileString(original, { disableInlining: true }).bytecode); + }); + + it('assigns IDs by first use across functions and constants', () => { + const source = (first: string, second: string, fn: string, order: number[]): string => { + const definitions = [ + `bytes32 constant ${first} = ${longHex('aa')};`, + `function ${fn}() returns (bytes32) { return ${longHex('cc')}; }`, + `bytes32 constant ${second} = ${longHex('bb')};`, + ]; + + return ` + ${order.map((index) => definitions[index]).join('\n')} + contract Stable(bytes32 a, bytes32 b, bytes32 c) { + function spend() { + require(a == ${first} && b == ${fn}() && c == ${second}); + } + }`; + }; + + const original = compileString(source('FIRST', 'SECOND', 'value', [0, 1, 2]), { disableInlining: true }); + const renamedAndReordered = compileString(source('ALPHA', 'OMEGA', 'renamed', [2, 0, 1]), { disableInlining: true }); + + expect(renamedAndReordered.bytecode).toEqual(original.bytecode); + expect(original.debug?.functions?.map(({ name, id }) => ({ name, id }))).toEqual([ + { name: 'FIRST', id: 0 }, + { name: 'value', id: 1 }, + { name: 'SECOND', id: 2 }, + ]); + }); + + it('keeps bytecode identical under renaming and reordering while inlining is active', () => { + const source = (first: string, second: string, fn: string, order: number[]): string => { + const definitions = [ + `bytes32 constant ${first} = ${longHex('aa')};`, + `function ${fn}() returns (bytes32) { return ${longHex('cc')}; }`, + `bytes32 constant ${second} = ${longHex('bb')};`, + ]; + + return ` + ${order.map((index) => definitions[index]).join('\n')} + contract Stable(bytes32 a, bytes32 b, bytes32 c, bytes32 d, bytes32 e, bytes32 f) { + function spend() { + require(a == ${first} && b == ${first}); + require(c == ${fn}() && d == ${fn}()); + require(e == ${second} && f == ${second}); + } + }`; + }; + + const original = compileString(source('FIRST', 'SECOND', 'value', [0, 1, 2])); + const renamedAndReordered = compileString(source('ALPHA', 'OMEGA', 'renamed', [2, 0, 1])); + + expect(renamedAndReordered.bytecode).toEqual(original.bytecode); + expect(original.debug?.functions?.map(({ name, id }) => ({ name, id }))).toEqual([ + { name: 'FIRST', id: 0 }, + { name: 'value', id: 1 }, + { name: 'SECOND', id: 2 }, + ]); + }); + + it('assigns dense IDs when an inlined definition sits between shared ones', () => { + const source = ` + bytes32 constant FIRST = ${longHex('aa')}; + function identity(bytes32 value) returns (bytes32) { return value; } + bytes32 constant SECOND = ${longHex('bb')}; + contract Contiguous(bytes32 a, bytes32 b, bytes32 c, bytes32 d, bytes32 e) { + function spend() { + require(a == FIRST && b == FIRST); + require(identity(c) == c); + require(d == SECOND && e == SECOND); + } + }`; + + const artifact = compileString(source); + expect(artifact.debug?.functions?.map(({ name, id }) => ({ name, id }))).toEqual([ + { name: 'FIRST', id: 0 }, + { name: 'SECOND', id: 1 }, + { name: 'identity', id: undefined }, + ]); + }); +}); diff --git a/packages/cashc/test/import-fixtures/bad_pragma_helper.cash b/packages/cashc/test/import-fixtures/bad_pragma_helper.cash new file mode 100644 index 000000000..45fd237a4 --- /dev/null +++ b/packages/cashc/test/import-fixtures/bad_pragma_helper.cash @@ -0,0 +1,5 @@ +pragma cashscript >=999.0.0; + +function bump(int a) returns (int) { + return a + 1; +} diff --git a/packages/cashc/test/import-fixtures/bad_pragma_main.cash b/packages/cashc/test/import-fixtures/bad_pragma_main.cash new file mode 100644 index 000000000..61c705d88 --- /dev/null +++ b/packages/cashc/test/import-fixtures/bad_pragma_main.cash @@ -0,0 +1,7 @@ +import "./bad_pragma_helper.cash"; + +contract BadPragma() { + function spend(int x) { + require(bump(x) == 5); + } +} diff --git a/packages/cashc/test/import-fixtures/complex/lib/a.cash b/packages/cashc/test/import-fixtures/complex/lib/a.cash new file mode 100644 index 000000000..d7e12e52f --- /dev/null +++ b/packages/cashc/test/import-fixtures/complex/lib/a.cash @@ -0,0 +1,6 @@ +import "./util/leaf.cash"; +import "../../shared.cash"; + +function alpha(int n) returns (int) { + return leaf(n) + shared(n); +} diff --git a/packages/cashc/test/import-fixtures/complex/lib/b.cash b/packages/cashc/test/import-fixtures/complex/lib/b.cash new file mode 100644 index 000000000..073fd06bf --- /dev/null +++ b/packages/cashc/test/import-fixtures/complex/lib/b.cash @@ -0,0 +1,5 @@ +import "./util/leaf.cash"; + +function beta(int n) returns (int) { + return leaf(n) * 2; +} diff --git a/packages/cashc/test/import-fixtures/complex/lib/util/leaf.cash b/packages/cashc/test/import-fixtures/complex/lib/util/leaf.cash new file mode 100644 index 000000000..b940a4bce --- /dev/null +++ b/packages/cashc/test/import-fixtures/complex/lib/util/leaf.cash @@ -0,0 +1,3 @@ +function leaf(int n) returns (int) { + return n + 1; +} diff --git a/packages/cashc/test/import-fixtures/complex/main.cash b/packages/cashc/test/import-fixtures/complex/main.cash new file mode 100644 index 000000000..4ae3d51d1 --- /dev/null +++ b/packages/cashc/test/import-fixtures/complex/main.cash @@ -0,0 +1,9 @@ +import "./lib/a.cash"; +import "./lib/b.cash"; +import "../shared.cash"; + +contract Complex() { + function spend(int x) { + require(alpha(x) + beta(x) + shared(x) == 42); + } +} diff --git a/packages/cashc/test/import-fixtures/cycle_a.cash b/packages/cashc/test/import-fixtures/cycle_a.cash new file mode 100644 index 000000000..5197f26ec --- /dev/null +++ b/packages/cashc/test/import-fixtures/cycle_a.cash @@ -0,0 +1,5 @@ +import "./cycle_b.cash"; + +function a(int n) returns (int) { + return n + 1; +} diff --git a/packages/cashc/test/import-fixtures/cycle_b.cash b/packages/cashc/test/import-fixtures/cycle_b.cash new file mode 100644 index 000000000..da059e51b --- /dev/null +++ b/packages/cashc/test/import-fixtures/cycle_b.cash @@ -0,0 +1,5 @@ +import "./cycle_a.cash"; + +function b(int n) returns (int) { + return n * 2; +} diff --git a/packages/cashc/test/import-fixtures/cycle_main.cash b/packages/cashc/test/import-fixtures/cycle_main.cash new file mode 100644 index 000000000..6f54878b7 --- /dev/null +++ b/packages/cashc/test/import-fixtures/cycle_main.cash @@ -0,0 +1,7 @@ +import "./cycle_a.cash"; + +contract Cycle() { + function spend(int x) { + require(a(x) + b(x) == 7); + } +} diff --git a/packages/cashc/test/import-fixtures/diamond.cash b/packages/cashc/test/import-fixtures/diamond.cash new file mode 100644 index 000000000..e3629cb4d --- /dev/null +++ b/packages/cashc/test/import-fixtures/diamond.cash @@ -0,0 +1,8 @@ +import "./mid1.cash"; +import "./mid2.cash"; + +contract Diamond() { + function spend(int x) { + require(m1(x) + m2(x) == 18); + } +} diff --git a/packages/cashc/test/import-fixtures/duplicate_import_helper.cash b/packages/cashc/test/import-fixtures/duplicate_import_helper.cash new file mode 100644 index 000000000..03a29aa07 --- /dev/null +++ b/packages/cashc/test/import-fixtures/duplicate_import_helper.cash @@ -0,0 +1,3 @@ +function shared(int a) returns (int) { + return a + 99; +} diff --git a/packages/cashc/test/import-fixtures/duplicate_import_main.cash b/packages/cashc/test/import-fixtures/duplicate_import_main.cash new file mode 100644 index 000000000..2ec046823 --- /dev/null +++ b/packages/cashc/test/import-fixtures/duplicate_import_main.cash @@ -0,0 +1,11 @@ +import "./duplicate_import_helper.cash"; + +function shared(int a) returns (int) { + return a + 1; +} + +contract DuplicateImport() { + function spend() { + require(shared(1) == 2); + } +} diff --git a/packages/cashc/test/import-fixtures/leaf.cash b/packages/cashc/test/import-fixtures/leaf.cash new file mode 100644 index 000000000..c3d21091a --- /dev/null +++ b/packages/cashc/test/import-fixtures/leaf.cash @@ -0,0 +1,3 @@ +function leaf(int a) returns (int) { + return a + 1; +} diff --git a/packages/cashc/test/import-fixtures/main.cash b/packages/cashc/test/import-fixtures/main.cash new file mode 100644 index 000000000..f9c1a552b --- /dev/null +++ b/packages/cashc/test/import-fixtures/main.cash @@ -0,0 +1,7 @@ +import "./math.cash"; + +contract Main() { + function spend(int x) { + require(double(addOne(x)) == 8); + } +} diff --git a/packages/cashc/test/import-fixtures/math.cash b/packages/cashc/test/import-fixtures/math.cash new file mode 100644 index 000000000..9d74e6923 --- /dev/null +++ b/packages/cashc/test/import-fixtures/math.cash @@ -0,0 +1,7 @@ +function addOne(int a) returns (int) { + return a + 1; +} + +function double(int a) returns (int) { + return a * 2; +} diff --git a/packages/cashc/test/import-fixtures/mid1.cash b/packages/cashc/test/import-fixtures/mid1.cash new file mode 100644 index 000000000..096decc5a --- /dev/null +++ b/packages/cashc/test/import-fixtures/mid1.cash @@ -0,0 +1,4 @@ +import "./leaf.cash"; +function m1(int a) returns (int) { + return leaf(a) * 2; +} diff --git a/packages/cashc/test/import-fixtures/mid2.cash b/packages/cashc/test/import-fixtures/mid2.cash new file mode 100644 index 000000000..cdc964c0c --- /dev/null +++ b/packages/cashc/test/import-fixtures/mid2.cash @@ -0,0 +1,4 @@ +import "./leaf.cash"; +function m2(int a) returns (int) { + return leaf(a) + 3; +} diff --git a/packages/cashc/test/import-fixtures/missing_import_main.cash b/packages/cashc/test/import-fixtures/missing_import_main.cash new file mode 100644 index 000000000..4f6871147 --- /dev/null +++ b/packages/cashc/test/import-fixtures/missing_import_main.cash @@ -0,0 +1,7 @@ +import "./does-not-exist.cash"; + +contract Missing() { + function spend() { + require(true); + } +} diff --git a/packages/cashc/test/import-fixtures/multi_return_lib.cash b/packages/cashc/test/import-fixtures/multi_return_lib.cash new file mode 100644 index 000000000..f243ae4b4 --- /dev/null +++ b/packages/cashc/test/import-fixtures/multi_return_lib.cash @@ -0,0 +1,3 @@ +function divmod(int a, int b) returns (int, int) { + return a / b, a % b; +} diff --git a/packages/cashc/test/import-fixtures/multi_return_main.cash b/packages/cashc/test/import-fixtures/multi_return_main.cash new file mode 100644 index 000000000..2298ab641 --- /dev/null +++ b/packages/cashc/test/import-fixtures/multi_return_main.cash @@ -0,0 +1,9 @@ +import "./multi_return_lib.cash"; + +contract MultiReturnMain() { + function spend(int x) { + int q, int r = divmod(x, 3); + require(q == 4); + require(r == 1); + } +} diff --git a/packages/cashc/test/import-fixtures/nested/helper.cash b/packages/cashc/test/import-fixtures/nested/helper.cash new file mode 100644 index 000000000..29096badf --- /dev/null +++ b/packages/cashc/test/import-fixtures/nested/helper.cash @@ -0,0 +1,3 @@ +function nestedHelper(int a) returns (int) { + return a + 5; +} diff --git a/packages/cashc/test/import-fixtures/nested/nm_nested_main.cash b/packages/cashc/test/import-fixtures/nested/nm_nested_main.cash new file mode 100644 index 000000000..470bd6f45 --- /dev/null +++ b/packages/cashc/test/import-fixtures/nested/nm_nested_main.cash @@ -0,0 +1,7 @@ +import "mathlib/math.cash"; + +contract NodeModulesNested() { + function spend(int x) { + require(nmAdd(x, 3) == 8); + } +} diff --git a/packages/cashc/test/import-fixtures/nested_main.cash b/packages/cashc/test/import-fixtures/nested_main.cash new file mode 100644 index 000000000..54ed6ab94 --- /dev/null +++ b/packages/cashc/test/import-fixtures/nested_main.cash @@ -0,0 +1,7 @@ +import "./nested/helper.cash"; + +contract Nested() { + function spend(int x) { + require(nestedHelper(x) == 8); + } +} diff --git a/packages/cashc/test/import-fixtures/nm_main.cash b/packages/cashc/test/import-fixtures/nm_main.cash new file mode 100644 index 000000000..4fb87641e --- /dev/null +++ b/packages/cashc/test/import-fixtures/nm_main.cash @@ -0,0 +1,7 @@ +import "mathlib/math.cash"; + +contract NodeModulesMain() { + function spend(int x) { + require(nmAdd(x, 3) == 8); + } +} diff --git a/packages/cashc/test/import-fixtures/nm_missing_main.cash b/packages/cashc/test/import-fixtures/nm_missing_main.cash new file mode 100644 index 000000000..896612fb1 --- /dev/null +++ b/packages/cashc/test/import-fixtures/nm_missing_main.cash @@ -0,0 +1,7 @@ +import "nonexistent-cashscript-pkg/foo.cash"; + +contract NodeModulesMissing() { + function spend(int x) { + require(x == 1); + } +} diff --git a/packages/cashc/test/import-fixtures/nm_package_deps_main.cash b/packages/cashc/test/import-fixtures/nm_package_deps_main.cash new file mode 100644 index 000000000..b6883a2cd --- /dev/null +++ b/packages/cashc/test/import-fixtures/nm_package_deps_main.cash @@ -0,0 +1,7 @@ +import "mathlib/combined.cash"; + +contract NodeModulesPackageDeps() { + function spend(int x) { + require(nmCombined(x) == 10); + } +} diff --git a/packages/cashc/test/import-fixtures/nm_scoped_main.cash b/packages/cashc/test/import-fixtures/nm_scoped_main.cash new file mode 100644 index 000000000..6ca733609 --- /dev/null +++ b/packages/cashc/test/import-fixtures/nm_scoped_main.cash @@ -0,0 +1,7 @@ +import "@cashlibs/utils/util.cash"; + +contract NodeModulesScoped() { + function spend(int x) { + require(scopedUtil(x) == 9); + } +} diff --git a/packages/cashc/test/import-fixtures/nm_transitive_main.cash b/packages/cashc/test/import-fixtures/nm_transitive_main.cash new file mode 100644 index 000000000..e0d0cf48d --- /dev/null +++ b/packages/cashc/test/import-fixtures/nm_transitive_main.cash @@ -0,0 +1,7 @@ +import "mathlib/main.cash"; + +contract NodeModulesTransitive() { + function spend(int x) { + require(nmMain(x) == 16); + } +} diff --git a/packages/cashc/test/import-fixtures/node_modules/@cashlibs/utils/util.cash b/packages/cashc/test/import-fixtures/node_modules/@cashlibs/utils/util.cash new file mode 100644 index 000000000..d911d7f46 --- /dev/null +++ b/packages/cashc/test/import-fixtures/node_modules/@cashlibs/utils/util.cash @@ -0,0 +1,3 @@ +function scopedUtil(int a) returns (int) { + return a * 3; +} diff --git a/packages/cashc/test/import-fixtures/node_modules/mathlib/combined.cash b/packages/cashc/test/import-fixtures/node_modules/mathlib/combined.cash new file mode 100644 index 000000000..38603e68d --- /dev/null +++ b/packages/cashc/test/import-fixtures/node_modules/mathlib/combined.cash @@ -0,0 +1,5 @@ +import "@cashlibs/utils/util.cash"; + +function nmCombined(int a) returns (int) { + return scopedUtil(a) + 1; +} diff --git a/packages/cashc/test/import-fixtures/node_modules/mathlib/helper.cash b/packages/cashc/test/import-fixtures/node_modules/mathlib/helper.cash new file mode 100644 index 000000000..43b9a6253 --- /dev/null +++ b/packages/cashc/test/import-fixtures/node_modules/mathlib/helper.cash @@ -0,0 +1,3 @@ +function nmHelper(int a) returns (int) { + return a + 5; +} diff --git a/packages/cashc/test/import-fixtures/node_modules/mathlib/main.cash b/packages/cashc/test/import-fixtures/node_modules/mathlib/main.cash new file mode 100644 index 000000000..7715b3aad --- /dev/null +++ b/packages/cashc/test/import-fixtures/node_modules/mathlib/main.cash @@ -0,0 +1,5 @@ +import "./helper.cash"; + +function nmMain(int a) returns (int) { + return nmHelper(a) * 2; +} diff --git a/packages/cashc/test/import-fixtures/node_modules/mathlib/math.cash b/packages/cashc/test/import-fixtures/node_modules/mathlib/math.cash new file mode 100644 index 000000000..12ff81286 --- /dev/null +++ b/packages/cashc/test/import-fixtures/node_modules/mathlib/math.cash @@ -0,0 +1,3 @@ +function nmAdd(int a, int b) returns (int) { + return a + b; +} diff --git a/packages/cashc/test/import-fixtures/shadow/nm_shadow_main.cash b/packages/cashc/test/import-fixtures/shadow/nm_shadow_main.cash new file mode 100644 index 000000000..a46dcd69a --- /dev/null +++ b/packages/cashc/test/import-fixtures/shadow/nm_shadow_main.cash @@ -0,0 +1,7 @@ +import "mathlib/math.cash"; + +contract NodeModulesShadow() { + function spend(int x) { + require(nmNearest(x) == 4); + } +} diff --git a/packages/cashc/test/import-fixtures/shadow/node_modules/mathlib/math.cash b/packages/cashc/test/import-fixtures/shadow/node_modules/mathlib/math.cash new file mode 100644 index 000000000..82d638be1 --- /dev/null +++ b/packages/cashc/test/import-fixtures/shadow/node_modules/mathlib/math.cash @@ -0,0 +1,3 @@ +function nmNearest(int a) returns (int) { + return a - 1; +} diff --git a/packages/cashc/test/import-fixtures/shared.cash b/packages/cashc/test/import-fixtures/shared.cash new file mode 100644 index 000000000..9aa4ca0d2 --- /dev/null +++ b/packages/cashc/test/import-fixtures/shared.cash @@ -0,0 +1,3 @@ +function shared(int a) returns (int) { + return a + 10; +} diff --git a/packages/cashc/test/imports.test.ts b/packages/cashc/test/imports.test.ts new file mode 100644 index 000000000..862522f8e --- /dev/null +++ b/packages/cashc/test/imports.test.ts @@ -0,0 +1,279 @@ +import fs from 'fs'; +import { fileURLToPath } from 'url'; +import { compileFile, compileString } from '../src/internal.js'; +import { ImportResolutionError, RedefinitionError, VersionError } from '../src/Errors.js'; + +const fixture = (name: string): string => fileURLToPath(new URL(`./import-fixtures/${name}`, import.meta.url)); + +const readFixture = (name: string): string => fs.readFileSync(fixture(name), { encoding: 'utf-8' }); + +const countOpDefines = (bytecode: string): number => [...bytecode.matchAll(/OP_DEFINE/g)].length; + +describe('Imports from the filesystem (compileFile)', () => { + it('merges global functions from an imported file', () => { + const artifact = compileFile(fixture('main.cash'), { disableInlining: true }); + expect(artifact.contractName).toEqual('Main'); + expect(artifact.bytecode).toContain('OP_INVOKE'); + // both imported functions are defined (one OP_DEFINE each) + expect(countOpDefines(artifact.bytecode)).toEqual(2); + }); + + it('de-duplicates a diamond import so a shared leaf is defined once', () => { + // Diamond imports mid1 and mid2, which both import leaf. The leaf function must be merged once + // (otherwise it would be a redefinition): leaf, m1, m2 = 3 OP_DEFINEs. + const artifact = compileFile(fixture('diamond.cash'), { disableInlining: true }); + expect(artifact.contractName).toEqual('Diamond'); + expect(countOpDefines(artifact.bytecode)).toEqual(3); + }); + + it('destructures a multi-return function imported from another file', () => { + // The multi-return function is defined in an imported file and destructured in the contract, + // proving multi-return composes with the import/module system. + const artifact = compileFile(fixture('multi_return_main.cash'), { disableInlining: true }); + expect(artifact.contractName).toEqual('MultiReturnMain'); + expect(artifact.bytecode).toContain('OP_INVOKE'); + expect(countOpDefines(artifact.bytecode)).toEqual(1); + }); + + it('throws when an imported file cannot be found', () => { + expect(() => compileFile(fixture('missing_import_main.cash'))).toThrow(ImportResolutionError); + }); + + it('throws when an imported function collides with a local function of the same name', () => { + // duplicate_import_main defines `shared` and imports a file that also defines `shared`. + expect(() => compileFile(fixture('duplicate_import_main.cash'))).toThrow(RedefinitionError); + }); + + it('throws on cyclic imports', () => { + // cycle_a imports cycle_b which imports cycle_a back + expect(() => compileFile(fixture('cycle_main.cash'))).toThrow(ImportResolutionError); + expect(() => compileFile(fixture('cycle_main.cash'))).toThrow(/Cyclic import of '\.\/cycle_a\.cash'/); + }); + + it('records provenance as the path relative to the main file', () => { + const artifact = compileFile(fixture('nested_main.cash'), { disableInlining: true }); + expect(artifact.debug?.functions?.map((func) => func.sourceFile)).toEqual(['nested/helper.cash']); + }); + + it('throws when an imported file has a pragma that the compiler version does not satisfy', () => { + expect(() => compileFile(fixture('bad_pragma_main.cash'))).toThrow(VersionError); + expect(() => compileFile(fixture('bad_pragma_main.cash'))).toThrow(/bad_pragma_helper\.cash/); + }); +}); + +describe('Imports from in-memory files (compileString)', () => { + const mathSource = ` + function addOne(int a) returns (int) { return a + 1; } + function double(int a) returns (int) { return a * 2; } + `; + + const mainCode = 'import "./math.cash";\ncontract Main() { function spend(int x) { require(double(addOne(x)) == 8); } }'; + + it('merges global functions from a provided file', () => { + const artifact = compileString(mainCode, { files: { './math.cash': mathSource }, disableInlining: true }); + expect(artifact.contractName).toEqual('Main'); + expect(artifact.bytecode).toContain('OP_INVOKE'); + expect(countOpDefines(artifact.bytecode)).toEqual(2); + }); + + it('normalises file keys so they match regardless of a leading ./', () => { + const artifact = compileString(mainCode, { files: { 'math.cash': mathSource }, disableInlining: true }); + expect(countOpDefines(artifact.bytecode)).toEqual(2); + }); + + it('resolves transitive imports relative to the importing file', () => { + // lib/a.cash imports './b.cash', which is relative to lib/ — so its key is 'lib/b.cash' + const code = 'import "./lib/a.cash";\ncontract C() { function spend(int x) { require(a(x) == 7); } }'; + const files = { + 'lib/a.cash': 'import "./b.cash";\nfunction a(int n) returns (int) { return b(n) + 1; }', + 'lib/b.cash': 'function b(int n) returns (int) { return n * 3; }', + }; + + const artifact = compileString(code, { files, disableInlining: true }); + expect(countOpDefines(artifact.bytecode)).toEqual(2); + expect(artifact.debug?.functions?.map((func) => func.sourceFile).sort()).toEqual(['lib/a.cash', 'lib/b.cash']); + }); + + it('supports files with the same basename in different directories', () => { + const code = 'import "./a/helper.cash";\nimport "./b/helper.cash";\n' + + 'contract C() { function spend(int x) { require(helperA(x) + helperB(x) == 10); } }'; + const files = { + 'a/helper.cash': 'function helperA(int n) returns (int) { return n + 1; }', + 'b/helper.cash': 'function helperB(int n) returns (int) { return n * 2; }', + }; + + const artifact = compileString(code, { files, disableInlining: true }); + expect(countOpDefines(artifact.bytecode)).toEqual(2); + }); + + it('de-duplicates a diamond import so a shared leaf is defined once', () => { + const code = 'import "./mid1.cash";\nimport "./mid2.cash";\n' + + 'contract Diamond() { function spend(int x) { require(m1(x) + m2(x) == 18); } }'; + const files = { + 'mid1.cash': 'import "./leaf.cash";\nfunction m1(int a) returns (int) { return leaf(a) * 2; }', + 'mid2.cash': 'import "./leaf.cash";\nfunction m2(int a) returns (int) { return leaf(a) + 3; }', + 'leaf.cash': 'function leaf(int a) returns (int) { return a + 1; }', + }; + + const artifact = compileString(code, { files, disableInlining: true }); + expect(countOpDefines(artifact.bytecode)).toEqual(3); + }); + + it('resolves parent-directory imports', () => { + const code = 'import "../shared.cash";\ncontract C() { function spend(int x) { require(shared(x) == 4); } }'; + const files = { '../shared.cash': 'function shared(int n) returns (int) { return n + 1; }' }; + + const artifact = compileString(code, { files, disableInlining: true }); + expect(countOpDefines(artifact.bytecode)).toEqual(1); + }); + + it('throws when compiling a string with imports without providing files', () => { + expect(() => compileString(mainCode)).toThrow(ImportResolutionError); + }); + + it('throws when an import is missing from the provided files', () => { + expect(() => compileString(mainCode, { files: {} })).toThrow(ImportResolutionError); + }); + + it('compiles when the pragmas of all imported files are satisfied', () => { + const files = { './math.cash': `pragma cashscript >=0.14.0;\n${mathSource}` }; + const artifact = compileString(mainCode, { files, disableInlining: true }); + expect(countOpDefines(artifact.bytecode)).toEqual(2); + }); + + it('throws a VersionError naming the imported file when its pragma is not satisfied', () => { + const files = { './math.cash': `pragma cashscript >=999.0.0;\n${mathSource}` }; + expect(() => compileString(mainCode, { files })).toThrow(VersionError); + expect(() => compileString(mainCode, { files })).toThrow( + /cashc version .* does not satisfy version constraint >=999\.0\.0 \(from pragma in imported file 'math\.cash'\)/, + ); + }); + + it('enforces the pragma of a transitively imported file', () => { + const code = 'import "./lib/a.cash";\ncontract C() { function spend(int x) { require(a(x) == 7); } }'; + const files = { + 'lib/a.cash': 'import "./b.cash";\nfunction a(int n) returns (int) { return b(n) + 1; }', + 'lib/b.cash': 'pragma cashscript >=999.0.0;\nfunction b(int n) returns (int) { return n * 3; }', + }; + + expect(() => compileString(code, { files })).toThrow(VersionError); + expect(() => compileString(code, { files })).toThrow(/lib\/b\.cash/); + }); +}); + +describe('Imports from node_modules (package imports)', () => { + it('resolves a package import from the nearest node_modules directory', () => { + const artifact = compileFile(fixture('nm_main.cash'), { disableInlining: true }); + expect(artifact.contractName).toEqual('NodeModulesMain'); + expect(artifact.bytecode).toContain('OP_INVOKE'); + expect(countOpDefines(artifact.bytecode)).toEqual(1); + }); + + it('walks up parent directories to find node_modules', () => { + // nested/nm_nested_main.cash has no nested/node_modules, so resolution walks up to + // import-fixtures/node_modules + const artifact = compileFile(fixture('nested/nm_nested_main.cash'), { disableInlining: true }); + expect(artifact.contractName).toEqual('NodeModulesNested'); + expect(countOpDefines(artifact.bytecode)).toEqual(1); + }); + + it('prefers the nearest node_modules over an ancestor one', () => { + // shadow/node_modules/mathlib/math.cash defines nmNearest, the ancestor copy defines nmAdd. + // The contract calls nmNearest, so it only compiles if the nearest copy is resolved. + const artifact = compileFile(fixture('shadow/nm_shadow_main.cash'), { disableInlining: true }); + expect(artifact.contractName).toEqual('NodeModulesShadow'); + expect(countOpDefines(artifact.bytecode)).toEqual(1); + }); + + it('resolves relative imports inside an imported package relative to the package file', () => { + // mathlib/main.cash imports './helper.cash', which lives inside the package + const artifact = compileFile(fixture('nm_transitive_main.cash'), { disableInlining: true }); + expect(countOpDefines(artifact.bytecode)).toEqual(2); + }); + + it('resolves package imports inside an imported package (transitive dependencies)', () => { + // mathlib/combined.cash itself contains a package import of @cashlibs/utils/util.cash, which is + // resolved by walking up from mathlib's own directory to the shared node_modules + const artifact = compileFile(fixture('nm_package_deps_main.cash'), { disableInlining: true }); + expect(artifact.contractName).toEqual('NodeModulesPackageDeps'); + expect(countOpDefines(artifact.bytecode)).toEqual(2); + expect(artifact.debug?.functions?.map((func) => func.sourceFile).sort()) + .toEqual(['@cashlibs/utils/util.cash', 'mathlib/combined.cash']); + }); + + it('resolves scoped package imports', () => { + const artifact = compileFile(fixture('nm_scoped_main.cash'), { disableInlining: true }); + expect(artifact.contractName).toEqual('NodeModulesScoped'); + expect(countOpDefines(artifact.bytecode)).toEqual(1); + }); + + it('records provenance as the package import path rather than a filesystem path', () => { + const artifact = compileFile(fixture('nm_transitive_main.cash'), { disableInlining: true }); + expect(artifact.debug?.functions?.map((func) => func.sourceFile).sort()) + .toEqual(['mathlib/helper.cash', 'mathlib/main.cash']); + }); + + it('throws when a package import cannot be found in any node_modules directory', () => { + expect(() => compileFile(fixture('nm_missing_main.cash'))).toThrow(ImportResolutionError); + expect(() => compileFile(fixture('nm_missing_main.cash'))).toThrow( + /Could not find imported file 'nonexistent-cashscript-pkg\/foo\.cash' in any node_modules directory/, + ); + }); + + it('resolves package imports verbatim from the files option when compiling from a string', () => { + const code = 'import "mathlib/math.cash";\n' + + 'contract C() { function spend(int x) { require(nmAdd(x, 3) == 8); } }'; + const files = { 'mathlib/math.cash': readFixture('node_modules/mathlib/math.cash') }; + + const artifact = compileString(code, { files, disableInlining: true }); + expect(countOpDefines(artifact.bytecode)).toEqual(1); + expect(artifact.debug?.functions?.map((func) => func.sourceFile)).toEqual(['mathlib/math.cash']); + }); + + it('resolves relative imports inside an in-memory package relative to the package file', () => { + const code = 'import "pkg/main.cash";\n' + + 'contract C() { function spend(int x) { require(pkgMain(x) == 12); } }'; + const files = { + 'pkg/main.cash': 'import "./helper.cash";\nfunction pkgMain(int n) returns (int) { return pkgHelper(n) * 2; }', + 'pkg/helper.cash': 'function pkgHelper(int n) returns (int) { return n + 1; }', + }; + + const artifact = compileString(code, { files, disableInlining: true }); + expect(countOpDefines(artifact.bytecode)).toEqual(2); + }); + + it('compiles a node_modules import to the exact same artifact from disk and from memory', () => { + const fromDisk = compileFile(fixture('nm_transitive_main.cash'), { disableInlining: true }); + + const files = { + 'mathlib/main.cash': readFixture('node_modules/mathlib/main.cash'), + 'mathlib/helper.cash': readFixture('node_modules/mathlib/helper.cash'), + }; + const fromString = compileString(readFixture('nm_transitive_main.cash'), { files, disableInlining: true }); + + expect(fromString).toEqual({ ...fromDisk, updatedAt: expect.any(String) }); + }); +}); + +describe('compileFile / compileString equivalence', () => { + it('compiles a complex import graph to the exact same artifact from disk and from memory', () => { + // complex/main.cash exercises nested directories, a diamond (a and b both import util/leaf), + // and a parent-directory import reached through two different routes (main imports + // '../shared.cash' and lib/a.cash imports '../../shared.cash' — both must de-duplicate to the + // same file). + const fromDisk = compileFile(fixture('complex/main.cash'), { disableInlining: true }); + + const files = { + 'lib/a.cash': readFixture('complex/lib/a.cash'), + 'lib/b.cash': readFixture('complex/lib/b.cash'), + 'lib/util/leaf.cash': readFixture('complex/lib/util/leaf.cash'), + '../shared.cash': readFixture('shared.cash'), + }; + const fromString = compileString(readFixture('complex/main.cash'), { files, disableInlining: true }); + + // sanity-check the fixture actually pulls in all four imported functions + expect(countOpDefines(fromDisk.bytecode)).toEqual(4); + + expect(fromString).toEqual({ ...fromDisk, updatedAt: expect.any(String) }); + }); +}); diff --git a/packages/cashc/test/semantic/InjectLocktimeGuardTraversal.test.ts b/packages/cashc/test/semantic/InjectLocktimeGuardTraversal.test.ts index fc89f420b..c99f91139 100644 --- a/packages/cashc/test/semantic/InjectLocktimeGuardTraversal.test.ts +++ b/packages/cashc/test/semantic/InjectLocktimeGuardTraversal.test.ts @@ -213,3 +213,102 @@ describe('InjectLocktimeGuardTraversal', () => { expect(compileString(src, { enforceLocktimeGuard: false }).bytecode.startsWith(GUARD_PREFIX)).toBe(false); }); }); + +// The guard is only injected into contract spending functions, never into global functions (non-finality +// is a transaction-wide property, so a single guard on the spending path covers any tx.locktime accessed +// inside invoked global functions). Whether a contract function needs the guard is therefore decided by +// looking through the global functions it (transitively) invokes. The injected guard is a require with a +// distinctive message, which is detectable even when the OP_DEFINE prologue precedes the contract body. +describe('InjectLocktimeGuardTraversal — global functions', () => { + const GUARD_MESSAGE = 'non-final sequence number'; + const guardInjected = (src: string): boolean => (compileString(src).debug?.requires ?? []) + .some((statement) => statement.message?.includes(GUARD_MESSAGE)); + + it('injects guard in the contract when an invoked global uses tx.locktime', () => { + const src = ` + function usesLocktime() returns (int) { return tx.locktime; } + contract T() { + function spend() { + require(usesLocktime() >= 100); + } + }`; + expect(guardInjected(src)).toBe(true); + }); + + it('does not inject when the contract has a tx.time check before invoking the global', () => { + const src = ` + function usesLocktime() returns (int) { return tx.locktime; } + contract T() { + function spend() { + require(tx.time >= 100); + require(usesLocktime() >= 100); + } + }`; + expect(guardInjected(src)).toBe(false); + }); + + it('does not inject when the invoked global covers its own tx.locktime with a tx.time check', () => { + const src = ` + function usesLocktime() returns (int) { require(tx.time >= 100); return tx.locktime; } + contract T() { + function spend() { + require(usesLocktime() >= 0); + } + }`; + expect(guardInjected(src)).toBe(false); + }); + + it('injects guard for tx.locktime reached transitively through nested global invocations', () => { + const src = ` + function inner() returns (int) { return tx.locktime; } + function outer() returns (int) { return inner(); } + contract T() { + function spend() { + require(outer() >= 100); + } + }`; + expect(guardInjected(src)).toBe(true); + }); + + it('does not inject when a caller global covers the callee with its own tx.time check', () => { + const src = ` + function inner() returns (int) { return tx.locktime; } + function outer() returns (int) { require(tx.time >= 100); return inner(); } + contract T() { + function spend() { + require(outer() >= 0); + } + }`; + expect(guardInjected(src)).toBe(false); + }); + + it('injects guard when an invoked global only uses tx.locktime inside a branch', () => { + const src = ` + function maybeLocktime(int a) { + if (a > 0) { + int x = tx.locktime; + require(x >= 100); + } else { + require(a == 0); + } + } + contract T() { + function spend(int x) { + maybeLocktime(x); + require(x >= 0); + } + }`; + expect(guardInjected(src)).toBe(true); + }); + + it('does not inject when an invoked global does not use tx.locktime', () => { + const src = ` + function double(int a) returns (int) { return a * 2; } + contract T() { + function spend(int x) { + require(double(x) >= 2); + } + }`; + expect(guardInjected(src)).toBe(false); + }); +}); diff --git a/packages/cashc/test/valid-contract-files/checkdatasig_in_function.cash b/packages/cashc/test/valid-contract-files/checkdatasig_in_function.cash new file mode 100644 index 000000000..0ba76f2e2 --- /dev/null +++ b/packages/cashc/test/valid-contract-files/checkdatasig_in_function.cash @@ -0,0 +1,9 @@ +function verifyData(datasig s, bytes message, pubkey pk) returns (bool) { + return checkDataSig(s, message, pk); +} + +contract Test() { + function spend(datasig s, bytes message, pubkey pk) { + require(verifyData(s, message, pk)); + } +} diff --git a/packages/cashc/test/valid-contract-files/double_negation.cash b/packages/cashc/test/valid-contract-files/double_negation.cash new file mode 100644 index 000000000..55bcfb785 --- /dev/null +++ b/packages/cashc/test/valid-contract-files/double_negation.cash @@ -0,0 +1,22 @@ +contract DoubleNegation(bool flag) { + function spend(int target) { + // OP_NOT OP_NOT OP_VERIFY => OP_VERIFY + require(!!flag); + + int i = 0; + bool done = false; + + do { + i = i + 1; + done = i >= target; + // OP_NOT OP_NOT OP_UNTIL => OP_UNTIL + } while (!done); + + // OP_NOT OP_NOT OP_IF => OP_NOT OP_NOTIF => OP_IF + if (!!flag) { + require(i == target); + } + + require(i > 0); + } +} diff --git a/packages/cashc/test/valid-contract-files/global_constant_arithmetic.cash b/packages/cashc/test/valid-contract-files/global_constant_arithmetic.cash new file mode 100644 index 000000000..be41fe11b --- /dev/null +++ b/packages/cashc/test/valid-contract-files/global_constant_arithmetic.cash @@ -0,0 +1,17 @@ +int constant BASE = 10 + 10; +int constant DERIVED = BASE - 5; +int constant NEGATED = -DERIVED * 2; +int constant COMPLEX = (BASE * 100 + 50) / 4 % 1000; +string constant GREETING = "hello" + " " + "world"; +bytes4 constant MAGIC = 0x0102 + 0x0304; + +contract GlobalConstantArithmetic() { + function spend(int base, int derived, int negated, int complex, string greeting, bytes4 magic) { + require(base == BASE); + require(derived == DERIVED); + require(negated == NEGATED); + require(complex == COMPLEX); + require(greeting == GREETING); + require(magic == MAGIC); + } +} diff --git a/packages/cashc/test/valid-contract-files/global_constant_inlined.cash b/packages/cashc/test/valid-contract-files/global_constant_inlined.cash new file mode 100644 index 000000000..f582bea84 --- /dev/null +++ b/packages/cashc/test/valid-contract-files/global_constant_inlined.cash @@ -0,0 +1,7 @@ +int constant ONE = 1; + +contract GlobalConstantInlined(int value) { + function spend() { + require(value + ONE + ONE == 3); + } +} diff --git a/packages/cashc/test/valid-contract-files/global_constant_literals.cash b/packages/cashc/test/valid-contract-files/global_constant_literals.cash new file mode 100644 index 000000000..a27a7eb6d --- /dev/null +++ b/packages/cashc/test/valid-contract-files/global_constant_literals.cash @@ -0,0 +1,17 @@ +bool constant ENABLED = true; +int constant NEGATIVE = -7; +int constant INTERVAL = 2 hours; +int constant DEADLINE = date("2021-02-17T01:30:00"); +string constant GREETING = 'hello'; +bytes4 constant MAGIC = 0x01020304; + +contract GlobalConstantLiterals() { + function spend(bool enabled, int negative, int interval, int deadline, string greeting, bytes4 magic) { + require(enabled == ENABLED); + require(negative == NEGATIVE); + require(interval == INTERVAL); + require(deadline == DEADLINE); + require(greeting == GREETING); + require(magic == MAGIC); + } +} diff --git a/packages/cashc/test/valid-contract-files/global_constant_shared.cash b/packages/cashc/test/valid-contract-files/global_constant_shared.cash new file mode 100644 index 000000000..c64c198fe --- /dev/null +++ b/packages/cashc/test/valid-contract-files/global_constant_shared.cash @@ -0,0 +1,8 @@ +bytes32 constant HASH = 0x3333333333333333333333333333333333333333333333333333333333333333; + +contract GlobalConstantShared(bytes32 first, bytes32 second) { + function spend() { + require(first == HASH); + require(second == HASH); + } +} diff --git a/packages/cashc/test/valid-contract-files/global_function_altstack_cleanup.cash b/packages/cashc/test/valid-contract-files/global_function_altstack_cleanup.cash new file mode 100644 index 000000000..4f539ad7b --- /dev/null +++ b/packages/cashc/test/valid-contract-files/global_function_altstack_cleanup.cash @@ -0,0 +1,20 @@ +function orderedPair(int a, int b, int c) returns (int, int) { + int lo = 0; + int hi = 0; + if (c > 0) { + lo = a; + hi = b; + } else { + lo = b; + hi = a; + } + return lo, hi; +} + +contract GlobalFunctionAltStackCleanup() { + function spend(int x) { + int lo, int hi = orderedPair(1, 2, x); + require(lo == 1, "lo should be 1"); + require(hi == 2, "hi should be 2"); + } +} diff --git a/packages/cashc/test/valid-contract-files/global_function_in_control_flow.cash b/packages/cashc/test/valid-contract-files/global_function_in_control_flow.cash new file mode 100644 index 000000000..9d9d6a4b0 --- /dev/null +++ b/packages/cashc/test/valid-contract-files/global_function_in_control_flow.cash @@ -0,0 +1,17 @@ +function triple(int a) returns (int) { + return a * 3; +} + +contract GlobalFunctionInControlFlow() { + function spend(int x, bool useLoop) { + int total = 0; + if (useLoop) { + for (int i = 0; i < 3; i = i + 1) { + total = total + triple(x); + } + } else { + total = triple(x); + } + require(total == 9); + } +} diff --git a/packages/cashc/test/valid-contract-files/global_function_inlined.cash b/packages/cashc/test/valid-contract-files/global_function_inlined.cash new file mode 100644 index 000000000..73457c459 --- /dev/null +++ b/packages/cashc/test/valid-contract-files/global_function_inlined.cash @@ -0,0 +1,11 @@ +function checked(int x) returns (int) { + console.log("checking", x); + require(x > 0, "positive"); + return x; +} + +contract GlobalFunctionInlined() { + function spend(int n) { + require(checked(n) == n); + } +} diff --git a/packages/cashc/test/valid-contract-files/global_function_multi_param.cash b/packages/cashc/test/valid-contract-files/global_function_multi_param.cash new file mode 100644 index 000000000..badac11dc --- /dev/null +++ b/packages/cashc/test/valid-contract-files/global_function_multi_param.cash @@ -0,0 +1,9 @@ +function sub(int a, int b) returns (int) { + return a - b; +} + +contract GlobalFunctionMultiParam() { + function spend(int x, int y) { + require(sub(x, y) == 7); + } +} diff --git a/packages/cashc/test/valid-contract-files/global_function_multi_return.cash b/packages/cashc/test/valid-contract-files/global_function_multi_return.cash new file mode 100644 index 000000000..384ab9603 --- /dev/null +++ b/packages/cashc/test/valid-contract-files/global_function_multi_return.cash @@ -0,0 +1,11 @@ +function divmod(int a, int b) returns (int, int) { + return a / b, a % b; +} + +contract GlobalFunctionMultiReturn() { + function spend(int x) { + int q, int r = divmod(x, 3); + require(q == 4); + require(r == 1); + } +} diff --git a/packages/cashc/test/valid-contract-files/global_function_multi_return_three.cash b/packages/cashc/test/valid-contract-files/global_function_multi_return_three.cash new file mode 100644 index 000000000..15e1ffcff --- /dev/null +++ b/packages/cashc/test/valid-contract-files/global_function_multi_return_three.cash @@ -0,0 +1,12 @@ +function spread(int a) returns (int, int, int) { + return a, a + 1, a + 2; +} + +contract GlobalFunctionMultiReturnThree() { + function spend(int x) { + int p, int q, int r = spread(x); + require(p == 5); + require(q == 6); + require(r == 7); + } +} diff --git a/packages/cashc/test/valid-contract-files/global_function_nested.cash b/packages/cashc/test/valid-contract-files/global_function_nested.cash new file mode 100644 index 000000000..cc350bcb9 --- /dev/null +++ b/packages/cashc/test/valid-contract-files/global_function_nested.cash @@ -0,0 +1,13 @@ +function addOne(int a) returns (int) { + return a + 1; +} + +function doubleIncremented(int a) returns (int) { + return addOne(a) * 2; +} + +contract GlobalFunctionNested() { + function spend(int x) { + require(doubleIncremented(x) == 8); + } +} diff --git a/packages/cashc/test/valid-contract-files/global_function_simple.cash b/packages/cashc/test/valid-contract-files/global_function_simple.cash new file mode 100644 index 000000000..851f6cd7f --- /dev/null +++ b/packages/cashc/test/valid-contract-files/global_function_simple.cash @@ -0,0 +1,9 @@ +function double(int a) returns (int) { + return a * 2; +} + +contract GlobalFunctionSimple() { + function spend(int x) { + require(double(x) == 6); + } +} diff --git a/packages/cashc/test/valid-contract-files/global_function_void.cash b/packages/cashc/test/valid-contract-files/global_function_void.cash new file mode 100644 index 000000000..945569f73 --- /dev/null +++ b/packages/cashc/test/valid-contract-files/global_function_void.cash @@ -0,0 +1,10 @@ +function requirePositive(int a) { + require(a > 0); +} + +contract GlobalFunctionVoid() { + function spend(int x) { + requirePositive(x); + require(x < 100); + } +} diff --git a/packages/cashc/test/valid-contract-files/tuple_modifiers.cash b/packages/cashc/test/valid-contract-files/tuple_modifiers.cash new file mode 100644 index 000000000..c603d6640 --- /dev/null +++ b/packages/cashc/test/valid-contract-files/tuple_modifiers.cash @@ -0,0 +1,27 @@ +// Tuple assignment targets accept the same modifiers as variable definitions: `constant` makes +// the declared variable immutable and `unused` drops the value right after the assignment, so a +// destructuring can keep just part of a tuple. + +function pair(int x) returns (int, int) { + return x + 1, x * 2; +} + +contract TupleModifiers() { + function spend(int n) { + // `unused` on the first target: the kept value is nipped from under the dropped one + int unused incremented, int constant doubled = pair(n); + + // `unused` on the last target: dropped straight off the top of the stack + bytes payload, bytes unused padding = 0x1234.split(1); + + // mixed in-branch reassignment: the unused declaration is dropped instead of being + // parked on the altstack + int acc = n; + if (acc > 0) { + (acc, int unused scratch) = pair(acc); + } + + require(acc + doubled >= 0); + require(payload == 0x12); + } +} diff --git a/packages/cashc/test/valid-contract-files/tuple_reassignment.cash b/packages/cashc/test/valid-contract-files/tuple_reassignment.cash new file mode 100644 index 000000000..a1a80ffd1 --- /dev/null +++ b/packages/cashc/test/valid-contract-files/tuple_reassignment.cash @@ -0,0 +1,50 @@ +// Destructuring into existing variables: a target without a type reassigns an already-declared +// variable instead of declaring a fresh one. Covers straight-line reassignment, mixed +// declaration+reassignment, and in-loop reassignment of loop-carried state (the case that previously +// needed a fresh-temp + per-element rebind workaround). + +function swap(int x, int y) returns (int, int) { + return y, x; +} + +// returns a fresh scalar followed by the two updated accumulators +function step(int x, int y) returns (int, int, int) { + return x * x + 1, y, x; +} + +contract TupleReassignment() { + function spend(int seed) { + int a = seed; + int b = seed + 1; + + // straight-line reassignment of both existing variables + (a, b) = swap(a, b); + + // mixed: declare c fresh, reassign a + (int c, a) = swap(a, b); + + // loop-carried state reassigned in place each iteration + for (int i = 0; i < 4; i = i + 1) { + (a, b) = swap(a, b); + } + + // mixed declaration + reassignment IN A LOOP: declarations-first compiles smallest + // (the reassignment values fold straight off the top of the stack) + for (int j = 0; j < 4; j = j + 1) { + (int lo, a, b) = step(a, b); + require(lo >= 0); + } + + // interleaved order works too: the declaration value is parked on the altstack while the + // reassignment value below it folds into its slot + for (int k = 0; k < 2; k = k + 1) { + (a, int hi, b) = step(a, b); + require(hi >= 0); + } + + // reassignment before declaration, in the bare (unparenthesized) form + b, int d = swap(a, c); + + require(a + b + c + d >= 0); + } +} diff --git a/packages/cashc/test/valid-contract-files/tuple_reassignment_after_final_read.cash b/packages/cashc/test/valid-contract-files/tuple_reassignment_after_final_read.cash new file mode 100644 index 000000000..15a643f64 --- /dev/null +++ b/packages/cashc/test/valid-contract-files/tuple_reassignment_after_final_read.cash @@ -0,0 +1,31 @@ +// Regression coverage: a scoped tuple reassignment is itself the variable's latest use, so the +// symbol pass must move the opRolls entry to it. Previously the last plain read kept the roll, +// codegen rolled the variable off the stack model at that read, and the reassignment crashed with +// a raw "Expected variable ... does not exist on the stack". + +function pair(int n) returns (int, int) { + return n * 2, n + 1; +} + +contract ReassignAfterFinalRead() { + function ifBranch(int x) { + int p = 10; + int q = 0; + require(p == 10); + if (x == 1) { + (p, q) = pair(x); + } + require(q == 0 || q == 2); + } + + function whileLoop(int x) { + int p = 10; + int q = 0; + require(p == 10); + while (x == 1) { + (p, q) = pair(x); + x = x + 1; + } + require(q == 0 || q == 2); + } +} diff --git a/packages/cashc/test/valid-contract-files/tuple_reassignment_branches.cash b/packages/cashc/test/valid-contract-files/tuple_reassignment_branches.cash new file mode 100644 index 000000000..f19f83281 --- /dev/null +++ b/packages/cashc/test/valid-contract-files/tuple_reassignment_branches.cash @@ -0,0 +1,33 @@ +// Scoped tuple reassignment inside if-branches: the stack layout must be preserved, so +// reassignment values are folded into the existing variable slots (with altstack parking when a +// declaration value sits above a reassignment value). + +// Called from both contract functions and large enough that it is NOT inlined, so destructuring +// also covers results produced by an OP_INVOKE call (single-call functions are always inlined). +function branchPair(int x) returns (int, int) { + int t = x * 3 + (x % 7) + (x / 2); + return t + 1, t * 2 - x; +} + +contract TupleReassignmentBranches() { + // mixed declaration + reassignment inside a branch, declarations first: no parking needed + function declarationFirst(int a) { + int total = 0; + if (a > 10) { + int d, total = branchPair(a); + require(d != 0); + } + require(total >= 0); + } + + // reassignment before declaration inside a branch: the trailing declaration value is parked + // on the altstack while the reassignment value below it folds into its slot + function reassignmentFirst(int a) { + int total = 0; + if (a > 10) { + (total, int extra) = branchPair(a + 1); + require(extra != 0); + } + require(total >= 0); + } +} diff --git a/packages/cashc/test/valid-contract-files/unused_modifier.cash b/packages/cashc/test/valid-contract-files/unused_modifier.cash new file mode 100644 index 000000000..344d451c5 --- /dev/null +++ b/packages/cashc/test/valid-contract-files/unused_modifier.cash @@ -0,0 +1,11 @@ +function pad(int value, int unused padding) returns (int) { + return value; +} + +contract UnusedModifier(int unused salt) { + function spend(int a, int b, bytes unused zeroPadding) { + int unused scratch = a + b; + int constant unused magic = 42; + require(pad(a, 100) + b == 5); + } +} diff --git a/packages/cashc/test/warnings/UnusedAssignmentWarning/loop_reassignment_after_final_read.cash b/packages/cashc/test/warnings/UnusedAssignmentWarning/loop_reassignment_after_final_read.cash new file mode 100644 index 000000000..84bc843bf --- /dev/null +++ b/packages/cashc/test/warnings/UnusedAssignmentWarning/loop_reassignment_after_final_read.cash @@ -0,0 +1,12 @@ +contract Test() { + function spend(int a) { + int x = 1; + require(x == 1); + int i = 0; + while (i < a) { + x = i; + i = i + 1; + } + require(true); + } +} diff --git a/packages/cashc/test/warnings/UnusedAssignmentWarning/parameter_reassignment.cash b/packages/cashc/test/warnings/UnusedAssignmentWarning/parameter_reassignment.cash new file mode 100644 index 000000000..0c6c3fb8e --- /dev/null +++ b/packages/cashc/test/warnings/UnusedAssignmentWarning/parameter_reassignment.cash @@ -0,0 +1,7 @@ +contract Test() { + function spend(int a) { + require(a == 1); + a = 2; + require(true); + } +} diff --git a/packages/cashc/test/warnings/UnusedAssignmentWarning/reassignment_after_final_read.cash b/packages/cashc/test/warnings/UnusedAssignmentWarning/reassignment_after_final_read.cash new file mode 100644 index 000000000..605a93048 --- /dev/null +++ b/packages/cashc/test/warnings/UnusedAssignmentWarning/reassignment_after_final_read.cash @@ -0,0 +1,8 @@ +contract Test() { + function spend() { + int x = 1; + require(x == 1); + x = 2; + require(true); + } +} diff --git a/packages/cashc/test/warnings/UnusedAssignmentWarning/reassignment_in_branch.cash b/packages/cashc/test/warnings/UnusedAssignmentWarning/reassignment_in_branch.cash new file mode 100644 index 000000000..4dfdcf798 --- /dev/null +++ b/packages/cashc/test/warnings/UnusedAssignmentWarning/reassignment_in_branch.cash @@ -0,0 +1,10 @@ +contract Test(int a) { + function spend() { + int x = 1; + require(x == 1); + if (a > 0) { + x = 2; + } + require(true); + } +} diff --git a/packages/cashc/test/warnings/UnusedAssignmentWarning/self_referencing_reassignment.cash b/packages/cashc/test/warnings/UnusedAssignmentWarning/self_referencing_reassignment.cash new file mode 100644 index 000000000..6d865705e --- /dev/null +++ b/packages/cashc/test/warnings/UnusedAssignmentWarning/self_referencing_reassignment.cash @@ -0,0 +1,8 @@ +contract Test() { + function spend() { + int x = 1; + require(x == 1); + x = x + 1; + require(true); + } +} diff --git a/packages/cashc/test/warnings/UnusedAssignmentWarning/tuple_reassignment_after_final_read.cash b/packages/cashc/test/warnings/UnusedAssignmentWarning/tuple_reassignment_after_final_read.cash new file mode 100644 index 000000000..e04d9fa2a --- /dev/null +++ b/packages/cashc/test/warnings/UnusedAssignmentWarning/tuple_reassignment_after_final_read.cash @@ -0,0 +1,9 @@ +contract Test() { + function spend() { + bytes head = 0x00; + bytes tail = 0x01; + require(head == 0x00 && tail == 0x01); + (head, tail) = 0x1234.split(1); + require(true); + } +} diff --git a/packages/cashc/test/compiler/UnusedVariableError/final_variable_definition.cash b/packages/cashc/test/warnings/UnusedVariableWarning/final_variable_definition.cash similarity index 100% rename from packages/cashc/test/compiler/UnusedVariableError/final_variable_definition.cash rename to packages/cashc/test/warnings/UnusedVariableWarning/final_variable_definition.cash diff --git a/packages/cashc/test/compiler/UnusedVariableError/unused_contract_parameter.cash b/packages/cashc/test/warnings/UnusedVariableWarning/unused_contract_parameter.cash similarity index 100% rename from packages/cashc/test/compiler/UnusedVariableError/unused_contract_parameter.cash rename to packages/cashc/test/warnings/UnusedVariableWarning/unused_contract_parameter.cash diff --git a/packages/cashc/test/compiler/UnusedVariableError/unused_function_parameter.cash b/packages/cashc/test/warnings/UnusedVariableWarning/unused_function_parameter.cash similarity index 100% rename from packages/cashc/test/compiler/UnusedVariableError/unused_function_parameter.cash rename to packages/cashc/test/warnings/UnusedVariableWarning/unused_function_parameter.cash diff --git a/packages/cashc/test/warnings/UnusedVariableWarning/unused_global_function_local.cash b/packages/cashc/test/warnings/UnusedVariableWarning/unused_global_function_local.cash new file mode 100644 index 000000000..f49662013 --- /dev/null +++ b/packages/cashc/test/warnings/UnusedVariableWarning/unused_global_function_local.cash @@ -0,0 +1,10 @@ +function withUnusedLocal(int a) returns (int) { + int scratch = a + 1; + return a; +} + +contract UnusedGlobalFunctionLocal() { + function spend() { + require(withUnusedLocal(1) == 1); + } +} diff --git a/packages/cashc/test/warnings/UnusedVariableWarning/unused_global_function_parameter.cash b/packages/cashc/test/warnings/UnusedVariableWarning/unused_global_function_parameter.cash new file mode 100644 index 000000000..815d952ea --- /dev/null +++ b/packages/cashc/test/warnings/UnusedVariableWarning/unused_global_function_parameter.cash @@ -0,0 +1,9 @@ +function withUnusedParameter(int a, int b) returns (int) { + return a; +} + +contract UnusedGlobalFunctionParameter() { + function spend() { + require(withUnusedParameter(1, 2) == 1); + } +} diff --git a/packages/cashc/test/compiler/UnusedVariableError/unused_scope_variable.cash b/packages/cashc/test/warnings/UnusedVariableWarning/unused_scope_variable.cash similarity index 100% rename from packages/cashc/test/compiler/UnusedVariableError/unused_scope_variable.cash rename to packages/cashc/test/warnings/UnusedVariableWarning/unused_scope_variable.cash diff --git a/packages/cashc/test/warnings/UnusedVariableWarning/unused_tuple_target.cash b/packages/cashc/test/warnings/UnusedVariableWarning/unused_tuple_target.cash new file mode 100644 index 000000000..e89cb3c01 --- /dev/null +++ b/packages/cashc/test/warnings/UnusedVariableWarning/unused_tuple_target.cash @@ -0,0 +1,6 @@ +contract Test() { + function spend() { + bytes head, bytes tail = 0x1234.split(1); + require(tail == 0x34); + } +} diff --git a/packages/cashc/test/compiler/UnusedVariableError/unused_variable.cash b/packages/cashc/test/warnings/UnusedVariableWarning/unused_variable.cash similarity index 100% rename from packages/cashc/test/compiler/UnusedVariableError/unused_variable.cash rename to packages/cashc/test/warnings/UnusedVariableWarning/unused_variable.cash diff --git a/packages/cashc/test/warnings/UnusedVariableWarning/write_only_scoped_variables.cash b/packages/cashc/test/warnings/UnusedVariableWarning/write_only_scoped_variables.cash new file mode 100644 index 000000000..ce3b7e0b2 --- /dev/null +++ b/packages/cashc/test/warnings/UnusedVariableWarning/write_only_scoped_variables.cash @@ -0,0 +1,12 @@ +contract Test() { + function spend(int a) { + int x = 0; + bytes head = 0x00; + bytes tail = 0x01; + if (a > 0) { + x = a; + (head, tail) = 0x1234.split(1); + } + require(tail == 0x34); + } +} diff --git a/packages/cashc/test/warnings/UnusedVariableWarning/write_only_tuple_target.cash b/packages/cashc/test/warnings/UnusedVariableWarning/write_only_tuple_target.cash new file mode 100644 index 000000000..b6a490b9c --- /dev/null +++ b/packages/cashc/test/warnings/UnusedVariableWarning/write_only_tuple_target.cash @@ -0,0 +1,8 @@ +contract Test() { + function spend() { + bytes head = 0x00; + bytes tail = 0x01; + (head, tail) = 0x1234.split(1); + require(tail == 0x34); + } +} diff --git a/packages/cashc/test/warnings/UnusedVariableWarning/write_only_variable.cash b/packages/cashc/test/warnings/UnusedVariableWarning/write_only_variable.cash new file mode 100644 index 000000000..f52980095 --- /dev/null +++ b/packages/cashc/test/warnings/UnusedVariableWarning/write_only_variable.cash @@ -0,0 +1,7 @@ +contract Test() { + function spend() { + int x = 1; + x = 2; + require(true); + } +} diff --git a/packages/cashscript/package.json b/packages/cashscript/package.json index b06585735..012ec7ed5 100644 --- a/packages/cashscript/package.json +++ b/packages/cashscript/package.json @@ -1,6 +1,6 @@ { "name": "cashscript", - "version": "0.13.3", + "version": "0.14.0-next.5", "description": "Easily write and interact with Bitcoin Cash contracts", "keywords": [ "bitcoin cash", @@ -42,19 +42,19 @@ }, "dependencies": { "@bitauth/libauth": "^3.1.0-next.8", - "@cashscript/utils": "^0.13.3", - "@electrum-cash/network": "^4.1.3", - "fflate": "^0.8.2", - "semver": "^7.7.2" + "@cashscript/utils": "^0.14.0-next.5", + "@electrum-cash/network": "^4.2.2", + "fflate": "^0.8.3", + "semver": "^7.8.5" }, "devDependencies": { - "@types/semver": "^7.5.8", - "@vitest/coverage-v8": "^4.0.15", + "@types/semver": "^7.8.0", + "@vitest/coverage-v8": "^4.1.10", "eslint": "^8.54.0", "p-queue": "^9.1.2", "p-retry": "^8.0.0", "typescript": "^5.9.2", - "vitest": "^4.0.15" + "vitest": "^4.1.10" }, "gitHead": "bf02a4b641d5d03c035d052247a545109c17b708" } diff --git a/packages/cashscript/src/Contract.ts b/packages/cashscript/src/Contract.ts index bdf097d63..fc4170244 100644 --- a/packages/cashscript/src/Contract.ts +++ b/packages/cashscript/src/Contract.ts @@ -13,7 +13,7 @@ import { ConstructorArgument, encodeFunctionArgument, encodeConstructorArguments, FunctionArgument, } from './Argument.js'; import { - Unlocker, ContractOptions, GenerateUnlockingBytecodeOptions, Utxo, ContractType, ContractFunctionUnlocker, + Unlocker, ContractOptions, GenerateUnlockingBytecodeOptions, SpendableUtxo, ContractType, ContractFunctionUnlocker, } from './interfaces.js'; import NetworkProvider from './network/NetworkProvider.js'; import { @@ -84,7 +84,7 @@ class ContractBase { * * @returns A list of UTXOs spendable by this contract. */ - async getUtxos(): Promise { + async getUtxos(): Promise { if (this.contractType === 'p2s') { return this.provider.getUtxosForLockingBytecode(this.bytecode); } @@ -195,7 +195,7 @@ class ContractInternal< if (!(arg instanceof SignatureTemplate)) return arg; // Generate transaction signature from SignatureTemplate - const preimage = createSighashPreimage(transaction, sourceOutputs, inputIndex, bytecode, arg.getHashType()); + const preimage = createSighashPreimage(transaction, sourceOutputs, inputIndex, bytecode, arg.sighashType); const sighash = hash256(preimage); return arg.generateSignature(sighash); }); @@ -204,9 +204,7 @@ class ContractInternal< return unlockingBytecode; }; - const generateLockingBytecode = (): Uint8Array => hexToBin(this.lockingBytecode); - - return { generateUnlockingBytecode, generateLockingBytecode, contract: this, params: args, abiFunction }; + return { generateUnlockingBytecode, contract: this, params: args, abiFunction }; }; } } diff --git a/packages/cashscript/src/Errors.ts b/packages/cashscript/src/Errors.ts index d23e45f91..9dc6ed82c 100644 --- a/packages/cashscript/src/Errors.ts +++ b/packages/cashscript/src/Errors.ts @@ -1,4 +1,11 @@ -import { Artifact, RequireStatement, sourceMapToLocationData, Type } from '@cashscript/utils'; +import { Artifact, RequireStatement, Type } from '@cashscript/utils'; +import { + CallStackEntry, + ResolvedFrame, + getLocationDataForFrame, + resolveInlineAttribution, + rootFrame, +} from './debug-frame.js'; export class TypeError extends Error { constructor(actual: string, expected: Type) { @@ -12,6 +19,12 @@ export class UndefinedInputError extends Error { } } +export class InputMissingLockingBytecodeError extends Error { + constructor() { + super('Input UTXO is missing its lockingBytecode. UTXOs fetched from a network provider include it automatically; when constructing UTXOs manually, set lockingBytecode to the hex-encoded locking script of the output.'); + } +} + export class OutputSatoshisTooSmallError extends Error { constructor(satoshis: bigint, minimumAmount: bigint) { super(`Tried to add an output with ${satoshis} satoshis, which is less than the required minimum for this output-type (${minimumAmount})`); @@ -90,6 +103,17 @@ export class UnlockingBytecodeTooLargeError extends Error { } } +export class UnlockerLockingBytecodeMismatchError extends Error { + constructor( + public inputIndex: number, + utxoLockScript: string, + unlockerLockScript: string, + unlockerDescription: string, + ) { + super(`Input #${inputIndex} is locked by ${utxoLockScript}, which does not match the provided unlocker (${unlockerDescription}, corresponding to ${unlockerLockScript}). This transaction would be rejected by the network. Make sure to use an unlocker that matches the address/contract holding the UTXO.`); + } +} + export class TransactionTooLargeError extends Error { constructor(size: number, maximumSize: number) { super(`Transaction size of ${size} is greater than the maximum standard transaction size of ${maximumSize}`); @@ -134,12 +158,15 @@ export class FailedTransactionEvaluationError extends FailedTransactionError { public inputIndex: number, public bitauthUri: string, public libauthErrorMessage: string, + frame?: ResolvedFrame, ) { let message = `${artifact.contractName}.cash Error in transaction at input ${inputIndex} in contract ${artifact.contractName}.cash.\nReason: ${libauthErrorMessage}`; if (artifact.debug) { - const { statement, lineNumber } = getLocationDataForInstructionPointer(artifact, failingInstructionPointer); - message = `${artifact.contractName}.cash:${lineNumber} Error in transaction at input ${inputIndex} in contract ${artifact.contractName}.cash at line ${lineNumber}.\nReason: ${libauthErrorMessage}\nFailing statement: ${statement}`; + const resolvedFrame = frame ?? rootFrame(artifact); + const { statement, lineNumber } = getLocationDataForFrame(resolvedFrame, failingInstructionPointer); + const context = formatFrameContext(resolvedFrame, artifact.contractName, lineNumber); + message = `${resolvedFrame.sourceName}:${lineNumber} Error in transaction at input ${inputIndex} ${context}.\nReason: ${libauthErrorMessage}\nFailing statement: ${statement}`; } super(message, bitauthUri); @@ -154,42 +181,44 @@ export class FailedRequireError extends FailedTransactionError { public inputIndex: number, public bitauthUri: string, public libauthErrorMessage?: string, + frame?: ResolvedFrame, + public callStack: CallStackEntry[] = [], ) { - const { statement, lineNumber } = getLocationDataForInstructionPointer(artifact, failingInstructionPointer); + const resolvedFrame = frame ?? rootFrame(artifact); - const baseMessage = `${artifact.contractName}.cash:${lineNumber} Require statement failed at input ${inputIndex} in contract ${artifact.contractName}.cash at line ${lineNumber}`; + const inline = resolveInlineAttribution(artifact, resolvedFrame, requireStatement, 'requires'); + const attributedFrame = inline?.frame ?? resolvedFrame; + const attributedIp = inline?.entry.ip ?? failingInstructionPointer; + + const { statement, lineNumber } = getLocationDataForFrame(attributedFrame, attributedIp); + const context = formatFrameContext(attributedFrame, artifact.contractName, lineNumber); + + const baseMessage = `${attributedFrame.sourceName}:${lineNumber} Require statement failed at input ${inputIndex} ${context}`; const baseMessageWithRequireMessage = `${baseMessage} with the following message: ${requireStatement.message}`; const headline = `${requireStatement.message ? baseMessageWithRequireMessage : baseMessage}.`; // Compiler-injected guards (e.g. the tx.locktime guard) have no user-written source, so the // extracted statement is empty — the require message fully describes the failure on its own. - const fullMessage = statement.trim() ? `${headline}\nFailing statement: ${statement}` : headline; + const statementMessage = statement.trim() ? `${headline}\nFailing statement: ${statement}` : headline; + + // A single-entry call stack adds nothing over the headline, so it is only shown for nested calls + const callStackMessage = callStack.length >= 2 ? `\n${formatCallStack(callStack)}` : ''; - super(fullMessage, bitauthUri); + super(statementMessage + callStackMessage, bitauthUri); } } -const getLocationDataForInstructionPointer = ( - artifact: Artifact, - instructionPointer: number, -): { lineNumber: number, statement: string } => { - const locationData = sourceMapToLocationData(artifact.debug!.sourceMap); - - // We subtract the constructor inputs because these are present in the evaluation (and thus the instruction pointer) - // but they are not present in the source code (and thus the location data) - const modifiedInstructionPointer = instructionPointer - artifact.constructorInputs.length; - - const { location } = locationData[modifiedInstructionPointer]; - - const failingLines = artifact.source.split('\n').slice(location.start.line - 1, location.end.line); - - // Slice off the start and end of the statement's start and end lines to only return the failing part - // Note that we first slice off the end, to avoid shifting the end column index - failingLines[failingLines.length - 1] = failingLines[failingLines.length - 1].slice(0, location.end.column); - failingLines[0] = failingLines[0].slice(location.start.column); - - const statement = failingLines.join('\n'); - const lineNumber = location.start.line; +const formatFrameContext = (frame: ResolvedFrame, contractName: string, lineNumber: number): string => { + if (frame.functionName) { + return `in contract ${contractName}, function ${frame.functionName} (${frame.sourceName}, line ${lineNumber})`; + } - return { statement, lineNumber }; + return `in contract ${contractName}.cash at line ${lineNumber}`; }; + +const formatCallStack = (callStack: CallStackEntry[]): string => callStack + .map(({ functionName, sourceName, line, statement }) => { + const location = functionName ? `${functionName} (${sourceName}:${line})` : `${sourceName}:${line}`; + return ` at ${location} — ${statement}`; + }) + .join('\n'); diff --git a/packages/cashscript/src/SignatureTemplate.ts b/packages/cashscript/src/SignatureTemplate.ts index d8a3f9e7e..f80246efe 100644 --- a/packages/cashscript/src/SignatureTemplate.ts +++ b/packages/cashscript/src/SignatureTemplate.ts @@ -1,35 +1,40 @@ -import { decodePrivateKeyWif, hexToBin, isHex, secp256k1, SigningSerializationFlag } from '@bitauth/libauth'; +import { decodePrivateKeyWif, hexToBin, isHex, secp256k1 } from '@bitauth/libauth'; import { hash256, scriptToBytecode } from '@cashscript/utils'; import { GenerateUnlockingBytecodeOptions, - HashType, + SighashType, SignatureAlgorithm, P2PKHUnlocker, } from './interfaces.js'; -import { createSighashPreimage, publicKeyToP2PKHLockingBytecode } from './utils.js'; +import { createSighashPreimage, publicKeyToP2PKHLockingBytecode, toSigningSerializationType } from './utils.js'; /** * A signature template used to sign CashScript transactions. Wraps a private key together with - * the desired `HashType` and `SignatureAlgorithm`, and is consumed by the `TransactionBuilder` + * the desired `SighashType` and `SignatureAlgorithm`, and is consumed by the `TransactionBuilder` * whenever a `sig` argument is required or when unlocking a P2PKH input. */ export default class SignatureTemplate { /** The raw private key bytes used for signing. */ public privateKey: Uint8Array; + /** The 33-byte compressed public key that corresponds to the template's private key. */ + get publicKey(): Uint8Array { + return secp256k1.derivePublicKeyCompressed(this.privateKey) as Uint8Array; + } + /** * Create a new SignatureTemplate. * * @param signer - A 32-byte private key (Uint8Array), a WIF or hex-encoded private key string, * or any object exposing a `toWIF()` method (e.g. bitcore-lib or bitcoincashjs `ECPair`). - * @param hashtype - Sighash flags to use when signing. Defaults to `SIGHASH_ALL | SIGHASH_UTXOS`. + * @param sighashType - Sighash flags to use when signing. Defaults to `SIGHASH_ALL | SIGHASH_UTXOS`. * @param signatureAlgorithm - The signature algorithm to use. Defaults to * `SignatureAlgorithm.SCHNORR`. */ constructor( signer: Keypair | Uint8Array | string, - private hashtype: HashType = HashType.SIGHASH_ALL | HashType.SIGHASH_UTXOS, - private signatureAlgorithm: SignatureAlgorithm = SignatureAlgorithm.SCHNORR, + public readonly sighashType: SighashType = SighashType.SIGHASH_ALL | SighashType.SIGHASH_UTXOS, + public readonly signatureAlgorithm: SignatureAlgorithm = SignatureAlgorithm.SCHNORR, ) { if (isKeypair(signer)) { const wif = signer.toWIF(); @@ -47,24 +52,22 @@ export default class SignatureTemplate { } /** - * Sign the provided sighash payload and return the signature concatenated with the hashtype + * Sign the provided sighash payload and return the signature concatenated with the sighash type * byte, ready to be used as a transaction signature. * * @param payload - The 32-byte sighash to sign. - * @param bchForkId - Whether to include the BCH fork id flag in the appended hashtype byte. - * Defaults to `true`. - * @returns The signature bytes followed by the hashtype byte. + * @returns The signature bytes followed by the sighash type byte. */ - generateSignature(payload: Uint8Array, bchForkId?: boolean): Uint8Array { + generateSignature(payload: Uint8Array): Uint8Array { const signature = this.signMessageHash(payload); - return Uint8Array.from([...signature, this.getHashType(bchForkId)]); + return Uint8Array.from([...signature, toSigningSerializationType(this.sighashType)]); } /** * Sign a raw 32-byte message hash using the template's private key and signature algorithm. * * @param payload - The 32-byte hash to sign. - * @returns The raw signature bytes (without an appended hashtype byte). + * @returns The raw signature bytes (without an appended sighash type byte). */ signMessageHash(payload: Uint8Array): Uint8Array { const signature = this.signatureAlgorithm === SignatureAlgorithm.SCHNORR @@ -74,32 +77,6 @@ export default class SignatureTemplate { return signature; } - /** - * Get the sighash flags used by this template. - * - * @param bchForkId - Whether to OR in the BCH fork id flag. Defaults to `true`. - * @returns The combined hashtype byte. - */ - getHashType(bchForkId: boolean = true): number { - return bchForkId ? (this.hashtype | SigningSerializationFlag.forkId) : this.hashtype; - } - - /** - * @returns The signature algorithm (ECDSA or Schnorr) used by this template. - */ - getSignatureAlgorithm(): SignatureAlgorithm { - return this.signatureAlgorithm; - } - - /** - * Derive the compressed public key that corresponds to the template's private key. - * - * @returns The 33-byte compressed public key. - */ - getPublicKey(): Uint8Array { - return secp256k1.derivePublicKeyCompressed(this.privateKey) as Uint8Array; - } - /** * Build a P2PKH `Unlocker` for the address derived from this template's private key. The * returned unlocker can be passed directly to `TransactionBuilder.addInput`. @@ -107,17 +84,14 @@ export default class SignatureTemplate { * @returns An unlocker that signs the corresponding P2PKH UTXO. */ unlockP2PKH(): P2PKHUnlocker { - const publicKey = this.getPublicKey(); - const prevOutScript = publicKeyToP2PKHLockingBytecode(publicKey); - const hashtype = this.getHashType(); + const prevOutScript = publicKeyToP2PKHLockingBytecode(this.publicKey); return { - generateLockingBytecode: () => prevOutScript, generateUnlockingBytecode: ({ transaction, sourceOutputs, inputIndex }: GenerateUnlockingBytecodeOptions) => { - const preimage = createSighashPreimage(transaction, sourceOutputs, inputIndex, prevOutScript, hashtype); + const preimage = createSighashPreimage(transaction, sourceOutputs, inputIndex, prevOutScript, this.sighashType); const sighash = hash256(preimage); const signature = this.generateSignature(sighash); - const unlockingBytecode = scriptToBytecode([signature, publicKey]); + const unlockingBytecode = scriptToBytecode([signature, this.publicKey]); return unlockingBytecode; }, template: this, diff --git a/packages/cashscript/src/TransactionBuilder.ts b/packages/cashscript/src/TransactionBuilder.ts index 7e0e54368..daad50f24 100644 --- a/packages/cashscript/src/TransactionBuilder.ts +++ b/packages/cashscript/src/TransactionBuilder.ts @@ -12,7 +12,7 @@ import { Output, TransactionDetails, UnlockableUtxo, - Utxo, + SpendableUtxo, InputOptions, isUnlockableUtxo, isStandardUnlockableUtxo, @@ -34,6 +34,7 @@ import { getOutputSize, validateInput, validateOutput, + validateUnlocker, } from './utils.js'; import { FailedTransactionError, @@ -109,7 +110,7 @@ export class TransactionBuilder { * @returns This builder for chaining. * @throws If the UTXO is invalid. */ - addInput(utxo: Utxo, unlocker: Unlocker, options?: InputOptions): this { + addInput(utxo: SpendableUtxo, unlocker: Unlocker, options?: InputOptions): this { return this.addInputs([utxo], unlocker, options); } @@ -122,7 +123,7 @@ export class TransactionBuilder { * @returns This builder for chaining. * @throws If any UTXO is invalid. */ - addInputs(utxos: Utxo[], unlocker: Unlocker, options?: InputOptions): this; + addInputs(utxos: SpendableUtxo[], unlocker: Unlocker, options?: InputOptions): this; /** * Add multiple UTXOs that each carry their own unlocker. @@ -133,7 +134,7 @@ export class TransactionBuilder { */ addInputs(utxos: UnlockableUtxo[]): this; - addInputs(utxos: Utxo[] | UnlockableUtxo[], unlocker?: Unlocker, options?: InputOptions): this { + addInputs(utxos: SpendableUtxo[] | UnlockableUtxo[], unlocker?: Unlocker, options?: InputOptions): this { utxos.forEach((utxo) => validateInput(utxo, this.changeLocks)); if ( (!unlocker && utxos.some((utxo) => !isUnlockableUtxo(utxo))) @@ -142,6 +143,10 @@ export class TransactionBuilder { throw new Error('Either all UTXOs must have an individual unlocker specified, or no UTXOs must have an individual unlocker specified and a shared unlocker must be provided'); } + utxos.forEach((utxo, i) => ( + validateUnlocker(utxo, unlocker ?? (utxo as UnlockableUtxo).unlocker, this.inputs.length + i, this.provider.network) + )); + if (!unlocker) { this.inputs = this.inputs.concat(utxos as UnlockableUtxo[]); return this; @@ -393,7 +398,7 @@ export class TransactionBuilder { .map((input) => 'contract' in input.unlocker ? input.unlocker.contract.artifact.compiler.version : null) .filter((version) => version !== null); - if (!contractVersions.every((version) => semver.satisfies(version, '>=0.11.0'))) { + if (!contractVersions.every((version) => semver.satisfies(version, '>=0.11.0', { includePrerelease: true }))) { console.warn('For the best debugging experience, please recompile your contract with cashc version 0.11.0 or newer.'); } diff --git a/packages/cashscript/src/debug-frame.ts b/packages/cashscript/src/debug-frame.ts new file mode 100644 index 000000000..234131c93 --- /dev/null +++ b/packages/cashscript/src/debug-frame.ts @@ -0,0 +1,261 @@ +import { + AuthenticationInstruction, + AuthenticationProgramStackFrame, + AuthenticationProgramStateCommon, + binToHex, + encodeAuthenticationInstructions, + hexToBin, +} from '@bitauth/libauth'; +import { + Artifact, + DebugEntry, + DebugFrame, + InlineRange, + LogEntry, + Op, + RequireStatement, + Script, + bytecodeToScript, + parseAndResolveInlineRanges, + sourceMapToLocationData, +} from '@cashscript/utils'; + +export interface ResolvedFrame { + sourceMap: string; + source: string; + sourceName: string; + ipOffset: number; + requires: readonly RequireStatement[]; + logs: readonly LogEntry[]; + inlineRanges?: string; + functionName?: string; +} + +export interface CallStackEntry { + functionName?: string; // absent for the contract's own code + sourceName: string; + line: number; + statement: string; // flattened to a single line for display +} + +export const rootFrame = (artifact: Artifact): ResolvedFrame => ({ + sourceMap: artifact.debug?.sourceMap ?? '', + source: artifact.source, + sourceName: `${artifact.contractName}.cash`, + ipOffset: artifact.constructorInputs.length, + requires: artifact.debug?.requires ?? [], + logs: artifact.debug?.logs ?? [], + inlineRanges: artifact.debug?.inlineRanges, +}); + +export const getActiveBytecode = (instructions: AuthenticationInstruction[]): string => + binToHex(encodeAuthenticationInstructions(instructions)); + +export const resolveFrame = ( + artifact: Artifact, + step: AuthenticationProgramStateCommon, +): ResolvedFrame => resolveFrameByBytecode(artifact, getActiveBytecode(step.instructions)); + +// Only defined frames (id present) execute as standalone VM programs; an inlined callable's +// frame documents a body that only ever runs spliced into another program +const resolveFrameByBytecode = (artifact: Artifact, activeBytecode: string): ResolvedFrame => { + const frame = (artifact.debug?.functions ?? []) + .filter((candidate) => candidate.id !== undefined) + .find((candidate) => candidate.bytecode === activeBytecode); + + return frame ? resolveDebugFrame(artifact, frame) : rootFrame(artifact); +}; + +const resolveDebugFrame = (artifact: Artifact, frame: DebugFrame): ResolvedFrame => ({ + sourceMap: frame.sourceMap, + source: frame.source ?? artifact.source, + sourceName: frame.sourceFile ?? `${artifact.contractName}.cash`, + ipOffset: 0, // function bodies have no constructor-arg prefix; their ips start at 0 + requires: frame.requires, + logs: frame.logs, + inlineRanges: frame.inlineRanges, + functionName: frame.name, +}); + +export interface InlineAttribution { + frame: ResolvedFrame; // the inlined callable, resolved like any other frame + entry: DebugEntry; // the callable's own entry (frame-local ip and line) +} + +export const resolveInlineAttribution = ( + artifact: Artifact, + containerFrame: ResolvedFrame, + entry: DebugEntry, + kind: 'requires' | 'logs', +): InlineAttribution | undefined => { + const attributionStack = resolveInlineAttributionStack(artifact, containerFrame, entry, kind); + return attributionStack[0]; +}; + +// The chain of inlined callables containing the entry, from the innermost to the outermost +const resolveInlineAttributionStack = ( + artifact: Artifact, + containerFrame: ResolvedFrame, + entry: DebugEntry, + kind: 'requires' | 'logs', +): InlineAttribution[] => { + const range = parseAndResolveInlineRanges(containerFrame.inlineRanges, artifact.debug?.functions) + .find((candidate) => entry.ip >= candidate.startIp && entry.ip <= candidate.endIp); + if (!range) return []; + + const frameEntry = findMatchingFrameEntry(containerFrame[kind], range.frame[kind], range, entry); + if (!frameEntry) return []; + + const frame = resolveDebugFrame(artifact, range.frame); + + // The callable may itself contain deeper inlined callables + return [ + ...resolveInlineAttributionStack(artifact, frame, frameEntry, kind), + { frame, entry: frameEntry }, + ]; +}; + +// A log merged from an inlined callable is attributed to the callable's own source +export const attributeLogEntry = ( + artifact: Artifact, + frame: ResolvedFrame, + logEntry: LogEntry, +): { logEntry: LogEntry, sourceName: string } => { + const inline = resolveInlineAttribution(artifact, frame, logEntry, 'logs'); + if (!inline) return { logEntry, sourceName: frame.sourceName }; + + return { + logEntry: { ...logEntry, line: inline.entry.line }, + sourceName: inline.frame.sourceName, + }; +}; + +const findMatchingFrameEntry = ( + containerEntries: readonly DebugEntry[], + frameEntries: readonly DebugEntry[], + range: { startIp: number, endIp: number }, + entry: DebugEntry, +): DebugEntry | undefined => { + const entriesInRange = containerEntries.filter((candidate) => ( + candidate.ip >= range.startIp && candidate.ip <= range.endIp + )); + + const position = entriesInRange.indexOf(entry); + if (position === -1) return undefined; + return frameEntries[position]; +}; + +export const buildCallStack = ( + artifact: Artifact, + failingStep: AuthenticationProgramStateCommon, + failingFrame: ResolvedFrame, + requireStatement: RequireStatement, + failingInstructionPointer: number, +): CallStackEntry[] => { + // These entries represent any called inlined functions at the top of the call stack. These are handled separately, + // because the failing debug step refers to the frameEntry below, so we manually need to handle "deeper" calls + const inlineEntries = resolveInlineAttributionStack(artifact, failingFrame, requireStatement, 'requires') + .map(({ frame, entry }) => toCallStackEntry(frame, entry.ip)); + + // This entry is the actual VM-level failing function + const frameEntry = toCallStackEntry(failingFrame, failingInstructionPointer); + + // These entries are the rest of the callstack, so all the intermediate (defined & inlined) functions that call + // the VM-level failing function + const runtimeCallers = failingStep.controlStack + .filter(isAuthenticationProgramStackFrame) + .reverse() + .flatMap((controlFrame) => { + const callerBytecode = getActiveBytecode(controlFrame.instructions); + const callerFrame = resolveFrameByBytecode(artifact, callerBytecode); + // The control frame stores the ip to resume at, which is one past the OP_INVOKE call site + return expandRuntimeCaller(artifact, callerFrame, bytecodeToScript(hexToBin(callerBytecode)), controlFrame.ip - 1); + }); + + return [...inlineEntries, frameEntry, ...runtimeCallers]; +}; + +const expandRuntimeCaller = ( + artifact: Artifact, + frame: ResolvedFrame, + script: Script, + invokeIp: number, +): CallStackEntry[] => [ + ...resolveInlinedCallerHops(artifact, frame, script, invokeIp), + toCallStackEntry(frame, invokeIp), +]; + +// Every defined function already gets expanded in buildCallStack, so this function exists to make sure any inlined +// callers also get added to the call stack +const resolveInlinedCallerHops = ( + artifact: Artifact, + frame: ResolvedFrame, + script: Script, + invokeIp: number, +): CallStackEntry[] => { + const inlineRange = parseAndResolveInlineRanges(frame.inlineRanges, artifact.debug?.functions) + .find((candidate) => invokeIp >= candidate.startIp && invokeIp <= candidate.endIp); + if (!inlineRange) return []; + + const frameScript = bytecodeToScript(hexToBin(inlineRange.frame.bytecode)); + const frameLocalIp = findMatchingFrameInvokeIp(script, frameScript, inlineRange, invokeIp); + if (frameLocalIp === undefined) return []; + + // The invoke is expanded again within the callable, since deeper inlined callables may wrap it + return expandRuntimeCaller(artifact, resolveDebugFrame(artifact, inlineRange.frame), frameScript, frameLocalIp); +}; + +// The container's n-th OP_INVOKE within the range corresponds to the n-th OP_INVOKE in the inlined +// callable's own bytecode, matching by position gives the invoke's exact frame-local ip. +const findMatchingFrameInvokeIp = ( + containerScript: Script, + frameScript: Script, + range: InlineRange, + invokeIp: number, +): number | undefined => { + const position = containerScript.slice(range.startIp, invokeIp).filter((op) => op === Op.OP_INVOKE).length; + const frameInvokeIps = frameScript.flatMap((op, ip) => (op === Op.OP_INVOKE ? [ip] : [])); + return frameInvokeIps[position]; +}; + +const isAuthenticationProgramStackFrame = ( + item: AuthenticationProgramStackFrame | boolean | number, +): item is AuthenticationProgramStackFrame => typeof item === 'object'; + +const toCallStackEntry = (frame: ResolvedFrame, instructionPointer: number): CallStackEntry => { + const { lineNumber, statement } = getLocationDataForFrame(frame, instructionPointer); + const flattenedStatement = statement.split('\n').map((line) => line.trim()).join(' '); + + return { + functionName: frame.functionName, + sourceName: frame.sourceName, + line: lineNumber, + statement: flattenedStatement, + }; +}; + +export const getLocationDataForFrame = ( + frame: ResolvedFrame, + instructionPointer: number, +): { lineNumber: number, statement: string } => { + const locationData = sourceMapToLocationData(frame.sourceMap); + + // We subtract the frame's ip offset (the constructor-arg prefix for the root frame, 0 for helper + // frames) because those pushes are present in the evaluation (and thus the instruction pointer) but + // not in the source code (and thus the location data). + const modifiedInstructionPointer = instructionPointer - frame.ipOffset; + + const { location } = locationData[modifiedInstructionPointer]; + + const failingLines = frame.source.split('\n').slice(location.start.line - 1, location.end.line); + + // Slice off the start and end of the statement's start and end lines to only return the failing part + // Note that we first slice off the end, to avoid shifting the end column index + failingLines[failingLines.length - 1] = failingLines[failingLines.length - 1].slice(0, location.end.column); + failingLines[0] = failingLines[0].slice(location.start.column); + + const statement = failingLines.join('\n'); + const lineNumber = location.start.line; + + return { statement, lineNumber }; +}; diff --git a/packages/cashscript/src/debugging.ts b/packages/cashscript/src/debugging.ts index d5bf39918..ca1d17dcc 100644 --- a/packages/cashscript/src/debugging.ts +++ b/packages/cashscript/src/debugging.ts @@ -1,37 +1,15 @@ -import { AuthenticationErrorCommon, AuthenticationInstruction, AuthenticationProgramCommon, AuthenticationProgramStateCommon, AuthenticationVirtualMachine, ResolvedTransactionCommon, WalletTemplate, WalletTemplateScriptUnlocking, binToHex, createCompiler, createVirtualMachineBch2023, createVirtualMachineBch2025, createVirtualMachineBch2026, createVirtualMachineBchSpec, decodeAuthenticationInstructions, encodeAuthenticationInstruction, walletTemplateToCompilerConfiguration } from '@bitauth/libauth'; +import { AuthenticationErrorCommon, AuthenticationInstruction, AuthenticationProgramCommon, AuthenticationProgramStateCommon, WalletTemplate, WalletTemplateScriptUnlocking, binToHex, createCompiler, decodeAuthenticationInstructions, encodeAuthenticationInstruction, walletTemplateToCompilerConfiguration } from '@bitauth/libauth'; import { Artifact, LogData, LogEntry, Op, PrimitiveType, StackItem, asmToBytecode, bytecodeToAsm, decodeBool, decodeInt, decodeString } from '@cashscript/utils'; import { findLastIndex, toRegExp } from './utils.js'; import { FailedRequireError, FailedTransactionError, FailedTransactionEvaluationError } from './Errors.js'; +import { attributeLogEntry, buildCallStack, getActiveBytecode, resolveFrame } from './debug-frame.js'; import { getBitauthUri } from './libauth-template/LibauthTemplate.js'; +import { createVirtualMachine, VM } from './libauth-template/utils.js'; import { VmTarget } from './interfaces.js'; export type DebugResult = AuthenticationProgramStateCommon[]; export type DebugResults = Record; -/* eslint-disable @typescript-eslint/indent */ -type VM = AuthenticationVirtualMachine< - ResolvedTransactionCommon, - AuthenticationProgramCommon, - AuthenticationProgramStateCommon ->; -/* eslint-enable @typescript-eslint/indent */ - -const createVirtualMachine = (vmTarget: VmTarget): VM => { - switch (vmTarget) { - case 'BCH_2023_05': - return createVirtualMachineBch2023(); - case 'BCH_2025_05': - return createVirtualMachineBch2025(); - case 'BCH_2026_05': - return createVirtualMachineBch2026(); - case 'BCH_SPEC': - // TODO: This typecast is shitty, but it's hard to fix - return createVirtualMachineBchSpec() as unknown as VM; - default: - throw new Error(`Debugging is not supported for the ${vmTarget} virtual machine.`); - } -}; - // debugs the template, optionally logging the execution data export const debugTemplate = (template: WalletTemplate, artifacts: Artifact[]): DebugResults => { // If a contract has the same name, but a different bytecode, then it is considered a name collision @@ -72,7 +50,11 @@ const debugSingleScenario = ( // P2SH executions have 3 phases, we only want the last one (locking script execution) // https://libauth.org/types/AuthenticationVirtualMachine.html#__type.debug - const lockingScriptDebugResult = fullDebugSteps.slice(findLastIndex(fullDebugSteps, (state) => state.ip === 0)); + // We additionally require an empty control stack: an invoked function body (OP_INVOKE) also starts at + // ip 0, but always with a saved return frame on the control stack. + const lockingScriptDebugResult = fullDebugSteps.slice( + findLastIndex(fullDebugSteps, (state) => state.ip === 0 && state.controlStack.length === 0), + ); // The controlStack determines whether the current debug step is in the executed branch // It also tracks loop / function usage, but for the purpose of determining whether a step was executed, @@ -84,26 +66,30 @@ const debugSingleScenario = ( // P2PKH inputs do not have an artifact, so we skip the console.log handling if (artifact) { - // Try to match each executed debug step to a log entry if it exists. Note that inside loops, - // the same log statement may be executed multiple times in different debug steps - // Also note that multiple log statements may exist for the same ip, so we need to handle all of them + // Try to match each executed debug step to a log entry if it exists. Notes: + // - inside loops, the same log statement may be executed multiple times in different debug steps + // - the same ip may be executed by multiple function frames, so they are matched against the active frame's logs. + // - multiple log statements may exist for the same ip, so we need to handle all of them. const executedLogs = executedDebugSteps .flatMap((debugStep, index) => { - const logEntries = artifact.debug?.logs?.filter((log) => log.ip === debugStep.ip); - if (!logEntries || logEntries.length === 0) return []; + const frame = resolveFrame(artifact, debugStep); + const logEntries = frame.logs.filter((log) => log.ip === debugStep.ip); + if (logEntries.length === 0) return []; const reversedPriorDebugSteps = executedDebugSteps.slice(0, index + 1).reverse(); + const frameBytecode = getActiveBytecode(debugStep.instructions); return logEntries.map((logEntry) => { const decodedLogData = logEntry.data - .map((dataEntry) => decodeLogDataEntry(dataEntry, reversedPriorDebugSteps, vm)); - return { logEntry, decodedLogData }; + .map((dataEntry) => decodeLogDataEntry(dataEntry, reversedPriorDebugSteps, vm, frameBytecode)); + + return { ...attributeLogEntry(artifact, frame, logEntry), decodedLogData }; }); }); - for (const { logEntry, decodedLogData } of executedLogs) { + for (const { logEntry, decodedLogData, sourceName } of executedLogs) { const inputIndex = extractInputIndexFromScenario(scenarioId); - logConsoleLogStatement(logEntry, decodedLogData, artifact.contractName, inputIndex); + logConsoleLogStatement(logEntry, decodedLogData, sourceName, inputIndex); } } @@ -135,19 +121,21 @@ const debugSingleScenario = ( throw new FailedTransactionError(error, getBitauthUri(template)); } - const requireStatement = (artifact.debug?.requires ?? []) - .find((statement) => statement.ip === requireStatementIp); + const frame = resolveFrame(artifact, lastExecutedDebugStep); + const requireStatement = frame.requires.find((statement) => statement.ip === requireStatementIp); if (requireStatement) { + const callStack = buildCallStack(artifact, lastExecutedDebugStep, frame, requireStatement, failingIp); + // Note that we use failingIp here rather than requireStatementIp, see comment above throw new FailedRequireError( - artifact, failingIp, requireStatement, inputIndex, getBitauthUri(template), error, + artifact, failingIp, requireStatement, inputIndex, getBitauthUri(template), error, frame, callStack, ); } // Note that we use failingIp here rather than requireStatementIp, see comment above throw new FailedTransactionEvaluationError( - artifact, failingIp, inputIndex, getBitauthUri(template), error, + artifact, failingIp, inputIndex, getBitauthUri(template), error, frame, ); } @@ -175,17 +163,28 @@ const debugSingleScenario = ( throw new FailedTransactionError(evaluationResult, getBitauthUri(template)); } - const requireStatement = (artifact.debug?.requires ?? []) - .find((message) => message.ip === finalExecutedVerifyIp); + const frame = resolveFrame(artifact, lastExecutedDebugStep); + const requireStatement = frame.requires.find((message) => message.ip === finalExecutedVerifyIp); if (requireStatement) { + const callStack = buildCallStack( + artifact, lastExecutedDebugStep, frame, requireStatement, sourcemapInstructionPointer, + ); + throw new FailedRequireError( - artifact, sourcemapInstructionPointer, requireStatement, inputIndex, getBitauthUri(template), + artifact, + sourcemapInstructionPointer, + requireStatement, + inputIndex, + getBitauthUri(template), + undefined, + frame, + callStack, ); } throw new FailedTransactionEvaluationError( - artifact, sourcemapInstructionPointer, inputIndex, getBitauthUri(template), evaluationResult, + artifact, sourcemapInstructionPointer, inputIndex, getBitauthUri(template), evaluationResult, frame, ); } @@ -236,20 +235,23 @@ const createProgram = (template: WalletTemplate, unlockingScriptId: string, scen const logConsoleLogStatement = ( log: LogEntry, decodedLogData: Array, - contractName: string, + sourceName: string, inputIndex: number, ): void => { - console.log(`[Input #${inputIndex}] ${contractName}.cash:${log.line} ${decodedLogData.join(' ')}`); + console.log(`[Input #${inputIndex}] ${sourceName}:${log.line} ${decodedLogData.join(' ')}`); }; const decodeLogDataEntry = ( dataEntry: LogData, reversedPriorDebugSteps: AuthenticationProgramStateCommon[], vm: VM, + frameBytecode: string, ): string | bigint | boolean => { if (typeof dataEntry === 'string') return dataEntry; - const dataEntryDebugStep = reversedPriorDebugSteps.find((step) => step.ip === dataEntry.ip); + const dataEntryDebugStep = reversedPriorDebugSteps.find( + (step) => step.ip === dataEntry.ip && getActiveBytecode(step.instructions) === frameBytecode, + ); if (!dataEntryDebugStep) { throw new Error(`Should not happen: corresponding data entry debug step not found for entry at ip ${dataEntry.ip}`); diff --git a/packages/cashscript/src/interfaces.ts b/packages/cashscript/src/interfaces.ts index 5841ac9f2..793b02e23 100644 --- a/packages/cashscript/src/interfaces.ts +++ b/packages/cashscript/src/interfaces.ts @@ -10,9 +10,14 @@ export interface Utxo { vout: number; satoshis: bigint; token?: TokenDetails; + lockingBytecode?: string; } -export interface UnlockableUtxo extends Utxo { +export interface SpendableUtxo extends Utxo { + lockingBytecode: string; +} + +export interface UnlockableUtxo extends SpendableUtxo { unlocker: Unlocker; options?: InputOptions; } @@ -40,7 +45,6 @@ export interface GenerateUnlockingBytecodeOptions { } export interface Unlocker { - generateLockingBytecode: () => Uint8Array; generateUnlockingBytecode: (options: GenerateUnlockingBytecodeOptions) => Uint8Array; } @@ -56,7 +60,7 @@ export interface P2PKHUnlocker extends Unlocker { export type StandardUnlocker = ContractUnlocker | P2PKHUnlocker; -export type PlaceholderP2PKHUnlocker = Unlocker & { placeholder: true }; +export type PlaceholderP2PKHUnlocker = Unlocker & { placeholder: true, lockingBytecode: string }; export type ContractFunctionUnlocker = (...args: FunctionArgument[]) => ContractUnlocker; @@ -133,7 +137,7 @@ export enum SignatureAlgorithm { SCHNORR = 0x01, } -export enum HashType { +export enum SighashType { SIGHASH_ALL = 0x01, SIGHASH_NONE = 0x02, SIGHASH_SINGLE = 0x03, diff --git a/packages/cashscript/src/libauth-template/utils.ts b/packages/cashscript/src/libauth-template/utils.ts index df817f3aa..7e3f0bea2 100644 --- a/packages/cashscript/src/libauth-template/utils.ts +++ b/packages/cashscript/src/libauth-template/utils.ts @@ -1,6 +1,23 @@ -import { AbiFunction, AbiInput, Artifact, bytecodeToScript, formatBitAuthScript, sha256 } from '@cashscript/utils'; -import { HashType, LibauthTokenDetails, SignatureAlgorithm, TokenDetails, VmTarget } from '../interfaces.js'; -import { hexToBin, binToHex, isHex, decodeCashAddress, Input, assertSuccess, decodeAuthenticationInstructions, AuthenticationInstructionPush } from '@bitauth/libauth'; +import { AbiFunction, AbiInput, Artifact, formatBitAuthScript, sha256 } from '@cashscript/utils'; +import { LibauthTokenDetails, SighashType, SignatureAlgorithm, TokenDetails, VmTarget } from '../interfaces.js'; +import { + hexToBin, + binToHex, + isHex, + decodeCashAddress, + Input, + assertSuccess, + decodeAuthenticationInstructions, + AuthenticationInstructionPush, + AuthenticationProgramCommon, + AuthenticationProgramStateCommon, + AuthenticationVirtualMachine, + ResolvedTransactionCommon, + createVirtualMachineBch2023, + createVirtualMachineBch2025, + createVirtualMachineBch2026, + createVirtualMachineBchSpec, +} from '@bitauth/libauth'; import { EncodedFunctionArgument } from '../Argument.js'; import { zip } from '../utils.js'; import SignatureTemplate from '../SignatureTemplate.js'; @@ -8,6 +25,30 @@ import { Contract } from '../Contract.js'; export const DEFAULT_VM_TARGET = VmTarget.BCH_2026_05; +/* eslint-disable @typescript-eslint/indent */ +export type VM = AuthenticationVirtualMachine< + ResolvedTransactionCommon, + AuthenticationProgramCommon, + AuthenticationProgramStateCommon +>; +/* eslint-enable @typescript-eslint/indent */ + +export const createVirtualMachine = (vmTarget: VmTarget): VM => { + switch (vmTarget) { + case 'BCH_2023_05': + return createVirtualMachineBch2023(); + case 'BCH_2025_05': + return createVirtualMachineBch2025(); + case 'BCH_2026_05': + return createVirtualMachineBch2026(); + case 'BCH_SPEC': + // TODO: This typecast is shitty, but it's hard to fix + return createVirtualMachineBchSpec() as unknown as VM; + default: + throw new Error(`Evaluation is not supported for the ${vmTarget} virtual machine.`); + } +}; + export const getLockScriptName = (contract: Contract): string => { if (contract.contractType === 'p2s') { return `${contract.artifact.contractName}_${binToHex(sha256(hexToBin(contract.lockingBytecode)))}_lock`; @@ -32,23 +73,23 @@ export const getSignatureAlgorithmName = (signatureAlgorithm: SignatureAlgorithm return signatureAlgorithmNames[signatureAlgorithm]; }; -export const getHashTypeName = (hashType: HashType): string => { - const hashtypeNames = { - [HashType.SIGHASH_ALL]: 'all_outputs', - [HashType.SIGHASH_ALL | HashType.SIGHASH_ANYONECANPAY]: 'all_outputs_single_input', - [HashType.SIGHASH_ALL | HashType.SIGHASH_UTXOS]: 'all_outputs_all_utxos', - [HashType.SIGHASH_ALL | HashType.SIGHASH_ANYONECANPAY | HashType.SIGHASH_UTXOS]: 'all_outputs_single_input_INVALID_all_utxos', - [HashType.SIGHASH_SINGLE]: 'corresponding_output', - [HashType.SIGHASH_SINGLE | HashType.SIGHASH_ANYONECANPAY]: 'corresponding_output_single_input', - [HashType.SIGHASH_SINGLE | HashType.SIGHASH_UTXOS]: 'corresponding_output_all_utxos', - [HashType.SIGHASH_SINGLE | HashType.SIGHASH_ANYONECANPAY | HashType.SIGHASH_UTXOS]: 'corresponding_output_single_input_INVALID_all_utxos', - [HashType.SIGHASH_NONE]: 'no_outputs', - [HashType.SIGHASH_NONE | HashType.SIGHASH_ANYONECANPAY]: 'no_outputs_single_input', - [HashType.SIGHASH_NONE | HashType.SIGHASH_UTXOS]: 'no_outputs_all_utxos', - [HashType.SIGHASH_NONE | HashType.SIGHASH_ANYONECANPAY | HashType.SIGHASH_UTXOS]: 'no_outputs_single_input_INVALID_all_utxos', +export const getSighashTypeName = (sighashType: SighashType): string => { + const sighashTypeNames = { + [SighashType.SIGHASH_ALL]: 'all_outputs', + [SighashType.SIGHASH_ALL | SighashType.SIGHASH_ANYONECANPAY]: 'all_outputs_single_input', + [SighashType.SIGHASH_ALL | SighashType.SIGHASH_UTXOS]: 'all_outputs_all_utxos', + [SighashType.SIGHASH_ALL | SighashType.SIGHASH_ANYONECANPAY | SighashType.SIGHASH_UTXOS]: 'all_outputs_single_input_INVALID_all_utxos', + [SighashType.SIGHASH_SINGLE]: 'corresponding_output', + [SighashType.SIGHASH_SINGLE | SighashType.SIGHASH_ANYONECANPAY]: 'corresponding_output_single_input', + [SighashType.SIGHASH_SINGLE | SighashType.SIGHASH_UTXOS]: 'corresponding_output_all_utxos', + [SighashType.SIGHASH_SINGLE | SighashType.SIGHASH_ANYONECANPAY | SighashType.SIGHASH_UTXOS]: 'corresponding_output_single_input_INVALID_all_utxos', + [SighashType.SIGHASH_NONE]: 'no_outputs', + [SighashType.SIGHASH_NONE | SighashType.SIGHASH_ANYONECANPAY]: 'no_outputs_single_input', + [SighashType.SIGHASH_NONE | SighashType.SIGHASH_UTXOS]: 'no_outputs_all_utxos', + [SighashType.SIGHASH_NONE | SighashType.SIGHASH_ANYONECANPAY | SighashType.SIGHASH_UTXOS]: 'no_outputs_single_input_INVALID_all_utxos', }; - return hashtypeNames[hashType]; + return sighashTypeNames[sighashType]; }; export const addHexPrefixExceptEmpty = (value: string): string => { @@ -63,9 +104,9 @@ export const formatParametersForDebugging = (types: readonly AbiInput[], args: E return typesAndArguments.map(([input, arg]) => { if (arg instanceof SignatureTemplate) { - const signatureAlgorithmName = getSignatureAlgorithmName(arg.getSignatureAlgorithm()); - const hashtypeName = getHashTypeName(arg.getHashType(false)); - return `<${input.name}.${signatureAlgorithmName}.${hashtypeName}> // ${input.type}`; + const signatureAlgorithmName = getSignatureAlgorithmName(arg.signatureAlgorithm); + const sighashTypeName = getSighashTypeName(arg.sighashType); + return `<${input.name}.${signatureAlgorithmName}.${sighashTypeName}> // ${input.type}`; } const typeStr = input.type === 'bytes' ? `bytes${arg.length}` : input.type; @@ -77,6 +118,7 @@ export const formatParametersForDebugging = (types: readonly AbiInput[], args: E }; export const formatBytecodeForDebugging = (artifact: Artifact): string => { + // Old artifacts carry no debug information, so we render the raw bytecode in execution order if (!artifact.debug) { return artifact.bytecode .split(' ') @@ -84,12 +126,7 @@ export const formatBytecodeForDebugging = (artifact: Artifact): string => { .join('\n'); } - return formatBitAuthScript( - bytecodeToScript(hexToBin(artifact.debug.bytecode)), - artifact.debug.sourceMap, - artifact.source, - artifact.debug.sourceTags, - ); + return formatBitAuthScript(artifact.debug, artifact.source); }; export const serialiseTokenDetails = ( diff --git a/packages/cashscript/src/network/ElectrumNetworkProvider.ts b/packages/cashscript/src/network/ElectrumNetworkProvider.ts index 432e014ff..f3e06a0a5 100644 --- a/packages/cashscript/src/network/ElectrumNetworkProvider.ts +++ b/packages/cashscript/src/network/ElectrumNetworkProvider.ts @@ -5,7 +5,7 @@ import { type RequestResponse, type ElectrumClientEvents, } from '@electrum-cash/network'; -import { Utxo, Network } from '../interfaces.js'; +import { SpendableUtxo, Network } from '../interfaces.js'; import NetworkProvider from './NetworkProvider.js'; import { addressToLockScript } from '../utils.js'; import { @@ -75,12 +75,12 @@ export default class ElectrumNetworkProvider implements NetworkProvider { } } - async getUtxos(address: string): Promise { + async getUtxos(address: string): Promise { const lockingBytecode = addressToLockScript(address); return this.getUtxosForLockingBytecode(lockingBytecode); } - async getUtxosForLockingBytecode(lockingBytecode: Uint8Array | string): Promise { + async getUtxosForLockingBytecode(lockingBytecode: Uint8Array | string): Promise { if (typeof lockingBytecode === 'string' && !isHex(lockingBytecode)) { throw new Error(`Invalid locking bytecode: ${lockingBytecode} is not a valid hex string`); } @@ -94,6 +94,7 @@ export default class ElectrumNetworkProvider implements NetworkProvider { txid: utxo.tx_hash, vout: utxo.tx_pos, satoshis: BigInt(utxo.value), + lockingBytecode: binToHex(lockingBytecodeBin), token: utxo.token_data ? { ...utxo.token_data, amount: BigInt(utxo.token_data.amount), diff --git a/packages/cashscript/src/network/MockNetworkProvider.ts b/packages/cashscript/src/network/MockNetworkProvider.ts index b7209db77..6fe888e54 100644 --- a/packages/cashscript/src/network/MockNetworkProvider.ts +++ b/packages/cashscript/src/network/MockNetworkProvider.ts @@ -1,9 +1,9 @@ -import { binToHex, decodeTransactionUnsafe, hexToBin, isHex } from '@bitauth/libauth'; +import { binToHex, decodeTransactionUnsafe, hexToBin, isHex, Transaction as LibauthTransaction } from '@bitauth/libauth'; import { sha256 } from '@cashscript/utils'; -import { Utxo, Network, VmTarget } from '../interfaces.js'; +import { SpendableUtxo, Utxo, Network, VmTarget } from '../interfaces.js'; import NetworkProvider from './NetworkProvider.js'; -import { addressToLockScript, libauthTokenDetailsToCashScriptTokenDetails } from '../utils.js'; -import { DEFAULT_VM_TARGET } from '../libauth-template/utils.js'; +import { addressToLockScript, cashScriptOutputToLibauthOutput, libauthTokenDetailsToCashScriptTokenDetails } from '../utils.js'; +import { createVirtualMachine, DEFAULT_VM_TARGET } from '../libauth-template/utils.js'; /** * Options accepted by the `MockNetworkProvider` constructor. @@ -15,7 +15,14 @@ export interface MockNetworkProviderOptions { * keep the UTXO set static. */ updateUtxoSet?: boolean; - /** The BCH VM target used for local debugging. Defaults to the current stable VM. */ + /** + * When `true` (default), broadcasting a transaction via `sendRawTransaction` evaluates it + * against the BCH VM using the *actual* locking bytecode of the spent UTXOs (like a real node + * would), rejecting invalid transactions. Requires `updateUtxoSet` to be enabled, since spent + * UTXOs are only looked up when the UTXO set is tracked. + */ + validateTransactions?: boolean; + /** The BCH VM target used for local debugging and transaction validation. Defaults to the current stable VM. */ vmTarget?: VmTarget; } @@ -26,7 +33,7 @@ export interface MockNetworkProviderOptions { */ export default class MockNetworkProvider implements NetworkProvider { // we use lockingBytecode hex as the key for utxoMap to make cash addresses and token addresses interchangeable - private utxoSet: Array<[string, Utxo]> = []; + private utxoSet: Array<[string, SpendableUtxo]> = []; private transactionMap: Record = {}; private blockHeight: number = 133700; public network: Network = Network.MOCKNET; @@ -40,16 +47,16 @@ export default class MockNetworkProvider implements NetworkProvider { * `TransactionBuilder.debug`. */ constructor(options?: Partial) { - this.options = { updateUtxoSet: true, ...options }; + this.options = { updateUtxoSet: true, validateTransactions: true, ...options }; this.vmTarget = this.options.vmTarget ?? DEFAULT_VM_TARGET; } - async getUtxos(address: string): Promise { + async getUtxos(address: string): Promise { const addressLockingBytecode = addressToLockScript(address); return this.getUtxosForLockingBytecode(addressLockingBytecode); } - async getUtxosForLockingBytecode(lockingBytecode: Uint8Array | string): Promise { + async getUtxosForLockingBytecode(lockingBytecode: Uint8Array | string): Promise { const lockingBytecodeHex = typeof lockingBytecode === 'string' ? lockingBytecode : binToHex(lockingBytecode); return this.utxoSet.filter(([key]) => key === lockingBytecodeHex).map(([, utxo]) => utxo); } @@ -77,28 +84,25 @@ export default class MockNetworkProvider implements NetworkProvider { const txid = binToHex(sha256(sha256(transactionBin)).reverse()); if (this.options.updateUtxoSet && this.transactionMap[txid]) { - throw new Error(`Transaction with txid ${txid} was already submitted`); + console.warn(`Transaction with txid ${txid} was already submitted`); + return txid; } - this.transactionMap[txid] = txHex; - - // If updateUtxoSet is false, we don't need to update the utxo set, and just return the txid - if (!this.options.updateUtxoSet) return txid; + // If updateUtxoSet is false, we don't track spent UTXOs, so we cannot validate the transaction either + if (!this.options.updateUtxoSet) { + this.transactionMap[txid] = txHex; + return txid; + } const decodedTransaction = decodeTransactionUnsafe(transactionBin); + const spentUtxoEntries = this.findSpentUtxoEntries(decodedTransaction, txid); - decodedTransaction.inputs.forEach((input) => { - const utxoIndex = this.utxoSet.findIndex( - ([, utxo]) => utxo.txid === binToHex(input.outpointTransactionHash) && utxo.vout === input.outpointIndex, - ); - - // TODO: we should check what error a BCHN node throws, so we can throw the same error here - if (utxoIndex === -1) { - throw new Error(`UTXO not found for input ${input.outpointIndex} of transaction ${txid}`); - } + if (this.options.validateTransactions) { + this.validateTransaction(decodedTransaction, spentUtxoEntries); + } - this.utxoSet.splice(utxoIndex, 1); - }); + this.transactionMap[txid] = txHex; + this.utxoSet = this.utxoSet.filter((entry) => !spentUtxoEntries.includes(entry)); decodedTransaction.outputs.forEach((output, vout) => { this.addUtxo(binToHex(output.lockingBytecode), { @@ -112,6 +116,39 @@ export default class MockNetworkProvider implements NetworkProvider { return txid; } + private findSpentUtxoEntries(transaction: LibauthTransaction, txid: string): Array<[string, SpendableUtxo]> { + const remainingUtxoEntries = [...this.utxoSet]; + + return transaction.inputs.map((input) => { + const utxoIndex = remainingUtxoEntries.findIndex( + ([, utxo]) => utxo.txid === binToHex(input.outpointTransactionHash) && utxo.vout === input.outpointIndex, + ); + + // TODO: we should check what error a BCHN node throws, so we can throw the same error here + if (utxoIndex === -1) { + throw new Error(`UTXO not found for input ${input.outpointIndex} of transaction ${txid}`); + } + + return remainingUtxoEntries.splice(utxoIndex, 1)[0]; + }); + } + + // Evaluates the transaction against the BCH VM using the spent UTXOs (like a real node would) + private validateTransaction(transaction: LibauthTransaction, spentUtxoEntries: Array<[string, SpendableUtxo]>): void { + const sourceOutputs = spentUtxoEntries.map(([lockingBytecode, utxo]) => cashScriptOutputToLibauthOutput({ + to: hexToBin(lockingBytecode), + amount: utxo.satoshis, + token: utxo.token, + })); + + const vm = createVirtualMachine(this.vmTarget); + const verificationResult = vm.verify({ transaction, sourceOutputs }); + + if (verificationResult !== true) { + throw new Error(verificationResult); + } + } + // Note: the user can technically add the same UTXO multiple times (txid + vout), to the same or different addresses // but we don't check for this in the sendRawTransaction method. We might want to prevent duplicates from being added // in the first place. @@ -121,14 +158,15 @@ export default class MockNetworkProvider implements NetworkProvider { * * @param addressOrLockingBytecode - Either a CashAddress or a hex-encoded locking bytecode. * @param utxo - The UTXO to make spendable. - * @returns The added UTXO. + * @returns The added UTXO, annotated with the locking bytecode it was added under. */ - addUtxo(addressOrLockingBytecode: string, utxo: Utxo): Utxo { + addUtxo(addressOrLockingBytecode: string, utxo: Utxo): SpendableUtxo { const lockingBytecode = isHex(addressOrLockingBytecode) ? addressOrLockingBytecode : binToHex(addressToLockScript(addressOrLockingBytecode)); - this.utxoSet.push([lockingBytecode, utxo]); - return utxo; + const annotatedUtxo = { ...utxo, lockingBytecode }; + this.utxoSet.push([lockingBytecode, annotatedUtxo]); + return annotatedUtxo; } /** diff --git a/packages/cashscript/src/network/NetworkProvider.ts b/packages/cashscript/src/network/NetworkProvider.ts index e966982b4..62e95145e 100644 --- a/packages/cashscript/src/network/NetworkProvider.ts +++ b/packages/cashscript/src/network/NetworkProvider.ts @@ -1,4 +1,4 @@ -import { Utxo, Network } from '../interfaces.js'; +import { SpendableUtxo, Network } from '../interfaces.js'; export default interface NetworkProvider { /** @@ -11,14 +11,14 @@ export default interface NetworkProvider { * @param address The CashAddress for which we wish to retrieve UTXOs. * @returns List of UTXOs spendable by the provided address. */ - getUtxos(address: string): Promise; + getUtxos(address: string): Promise; /** * Retrieve all UTXOs (confirmed and unconfirmed) for a given locking bytecode. * @param lockingBytecode The locking bytecode for which we wish to retrieve UTXOs. * @returns List of UTXOs spendable by the provided locking bytecode. */ - getUtxosForLockingBytecode(lockingBytecode: Uint8Array | string): Promise; + getUtxosForLockingBytecode(lockingBytecode: Uint8Array | string): Promise; /** * @returns The current block height. diff --git a/packages/cashscript/src/transaction-utils.ts b/packages/cashscript/src/transaction-utils.ts index 9f4c7741e..1373ef2c6 100644 --- a/packages/cashscript/src/transaction-utils.ts +++ b/packages/cashscript/src/transaction-utils.ts @@ -5,8 +5,8 @@ import { isFungibleTokenUtxo, isNonTokenUtxo } from './utils.js'; * Result of `gatherBchUtxos` and `gatherFungibleTokenUtxos`: the selected UTXOs and the total * amount they cover (satoshis for BCH, token amount for fungible tokens). */ -export interface GatherUtxosResult { - utxos: Utxo[]; +export interface GatherUtxosResult { + utxos: U[]; totalAmount: bigint; } @@ -19,12 +19,12 @@ export interface GatherUtxosResult { * @returns The selected UTXOs and their cumulative satoshi amount. * @throws If the available non-token UTXOs do not cover the requested amount. */ -export function gatherBchUtxos(utxos: Utxo[], amount: bigint): GatherUtxosResult { +export function gatherBchUtxos(utxos: U[], amount: bigint): GatherUtxosResult { const sortedBchUtxos = utxos .filter(isNonTokenUtxo) .toSorted((a, b) => Number(b.satoshis - a.satoshis)); - const targetUtxos: Utxo[] = []; + const targetUtxos: U[] = []; let total = 0n; for (const utxo of sortedBchUtxos) { @@ -50,12 +50,14 @@ export function gatherBchUtxos(utxos: Utxo[], amount: bigint): GatherUtxosResult * @returns The selected UTXOs and their cumulative token amount. * @throws If the available fungible token UTXOs do not cover the requested amount. */ -export function gatherFungibleTokenUtxos(utxos: Utxo[], tokenCategory: string, amount: bigint): GatherUtxosResult { +export function gatherFungibleTokenUtxos( + utxos: U[], tokenCategory: string, amount: bigint, +): GatherUtxosResult { const sortedTokenUtxos = utxos .filter((utxo) => isFungibleTokenUtxo(utxo) && utxo.token!.category === tokenCategory) .toSorted((a, b) => Number(b.token!.amount - a.token!.amount)); - const targetUtxos: Utxo[] = []; + const targetUtxos: U[] = []; let total = 0n; for (const utxo of sortedTokenUtxos) { diff --git a/packages/cashscript/src/utils.ts b/packages/cashscript/src/utils.ts index 743678938..e8a2744bf 100644 --- a/packages/cashscript/src/utils.ts +++ b/packages/cashscript/src/utils.ts @@ -14,6 +14,7 @@ import { bigIntToCompactUint, NonFungibleTokenCapability, bigIntToVmNumber, + SigningSerializationFlag, } from '@bitauth/libauth'; import { encodeInt, @@ -35,13 +36,21 @@ import { UnlockableUtxo, LibauthTokenDetails, ContractType, + SighashType, + SpendableUtxo, + Unlocker, + isContractUnlocker, + isP2PKHUnlocker, + isPlaceholderUnlocker, } from './interfaces.js'; import { VERSION_SIZE, LOCKTIME_SIZE } from './constants.js'; import { OutputSatoshisTooSmallError, OutputTokenAmountTooSmallError, TokensToNonTokenAddressError, + InputMissingLockingBytecodeError, UndefinedInputError, + UnlockerLockingBytecodeMismatchError, OutputAddressNetworkMismatchError, OutputTokenCategoryInvalidError, OutputTokenCommitmentInvalidError, @@ -57,9 +66,57 @@ export function validateInput(utxo: Utxo, changeLocks: Record): throw new UndefinedInputError(); } + if (!utxo.lockingBytecode) { + throw new InputMissingLockingBytecodeError(); + } + validateChangeLocks(changeLocks, utxo.token?.category); } +// A UTXO/unlocker mismatch would be rejected by the network, so we catch it locally with a descriptive error +export function validateUnlocker(utxo: SpendableUtxo, unlocker: Unlocker, inputIndex: number, network: Network): void { + const unlockerLockingBytecode = getUnlockerLockingBytecode(unlocker); + if (unlockerLockingBytecode === undefined || utxo.lockingBytecode === unlockerLockingBytecode) return; + + throw new UnlockerLockingBytecodeMismatchError( + inputIndex, + formatLockingBytecode(utxo.lockingBytecode, network), + formatLockingBytecode(unlockerLockingBytecode, network), + describeUnlocker(unlocker), + ); +} + +// The locking bytecode this unlocker is able to unlock +function getUnlockerLockingBytecode(unlocker: Unlocker): string | undefined { + if (isContractUnlocker(unlocker)) return unlocker.contract.lockingBytecode; + if (isP2PKHUnlocker(unlocker)) return binToHex(publicKeyToP2PKHLockingBytecode(unlocker.template.publicKey)); + if (isPlaceholderUnlocker(unlocker)) return unlocker.lockingBytecode; + return undefined; +} + +function describeUnlocker(unlocker: Unlocker): string { + if (isContractUnlocker(unlocker)) { + return `unlocker for function "${unlocker.abiFunction.name}" of contract "${unlocker.contract.name}"`; + } + + if (isP2PKHUnlocker(unlocker)) { + return `P2PKH unlocker for public key ${binToHex(unlocker.template.publicKey)}`; + } + + if (isPlaceholderUnlocker(unlocker)) { + return 'placeholder P2PKH unlocker'; + } + + return 'custom unlocker'; +} + +function formatLockingBytecode(lockingBytecode: string, network: Network): string { + const prefix = getNetworkPrefix(network); + const result = lockingBytecodeToCashAddress({ bytecode: hexToBin(lockingBytecode), prefix }); + if (typeof result === 'string') return `locking bytecode ${lockingBytecode}`; + return `address ${result.address} (locking bytecode ${lockingBytecode})`; +} + export function validateOutput(output: Output, network: Network, changeLocks: Record): void { validateChangeLocks(changeLocks, output.token?.category); @@ -177,7 +234,7 @@ export function generateLibauthSourceOutputs(inputs: UnlockableUtxo[]): LibauthO const sourceOutputs = inputs.map((input) => { const sourceOutput = { amount: input.satoshis, - to: input.unlocker.generateLockingBytecode(), + to: hexToBin(input.lockingBytecode), token: input.token, }; @@ -265,15 +322,20 @@ function toBin(output: string): Uint8Array { return encode(data); } +// BCH consensus requires the fork id flag on every signing serialization +export function toSigningSerializationType(sighashType: SighashType): number { + return sighashType | SigningSerializationFlag.forkId; +} + export function createSighashPreimage( transaction: Transaction, sourceOutputs: LibauthOutput[], inputIndex: number, coveredBytecode: Uint8Array, - hashtype: number, + sighashType: SighashType, ): Uint8Array { const context = { inputIndex, sourceOutputs, transaction }; - const signingSerializationType = new Uint8Array([hashtype]); + const signingSerializationType = new Uint8Array([toSigningSerializationType(sighashType)]); const sighashPreimage = generateSigningSerializationBch(context, { coveredBytecode, signingSerializationType }); @@ -364,14 +426,12 @@ const randomInt = (): bigint => BigInt(Math.floor(Math.random() * 10000)); * @param defaults - Values that override the randomly generated fields. * @returns A synthetic UTXO with random `txid`, `vout`, and `satoshis`. */ -export const randomUtxo = (defaults?: Partial): Utxo => ({ - ...{ - txid: binToHex(sha256(bigIntToVmNumber(randomInt()))), - vout: Math.floor(Math.random() * 10), - satoshis: 100_000n + randomInt(), - }, +export const randomUtxo = >(defaults?: T): Utxo & T => ({ + txid: binToHex(sha256(bigIntToVmNumber(randomInt()))), + vout: Math.floor(Math.random() * 10), + satoshis: 100_000n + randomInt(), ...defaults, -}); +} as Utxo & T); /** * Generate random fungible `TokenDetails` for use in tests and examples. Fields can be overridden diff --git a/packages/cashscript/src/walletconnect-utils.ts b/packages/cashscript/src/walletconnect-utils.ts index d4c7cd3a2..140ab3427 100644 --- a/packages/cashscript/src/walletconnect-utils.ts +++ b/packages/cashscript/src/walletconnect-utils.ts @@ -1,6 +1,6 @@ import { type LibauthOutput, isContractUnlocker, type PlaceholderP2PKHUnlocker, type UnlockableUtxo } from './interfaces.js'; import { type AbiFunction, type Artifact } from '@cashscript/utils'; -import { cashAddressToLockingBytecode, hexToBin, type Input, type TransactionCommon } from '@bitauth/libauth'; +import { binToHex, cashAddressToLockingBytecode, hexToBin, type Input, type TransactionCommon } from '@bitauth/libauth'; // Wallet Connect interfaces according to the spec // see https://github.com/mainnet-pat/wc2-bch-bcr @@ -80,10 +80,9 @@ export const placeholderP2PKHUnlocker = (userAddress: string): PlaceholderP2PKHU throw new Error(`Invalid address: ${decodeAddressResult}`); } - const lockingBytecode = decodeAddressResult.bytecode; return { - generateLockingBytecode: () => lockingBytecode, generateUnlockingBytecode: () => Uint8Array.from(Array(0)), placeholder: true, + lockingBytecode: binToHex(decodeAddressResult.bytecode), }; }; diff --git a/packages/cashscript/test/Contract.test.ts b/packages/cashscript/test/Contract.test.ts index 0125f070b..bd9c39b41 100644 --- a/packages/cashscript/test/Contract.test.ts +++ b/packages/cashscript/test/Contract.test.ts @@ -182,8 +182,8 @@ describe('Contract', () => { }); it('generates correct locking bytecode', () => { - expect(instance.unlock.spend(alicePub, new SignatureTemplate(alicePriv)).generateLockingBytecode()) - .toEqual(hexToBin('aa2034d9ffce86b4d136ca74e9db6f6433d3548966a6be064052e728a4c1d16aa3a587')); + expect(instance.lockingBytecode) + .toEqual('aa2034d9ffce86b4d136ca74e9db6f6433d3548966a6be064052e728a4c1d16aa3a587'); }); it('can spend from a p2s contract', async () => { @@ -205,6 +205,7 @@ describe('Contract', () => { txid: 'e5ac1aa9730d7514b541895e466c987327a4b0c57fcbbd50fc73788f5c0f65d9', vout: 4, satoshis: 102745n, + lockingBytecode: instance.lockingBytecode, }; const unlocker = instance.unlock.spend(alicePub, new SignatureTemplate(alicePriv)); diff --git a/packages/cashscript/test/SignatureTemplate.test.ts b/packages/cashscript/test/SignatureTemplate.test.ts index 138d45118..24fd070bf 100644 --- a/packages/cashscript/test/SignatureTemplate.test.ts +++ b/packages/cashscript/test/SignatureTemplate.test.ts @@ -1,5 +1,5 @@ import { generateLibauthSourceOutputs } from 'cashscript/dist/utils.js'; -import { HashType, MockNetworkProvider, SignatureAlgorithm, SignatureTemplate, TransactionBuilder } from '../src/index.js'; +import { MockNetworkProvider, SighashType, SignatureAlgorithm, SignatureTemplate, TransactionBuilder } from '../src/index.js'; import { aliceAddress, alicePriv, alicePub, aliceWif } from './fixture/vars.js'; import { binToHex, decodeTransactionUnsafe, hexToBin } from '@bitauth/libauth'; @@ -33,17 +33,11 @@ describe('SignatureTemplate', () => { expect(signature).toEqual(hexToBin('3045022100fa1d6a159a124e99479f78152422d55ff3c16f7fac5ae47fa291907f8f47613f02200d6c906f667b3712860b6f5a1f296ecb7dcd44da83c6a1eb45869b61c6b8dadb61')); }); - it('should append the correct hash type when fork ID is true', () => { - const signatureTemplate = new SignatureTemplate(alicePriv, HashType.SIGHASH_SINGLE); - const signature = signatureTemplate.generateSignature(hexToBin('0000000000000000000000'), true); + it('should append the configured sighash type, always including the BCH fork ID', () => { + const signatureTemplate = new SignatureTemplate(alicePriv, SighashType.SIGHASH_SINGLE); + const signature = signatureTemplate.generateSignature(hexToBin('0000000000000000000000')); expect(signature).toEqual(hexToBin('bcac180e17de108003cce026708bd2af54b860dad2626cee157f4ed5abd993b9085d615015f905978adc51e8878226280ddd27d899f086519c0978e53332d79943')); }); - - it('should append the correct hash type when fork ID is false', () => { - const signatureTemplate = new SignatureTemplate(alicePriv, HashType.SIGHASH_SINGLE); - const signature = signatureTemplate.generateSignature(hexToBin('0000000000000000000000'), false); - expect(signature).toEqual(hexToBin('bcac180e17de108003cce026708bd2af54b860dad2626cee157f4ed5abd993b9085d615015f905978adc51e8878226280ddd27d899f086519c0978e53332d79903')); - }); }); describe('signMessageHash', () => { @@ -62,10 +56,10 @@ describe('SignatureTemplate', () => { }); }); - describe('getPublicKey', () => { + describe('publicKey', () => { it('should generate a correct public key', () => { const signatureTemplate = new SignatureTemplate(alicePriv); - expect(signatureTemplate.getPublicKey()).toEqual(alicePub); + expect(signatureTemplate.publicKey).toEqual(alicePub); }); }); @@ -75,13 +69,12 @@ describe('SignatureTemplate', () => { txid: '043ec3826702c45460a6dd6b13e343a8f1bc06bc047b63ca484f791dfdfd92c2', vout: 8, satoshis: 109759n, + lockingBytecode: '76a914512dbb2c8c02efbac8d92431aa0ac33f6b0bf97088ac', }; const signatureTemplate = new SignatureTemplate(alicePriv); const unlocker = signatureTemplate.unlockP2PKH(); - expect(unlocker.generateLockingBytecode()).toEqual(hexToBin('76a914512dbb2c8c02efbac8d92431aa0ac33f6b0bf97088ac')); - const transactionBuilder = new TransactionBuilder({ provider: new MockNetworkProvider() }) .addInput(utxo, unlocker) .addOutput({ to: aliceAddress, amount: 1000n }); diff --git a/packages/cashscript/test/TransactionBuilder.test.ts b/packages/cashscript/test/TransactionBuilder.test.ts index bb81be5b7..c07c4e5a3 100644 --- a/packages/cashscript/test/TransactionBuilder.test.ts +++ b/packages/cashscript/test/TransactionBuilder.test.ts @@ -14,7 +14,7 @@ import { carolTokenAddress, alicePriv, } from './fixture/vars.js'; -import { Network } from '../src/interfaces.js'; +import { Network, SpendableUtxo, Utxo } from '../src/interfaces.js'; import { utxoComparator, calculateDust, randomUtxo, randomToken, isNonTokenUtxo, isFungibleTokenUtxo } from '../src/utils.js'; import p2pkhArtifact from './fixture/p2pkh.artifact.js'; import twtArtifact from './fixture/transfer_with_timeout.artifact.js'; @@ -22,9 +22,11 @@ import { TransactionBuilder } from '../src/TransactionBuilder.js'; import { addUtxo, getTxOutputs } from './test-util.js'; import { generateWcTransactionObjectFixture } from './fixture/walletconnect/fixtures.js'; import { + InputMissingLockingBytecodeError, OutputBchChangeLockedError, OutputTokenChangeLockedError, TokensToNonTokenAddressError, + UnlockerLockingBytecodeMismatchError, } from '../src/Errors.js'; import { FailingMockNetworkProvider } from '../src/network/MockNetworkProvider.js'; @@ -223,6 +225,45 @@ describe('Transaction Builder', () => { }); }); + describe('UTXO / unlocker mismatch checks', () => { + it('should fail when spending a contract UTXO with an unlocker of a different contract', async () => { + const p2pkhUtxos = (await p2pkhInstance.getUtxos()).filter(isNonTokenUtxo); + + expect(() => ( + new TransactionBuilder({ provider }) + .addInput(p2pkhUtxos[0], twtInstance.unlock.transfer(new SignatureTemplate(carolPriv))) + )).toThrow(UnlockerLockingBytecodeMismatchError); + }); + + it('should fail when spending a contract UTXO with an unlocker of the same contract with a different address type', async () => { + const p2sh20Instance = new Contract(p2pkhArtifact, [carolPkh], { provider, contractType: 'p2sh20' }); + const p2pkhUtxos = (await p2pkhInstance.getUtxos()).filter(isNonTokenUtxo); + + expect(() => ( + new TransactionBuilder({ provider }) + .addInput(p2pkhUtxos[0], p2sh20Instance.unlock.spend(carolPub, new SignatureTemplate(carolPriv))) + )).toThrow(UnlockerLockingBytecodeMismatchError); + }); + + it('should fail when spending a P2PKH UTXO with a SignatureTemplate for a different key', async () => { + const aliceUtxos = (await provider.getUtxos(aliceAddress)).filter(isNonTokenUtxo); + + expect(() => ( + new TransactionBuilder({ provider }) + .addInput(aliceUtxos[0], new SignatureTemplate(bobPriv).unlockP2PKH()) + )).toThrow(UnlockerLockingBytecodeMismatchError); + }); + + it('should fail when adding a UTXO without a lockingBytecode', async () => { + const utxoWithoutLockingBytecode = randomUtxo() as SpendableUtxo; + + expect(() => ( + new TransactionBuilder({ provider }) + .addInput(utxoWithoutLockingBytecode, new SignatureTemplate(bobPriv).unlockP2PKH()) + )).toThrow(InputMissingLockingBytecodeError); + }); + }); + describe('test TransactionBuilder.generateWcTransactionObject', () => { it('should match the generateWcTransactionObjectFixture ', async () => { const p2pkhUtxos = (await p2pkhInstance.getUtxos()).filter(isNonTokenUtxo).sort(utxoComparator).reverse(); @@ -304,7 +345,7 @@ describe('Transaction Builder', () => { it('should preserve the Bitauth URI when broadcast fails', async () => { const failingProvider = new FailingMockNetworkProvider(); const contract = new Contract(p2pkhArtifact, [carolPkh], { provider: failingProvider }); - const utxo = randomUtxo({ satoshis: 100_000n }); + const utxo = failingProvider.addUtxo(contract.address, randomUtxo({ satoshis: 100_000n })); const transaction = new TransactionBuilder({ provider: failingProvider }) .addInput(utxo, contract.unlock.spend(carolPub, new SignatureTemplate(carolPriv))) @@ -356,14 +397,18 @@ describe('Transaction Builder', () => { p2pkhInstance.unlock.spend(carolPub, new SignatureTemplate(carolPriv)) ); + const randomContractUtxo = (defaults?: Partial): SpendableUtxo => ( + randomUtxo({ ...defaults, lockingBytecode: p2pkhInstance.lockingBytecode }) + ); + describe('BCH change lock', () => { it('should prevent further inputs or outputs after a BCH change output was added', () => { const builder = new TransactionBuilder({ provider }) - .addInput(randomUtxo(), carolUnlocker()) + .addInput(randomContractUtxo(), carolUnlocker()) .addOutput({ to: bobAddress, amount: 1000n }) .addBchChangeOutputIfNeeded({ to: aliceAddress, feeRate: 1.0 }); - expect(() => builder.addInput(randomUtxo(), carolUnlocker())).toThrow(OutputBchChangeLockedError); + expect(() => builder.addInput(randomContractUtxo(), carolUnlocker())).toThrow(OutputBchChangeLockedError); expect(() => builder.addOutput({ to: bobAddress, amount: 1000n })).toThrow(OutputBchChangeLockedError); expect(() => builder.addOpReturnOutput(['hello'])).toThrow(OutputBchChangeLockedError); }); @@ -371,7 +416,7 @@ describe('Transaction Builder', () => { it('should still lock when no change output was added because the surplus would be dust', () => { // Output leaves only a few satoshis of surplus, well below dust — no change output is added const builder = new TransactionBuilder({ provider }) - .addInput(randomUtxo({ satoshis: 2_000n }), carolUnlocker()) + .addInput(randomContractUtxo({ satoshis: 2_000n }), carolUnlocker()) .addOutput({ to: bobAddress, amount: 1_500n }); const outputCountBefore = builder.outputs.length; builder.addBchChangeOutputIfNeeded({ to: aliceAddress, feeRate: 1.0 }); @@ -385,10 +430,10 @@ describe('Transaction Builder', () => { it('should prevent further inputs or outputs of the same category after a token change output was added', () => { const token = randomToken(); const builder = new TransactionBuilder({ provider }) - .addInput(randomUtxo({ token }), carolUnlocker()) + .addInput(randomContractUtxo({ token }), carolUnlocker()) .addTokenChangeOutputIfNeeded({ category: token.category, to: aliceTokenAddress }); - expect(() => builder.addInput(randomUtxo({ token }), carolUnlocker())).toThrow(OutputTokenChangeLockedError); + expect(() => builder.addInput(randomContractUtxo({ token }), carolUnlocker())).toThrow(OutputTokenChangeLockedError); expect(() => builder.addOutput({ to: bobTokenAddress, amount: 1000n, token: { amount: 100n, category: token.category }, })).toThrow(OutputTokenChangeLockedError); @@ -398,13 +443,13 @@ describe('Transaction Builder', () => { const tokenA = randomToken(); const tokenB = randomToken(); const builder = new TransactionBuilder({ provider }) - .addInput(randomUtxo({ token: tokenA }), carolUnlocker()) + .addInput(randomContractUtxo({ token: tokenA }), carolUnlocker()) .addTokenChangeOutputIfNeeded({ category: tokenA.category, to: aliceTokenAddress }); expect(() => { - builder.addInput(randomUtxo({ token: tokenB }), carolUnlocker()); + builder.addInput(randomContractUtxo({ token: tokenB }), carolUnlocker()); builder.addOutput({ to: bobTokenAddress, amount: 1000n, token: { amount: 100n, category: tokenB.category } }); - builder.addInput(randomUtxo(), carolUnlocker()); + builder.addInput(randomContractUtxo(), carolUnlocker()); builder.addOutput({ to: bobAddress, amount: 1000n }); builder.addOpReturnOutput(['hello']); }).not.toThrow(); @@ -416,7 +461,7 @@ describe('Transaction Builder', () => { const token = randomToken({ amount: 1000n }); const tx = new TransactionBuilder({ provider }) - .addInput(randomUtxo({ token }), carolUnlocker()) + .addInput(randomContractUtxo({ token }), carolUnlocker()) .addOutput({ to: bobTokenAddress, amount: 1000n, token: { amount: 400n, category: token.category } }) .addTokenChangeOutputIfNeeded({ category: token.category, to: aliceTokenAddress }) .build(); @@ -431,14 +476,14 @@ describe('Transaction Builder', () => { it('should lock the category without adding an output when no change is needed', () => { const token = randomToken({ amount: 1000n }); const builder = new TransactionBuilder({ provider }) - .addInput(randomUtxo({ token }), carolUnlocker()) + .addInput(randomContractUtxo({ token }), carolUnlocker()) // Match input amount with an explicit output so there's no surplus .addOutput({ to: bobTokenAddress, amount: 1000n, token: { amount: 1000n, category: token.category } }); const outputCountBefore = builder.outputs.length; builder.addTokenChangeOutputIfNeeded({ category: token.category, to: aliceTokenAddress }); expect(builder.outputs.length).toBe(outputCountBefore); - expect(() => builder.addInput(randomUtxo({ token }), carolUnlocker())).toThrow(OutputTokenChangeLockedError); + expect(() => builder.addInput(randomContractUtxo({ token }), carolUnlocker())).toThrow(OutputTokenChangeLockedError); }); it('should scope the change output to the configured category across multiple invocations', () => { @@ -446,8 +491,8 @@ describe('Transaction Builder', () => { const tokenB = randomToken({ amount: 5000n }); const tx = new TransactionBuilder({ provider }) - .addInput(randomUtxo({ token: tokenA }), carolUnlocker()) - .addInput(randomUtxo({ token: tokenB }), carolUnlocker()) + .addInput(randomContractUtxo({ token: tokenA }), carolUnlocker()) + .addInput(randomContractUtxo({ token: tokenB }), carolUnlocker()) .addOutput({ to: bobTokenAddress, amount: 1000n, token: { amount: 700n, category: tokenA.category } }) .addOutput({ to: bobTokenAddress, amount: 1000n, token: { amount: 2000n, category: tokenB.category } }) .addTokenChangeOutputIfNeeded({ category: tokenA.category, to: aliceTokenAddress }) @@ -464,7 +509,7 @@ describe('Transaction Builder', () => { it('should fail when the change address does not support tokens', () => { const token = randomToken(); const builder = new TransactionBuilder({ provider }) - .addInput(randomUtxo({ token }), carolUnlocker()); + .addInput(randomContractUtxo({ token }), carolUnlocker()); expect(() => { builder.addTokenChangeOutputIfNeeded({ category: token.category, to: aliceAddress }); @@ -474,7 +519,7 @@ describe('Transaction Builder', () => { it('should fail when a BCH change output was already added', () => { const token = randomToken(); const builder = new TransactionBuilder({ provider }) - .addInput(randomUtxo({ token }), carolUnlocker()) + .addInput(randomContractUtxo({ token }), carolUnlocker()) .addBchChangeOutputIfNeeded({ to: aliceAddress, feeRate: 1.0 }); expect(() => { diff --git a/packages/cashscript/test/debugging.test.ts b/packages/cashscript/test/debugging.test.ts index 441afcabd..36fe4f473 100644 --- a/packages/cashscript/test/debugging.test.ts +++ b/packages/cashscript/test/debugging.test.ts @@ -1,5 +1,5 @@ -import { Contract, FailedTransactionError, MockNetworkProvider, SignatureAlgorithm, SignatureTemplate, TransactionBuilder, VmTarget } from '../src/index.js'; -import { DEFAULT_VM_TARGET } from '../src/libauth-template/utils.js'; +import { Contract, MockNetworkProvider, SignatureAlgorithm, SignatureTemplate, TransactionBuilder, UnlockerLockingBytecodeMismatchError, VmTarget } from '../src/index.js'; +import { DEFAULT_VM_TARGET, getLockScriptName } from '../src/libauth-template/utils.js'; import { aliceAddress, alicePriv, alicePub, bobPriv, bobPub } from './fixture/vars.js'; import { randomUtxo } from '../src/utils.js'; import { AuthenticationErrorCommon, binToHex, hexToBin } from '@bitauth/libauth'; @@ -14,6 +14,18 @@ import { artifactTestZeroHandling, artifactTestRequireInsideLoop, artifactTestLogInsideLoop, + artifactTestFunctionDebugging, + artifactTestFunctionDebuggingDefined, + artifactTestFunctionIntermediateResults, + artifactTestImportedFunctionDebugging, + artifactTestImportedFunctionDebuggingDefined, + artifactTestMultiReturn, + artifactTestMultilineFunctionRequire, + artifactTestNestedFunctions, + artifactTestNestedFunctionsDefined, + artifactTestNestedImportedFunctions, + artifactTestMixedNestedFunctions, + artifactTestInlinedCallingDefined, } from './fixture/debugging/debugging_contracts.js'; import { sha256 } from '@cashscript/utils'; @@ -165,7 +177,7 @@ describe('Debugging tests', () => { it('should log inside a loop', async () => { const transaction = new TransactionBuilder({ provider }) - .addInput(contractUtxo, contractTestLogInsideLoop.unlock.test_log_inside_loop()) + .addInput(contractTestLogInsideLoopUtxo, contractTestLogInsideLoop.unlock.test_log_inside_loop()) .addOutput({ to: contractTestLogInsideLoop.address, amount: 10000n }); expect(transaction).toLog(new RegExp('^\\[Input #0] Test.cash:6 i: 0$')); @@ -739,18 +751,16 @@ describe('Debugging tests', () => { expect(Object.keys(result).length).toBeGreaterThan(0); }); - // We currently don't have a way to properly handle non-matching UTXOs and unlockers - // Note: that also goes for Contract UTXOs where a user uses an unlocker of a different contract - it.skip('should fail when spending from P2PKH inputs with an unlocker for a different public key', async () => { + it('should fail when spending from P2PKH inputs with an unlocker for a different public key', async () => { const provider = new MockNetworkProvider(); provider.addUtxo(aliceAddress, randomUtxo()); provider.addUtxo(aliceAddress, randomUtxo()); - const transactionBuilder = new TransactionBuilder({ provider }) - .addInputs(await provider.getUtxos(aliceAddress), new SignatureTemplate(bobPriv).unlockP2PKH()) - .addOutput({ to: aliceAddress, amount: 5000n }); + const utxos = await provider.getUtxos(aliceAddress); - expect(() => transactionBuilder.debug()).toThrow(FailedTransactionError); + expect(() => ( + new TransactionBuilder({ provider }).addInputs(utxos, new SignatureTemplate(bobPriv).unlockP2PKH()) + )).toThrow(UnlockerLockingBytecodeMismatchError); }); }); @@ -814,3 +824,250 @@ describe('VM Resources', () => { expect(vmUsage[2]?.hashDigestIterations).toBeGreaterThan(0); }); }); + +describe('Debugging tests - user-defined function frames', () => { + const provider = new MockNetworkProvider(); + + const contract = new Contract(artifactTestFunctionDebugging, [], { provider }); + const contractUtxo = provider.addUtxo(contract.address, randomUtxo()); + + const importedContract = new Contract(artifactTestImportedFunctionDebugging, [], { provider }); + const importedUtxo = provider.addUtxo(importedContract.address, randomUtxo()); + + const definedContract = new Contract(artifactTestFunctionDebuggingDefined, [], { provider }); + const definedUtxo = provider.addUtxo(definedContract.address, randomUtxo()); + + const importedDefinedContract = new Contract(artifactTestImportedFunctionDebuggingDefined, [], { provider }); + const importedDefinedUtxo = provider.addUtxo(importedDefinedContract.address, randomUtxo()); + + it('attributes a console.log inside an inlined function to the function source line', () => { + const transaction = new TransactionBuilder({ provider }) + .addInput(contractUtxo, contract.unlock.spend(5n)) + .addOutput({ to: contract.address, amount: 10000n }); + + expect(transaction).toLog(new RegExp('^\\[Input #0] Test.cash:3 checking 5$')); + }); + + it('attributes a require failing inside an inlined function to the function source line', () => { + const transaction = new TransactionBuilder({ provider }) + .addInput(contractUtxo, contract.unlock.spend(0n)) + .addOutput({ to: contract.address, amount: 10000n }); + + // The artifact's inline ranges tie the merged require back to the function's own frame, so + // inlining is transparent: the failure reads like the defined variant below + expect(transaction).toFailRequireWith('Test.cash:4 Require statement failed at input 0 in contract Test, function checkValue (Test.cash, line 4) with the following message: value must be positive.'); + expect(transaction).toFailRequireWith('Failing statement: require(value > 0, "value must be positive")'); + }); + + it('still attributes a contract-level require to the contract source line', () => { + const transaction = new TransactionBuilder({ provider }) + .addInput(contractUtxo, contract.unlock.spend(100n)) + .addOutput({ to: contract.address, amount: 10000n }); + + expect(transaction).toFailRequireWith('Test.cash:10 Require statement failed at input 0 in contract Test.cash at line 10 with the following message: x must be small.'); + expect(transaction).toFailRequireWith('Failing statement: require(x < 100, "x must be small")'); + }); + + it('shows a call stack when a require fails in a nested inlined function', () => { + const nestedContract = new Contract(artifactTestNestedFunctions, [], { provider }); + const nestedUtxo = provider.addUtxo(nestedContract.address, randomUtxo()); + + const transaction = new TransactionBuilder({ provider }) + .addInput(nestedUtxo, nestedContract.unlock.spend(0n)) + .addOutput({ to: nestedContract.address, amount: 10000n }); + + expect(transaction).toFailRequireWith('Test.cash:3 Require statement failed at input 0 in contract Test, function assertPositive (Test.cash, line 3) with the following message: value must be positive.'); + expect(transaction).toFailRequireWith(` at assertPositive (Test.cash:3) — require(value > 0, "value must be positive"); + at validate (Test.cash:7) — assertPositive(amount) + at Test.cash:13 — validate(x)`); + }); + + it('shows a call stack when a require fails in a nested defined function', () => { + const nestedContract = new Contract(artifactTestNestedFunctionsDefined, [], { provider }); + const nestedUtxo = provider.addUtxo(nestedContract.address, randomUtxo()); + + const transaction = new TransactionBuilder({ provider }) + .addInput(nestedUtxo, nestedContract.unlock.spend(0n)) + .addOutput({ to: nestedContract.address, amount: 10000n }); + + // The trace is identical to the inlined variant: runtime callers come from the VM's control + // stack instead of inline ranges, but the displayed stack is the same + expect(transaction).toFailRequireWith('Test.cash:3 Require statement failed at input 0 in contract Test, function assertPositive (Test.cash, line 3) with the following message: value must be positive.'); + expect(transaction).toFailRequireWith(` at assertPositive (Test.cash:3) — require(value > 0, "value must be positive"); + at validate (Test.cash:7) — assertPositive(amount) + at Test.cash:13 — validate(x)`); + }); + + it('shows a call stack across imported functions', () => { + const nestedContract = new Contract(artifactTestNestedImportedFunctions, [], { provider }); + const nestedUtxo = provider.addUtxo(nestedContract.address, randomUtxo()); + + const transaction = new TransactionBuilder({ provider }) + .addInput(nestedUtxo, nestedContract.unlock.spend(0n)) + .addOutput({ to: nestedContract.address, amount: 10000n }); + + expect(transaction).toFailRequireWith('nested_helpers.cash:3 Require statement failed at input 0 in contract Test, function assertPositive (nested_helpers.cash, line 3) with the following message: value must be positive.'); + expect(transaction).toFailRequireWith(` at assertPositive (nested_helpers.cash:3) — require(value > 0, "value must be positive"); + at validate (nested_helpers.cash:7) — assertPositive(amount) + at Test.cash:6 — validate(x)`); + }); + + it('shows a call stack when an inlined function calls a defined function', () => { + const inlinedCallingDefinedContract = new Contract(artifactTestInlinedCallingDefined, [], { provider }); + const inlinedCallingDefinedUtxo = provider.addUtxo(inlinedCallingDefinedContract.address, randomUtxo()); + + const transaction = new TransactionBuilder({ provider }) + .addInput(inlinedCallingDefinedUtxo, inlinedCallingDefinedContract.unlock.spend(0n)) + .addOutput({ to: inlinedCallingDefinedContract.address, amount: 10000n }); + + // The invoke of bigCheck sits inside the inlined wrapper's body within the contract; the + // inline range and the invoke's position within it give wrapper its own hop + expect(transaction).toFailRequireWith('Test.cash:3 Require statement failed at input 0 in contract Test, function bigCheck (Test.cash, line 3) with the following message: v must be positive.'); + expect(transaction).toFailRequireWith(` at bigCheck (Test.cash:3) — require(v > 0, "v must be positive"); + at wrapper (Test.cash:8) — bigCheck(v) + at Test.cash:13 — wrapper(x)`); + }); + + it('shows a call stack alternating between defined and inlined functions', () => { + const mixedContract = new Contract(artifactTestMixedNestedFunctions, [], { provider }); + const mixedUtxo = provider.addUtxo(mixedContract.address, randomUtxo()); + + const transaction = new TransactionBuilder({ provider }) + .addInput(mixedUtxo, mixedContract.unlock.spend(0n)) + .addOutput({ to: mixedContract.address, amount: 10000n }); + + // The inlined innerCheck attributes through deepHelper's inline ranges; the runtime hops come + // from the VM's control stack, with the inlined middle recovered from the position of the + // deepHelper invoke within middle's inline range in outerHelper + expect(transaction).toFailRequireWith('Test.cash:3 Require statement failed at input 0 in contract Test, function innerCheck (Test.cash, line 3) with the following message: v must be positive.'); + expect(transaction).toFailRequireWith(` at innerCheck (Test.cash:3) — require(v > 0, "v must be positive"); + at deepHelper (Test.cash:7) — innerCheck(v) + at middle (Test.cash:12) — deepHelper(v) + at outerHelper (Test.cash:16) — middle(v) + at Test.cash:21 — outerHelper(x)`); + }); + + it('attributes a multiline require failing inside an inlined function with its full statement', () => { + const multilineContract = new Contract(artifactTestMultilineFunctionRequire, [], { provider }); + const multilineUtxo = provider.addUtxo(multilineContract.address, randomUtxo()); + + const transaction = new TransactionBuilder({ provider }) + .addInput(multilineUtxo, multilineContract.unlock.spend(0n)) + .addOutput({ to: multilineContract.address, amount: 10000n }); + + expect(transaction).toFailRequireWith('Test.cash:3 Require statement failed at input 0 in contract Test, function checkRange (Test.cash, line 3) with the following message: value must be positive.'); + expect(transaction).toFailRequireWith(`Failing statement: require( + value > 0, + "value must be positive" + )`); + // In the call stack display, the multiline statement is flattened to a single line + expect(transaction).toFailRequireWith('at checkRange (Test.cash:3) — require( value > 0, "value must be positive" )'); + }); + + it('attributes a require failing inside an inlined imported function to the imported function', () => { + const transaction = new TransactionBuilder({ provider }) + .addInput(importedUtxo, importedContract.unlock.spend(0n)) + .addOutput({ to: importedContract.address, amount: 10000n }); + + // Inlining is transparent for debugging: the failure reads exactly like the defined + // (OP_DEFINE'd) form of the same function — see the defined variants below + expect(transaction).toFailRequireWith('function_helpers.cash:3 Require statement failed at input 0 in contract Test, function assertPositive (function_helpers.cash, line 3) with the following message: value must be positive.'); + expect(transaction).toFailRequireWith('Failing statement: require(value > 0, "value must be positive")'); + }); + + it('attributes a console.log inside an inlined imported function to the imported file', () => { + const transaction = new TransactionBuilder({ provider }) + .addInput(importedUtxo, importedContract.unlock.spend(5n)) + .addOutput({ to: importedContract.address, amount: 10000n }); + + expect(transaction).toLog(new RegExp('^\\[Input #0] function_helpers.cash:2 checking 5$')); + }); + + it('attributes a console.log inside an imported function frame to the imported file', () => { + const transaction = new TransactionBuilder({ provider }) + .addInput(importedDefinedUtxo, importedDefinedContract.unlock.spend(5n)) + .addOutput({ to: importedDefinedContract.address, amount: 10000n }); + + expect(transaction).toLog(new RegExp('^\\[Input #0] function_helpers.cash:2 checking 5$')); + }); + + it('attributes a require failing inside an imported function frame to the imported file', () => { + const transaction = new TransactionBuilder({ provider }) + .addInput(importedDefinedUtxo, importedDefinedContract.unlock.spend(0n)) + .addOutput({ to: importedDefinedContract.address, amount: 10000n }); + + expect(transaction).toFailRequireWith('function_helpers.cash:3 Require statement failed at input 0 in contract Test, function assertPositive (function_helpers.cash, line 3) with the following message: value must be positive.'); + expect(transaction).toFailRequireWith('Failing statement: require(value > 0, "value must be positive")'); + }); + + it('binds multi-return values to destructuring targets in declared order', () => { + const multiReturnContract = new Contract(artifactTestMultiReturn, [], { provider }); + const multiReturnUtxo = provider.addUtxo(multiReturnContract.address, randomUtxo()); + + // 13 / 3 = 4 remainder 1: both requires only pass if q binds the first return value and r the last + const transaction = new TransactionBuilder({ provider }) + .addInput(multiReturnUtxo, multiReturnContract.unlock.spend(13n)) + .addOutput({ to: multiReturnContract.address, amount: 10000n }); + + expect(transaction).not.toFailRequire(); + + // 14 / 3 = 4 remainder 2: the remainder require fails and attributes to its own line + const failingTransaction = new TransactionBuilder({ provider }) + .addInput(multiReturnUtxo, multiReturnContract.unlock.spend(14n)) + .addOutput({ to: multiReturnContract.address, amount: 10000n }); + + expect(failingTransaction).toFailRequireWith('Test.cash:10 Require statement failed at input 0 in contract Test.cash at line 10 with the following message: remainder should be 1.'); + }); + + it('logs intermediate results that get optimised out inside a function', () => { + const intermediateContract = new Contract(artifactTestFunctionIntermediateResults, [alicePub], { provider }); + const intermediateUtxo = provider.addUtxo(intermediateContract.address, randomUtxo()); + + const transaction = new TransactionBuilder({ provider }) + .addInput(intermediateUtxo, intermediateContract.unlock.spend()) + .addOutput({ to: intermediateContract.address, amount: 10000n }); + + const expectedHash = binToHex(sha256(alicePub)); + expect(transaction).toLog(new RegExp(`^\\[Input #0] Test.cash:4 0x${expectedHash}$`)); + }); + + it('renders source-mapped function definitions in the BitAuth IDE template', () => { + const transaction = new TransactionBuilder({ provider }) + .addInput(definedUtxo, definedContract.unlock.spend(5n)) + .addOutput({ to: definedContract.address, amount: 10000n }); + + const template = transaction.getLibauthTemplate(); + const lockScript = template.scripts[getLockScriptName(definedContract)].script; + + // The function body is rendered as a `<...>` push group annotated with its own source lines + expect(lockScript).toContain('/* function checkValue(int value) {'); + expect(lockScript).toContain('> OP_0 OP_DEFINE'); + expect(lockScript).toContain('OP_0 OP_INVOKE'); + }); + + it('renders imported function definitions with their import provenance in the BitAuth IDE template', () => { + const transaction = new TransactionBuilder({ provider }) + .addInput(importedDefinedUtxo, importedDefinedContract.unlock.spend(5n)) + .addOutput({ to: importedDefinedContract.address, amount: 10000n }); + + const template = transaction.getLibauthTemplate(); + const lockScript = template.scripts[getLockScriptName(importedDefinedContract)].script; + + expect(lockScript).toContain('>>> imported from function_helpers.cash'); + expect(lockScript).toContain('/* function assertPositive(int value) {'); + expect(lockScript).toContain('> OP_0 OP_DEFINE'); + }); + + it('renders an inlined function body without a definition or invocation', () => { + const transaction = new TransactionBuilder({ provider }) + .addInput(contractUtxo, contract.unlock.spend(5n)) + .addOutput({ to: contract.address, amount: 10000n }); + + const template = transaction.getLibauthTemplate(); + const lockScript = template.scripts[getLockScriptName(contract)].script; + + expect(lockScript).not.toContain('OP_DEFINE'); + expect(lockScript).not.toContain('OP_INVOKE'); + expect(lockScript).toContain('require(value > 0, "value must be positive")'); + }); +}); diff --git a/packages/cashscript/test/e2e/HodlVault.test.ts b/packages/cashscript/test/e2e/HodlVault.test.ts index d3b0f81e6..7c42c5888 100644 --- a/packages/cashscript/test/e2e/HodlVault.test.ts +++ b/packages/cashscript/test/e2e/HodlVault.test.ts @@ -6,7 +6,7 @@ import { Network, TransactionBuilder, SignatureAlgorithm, - HashType, + SighashType, } from '../../src/index.js'; import { alicePriv, @@ -108,7 +108,7 @@ describe('HodlVault', () => { const amount = 10000n; const { utxos, changeAmount } = gatherUtxos(await hodlVault.getUtxos(), { amount, fee: 2000n }); - const signatureTemplate = new SignatureTemplate(alicePriv, HashType.SIGHASH_ALL, SignatureAlgorithm.ECDSA); + const signatureTemplate = new SignatureTemplate(alicePriv, SighashType.SIGHASH_ALL, SignatureAlgorithm.ECDSA); // when const tx = await new TransactionBuilder({ provider }) @@ -185,7 +185,7 @@ describe('HodlVault', () => { .send()).rejects.toThrow('HodlVault.cash:31 Require statement failed at input 0 in contract HodlVault.cash at line 31'); // datasig: unlocker should throw when given an improper length - const signatureTemplate = new SignatureTemplate(alicePriv, HashType.SIGHASH_ALL, SignatureAlgorithm.ECDSA); + const signatureTemplate = new SignatureTemplate(alicePriv, SighashType.SIGHASH_ALL, SignatureAlgorithm.ECDSA); expect(() => hodlVault.unlock.spend(signatureTemplate, placeholder(100), message)).toThrow("Found type 'bytes100' where type 'datasig' was expected"); // datasig: unlocker should not throw when given a proper length, but transaction should fail on invalid sig diff --git a/packages/cashscript/test/e2e/LocktimeGuard.test.ts b/packages/cashscript/test/e2e/LocktimeGuard.test.ts index 76e4ac96a..8503aef93 100644 --- a/packages/cashscript/test/e2e/LocktimeGuard.test.ts +++ b/packages/cashscript/test/e2e/LocktimeGuard.test.ts @@ -59,7 +59,7 @@ describe('Locktime Guard with function parameters', () => { const provider = new MockNetworkProvider(); const artifactWithParameter = compileString(` - pragma cashscript ^0.13.0; + pragma cashscript ^0.14.0; contract ParameterizedLocktimeGuard() { function spend( diff --git a/packages/cashscript/test/e2e/MultiContract.test.ts b/packages/cashscript/test/e2e/MultiContract.test.ts index 734941962..2004cbf26 100644 --- a/packages/cashscript/test/e2e/MultiContract.test.ts +++ b/packages/cashscript/test/e2e/MultiContract.test.ts @@ -18,7 +18,7 @@ import { carolPriv, carolPub, } from '../fixture/vars.js'; -import { Network, Utxo } from '../../src/interfaces.js'; +import { Network, SpendableUtxo } from '../../src/interfaces.js'; import { addressToLockScript, randomUtxo } from '../../src/utils.js'; import p2pkhArtifact from '../fixture/p2pkh.artifact.js'; import twtArtifact from '../fixture/transfer_with_timeout.artifact.js'; @@ -214,9 +214,9 @@ describe('Multi Contract', () => { const correctLockingBytecode = addressToLockScript(correctContract.address); const siblingIntrospectionContract = new Contract(SiblingIntrospectionArtifact, [correctLockingBytecode], { provider }); - let correctContractUtxo: Utxo; - let incorrectContractUtxo: Utxo; - let siblingIntrospectionUtxo: Utxo; + let correctContractUtxo: SpendableUtxo; + let incorrectContractUtxo: SpendableUtxo; + let siblingIntrospectionUtxo: SpendableUtxo; beforeAll(async () => { correctContractUtxo = await addUtxo(provider, correctContract.address, randomUtxo()); diff --git a/packages/cashscript/test/e2e/network/MockNetworkProvider.test.ts b/packages/cashscript/test/e2e/network/MockNetworkProvider.test.ts index 3f14a954b..756d56182 100644 --- a/packages/cashscript/test/e2e/network/MockNetworkProvider.test.ts +++ b/packages/cashscript/test/e2e/network/MockNetworkProvider.test.ts @@ -9,6 +9,7 @@ import { alicePriv, alicePub, bobAddress, + bobPriv, } from '../../fixture/vars.js'; describe.skipIf(Boolean(process.env.TESTS_USE_CHIPNET))('MockNetworkProvider', () => { @@ -55,7 +56,7 @@ describe.skipIf(Boolean(process.env.TESTS_USE_CHIPNET))('MockNetworkProvider', ( await expect(provider.sendRawTransaction(tx.slice(0, -2))).rejects.toThrow('Error reading transaction.'); // send valid transaction - await expect(provider.sendRawTransaction(tx)).resolves.not.toThrow(); + const txid = await provider.sendRawTransaction(tx); // utxos should be removed from the provider expect(await provider.getUtxos(aliceAddress)).toHaveLength(0); @@ -64,7 +65,63 @@ describe.skipIf(Boolean(process.env.TESTS_USE_CHIPNET))('MockNetworkProvider', ( // utxo should be added to bob expect(await provider.getUtxos(bobAddress)).toHaveLength(1); - await expect(provider.sendRawTransaction(tx)).rejects.toThrow('already submitted'); + // resubmission resolves with the same txid and doesn't change the utxo set + await expect(provider.sendRawTransaction(tx)).resolves.toBe(txid); + expect(await provider.getUtxos(aliceAddress)).toHaveLength(0); + expect(await provider.getUtxos(p2pkhInstance.address)).toHaveLength(0); + expect(await provider.getUtxos(bobAddress)).toHaveLength(1); + }); + }); + + describe('transaction validation', () => { + const provider = new MockNetworkProvider(); + + beforeEach(() => { + provider.reset(); + }); + + it('should annotate UTXOs with the locking bytecode they were added under', async () => { + const aliceLockingBytecode = binToHex(addressToLockScript(aliceAddress)); + const addedUtxo = provider.addUtxo(aliceAddress, randomUtxo()); + + expect(addedUtxo.lockingBytecode).toBe(aliceLockingBytecode); + + const fetchedUtxos = await provider.getUtxos(aliceAddress); + expect(fetchedUtxos[0].lockingBytecode).toBe(aliceLockingBytecode); + }); + + it('should reject a transaction that spends a UTXO with a mismatched unlocker', async () => { + // We deliberately set the lockingBytecode to bob's lock script so the TransactionBuilder + // mismatch check does not trigger, allowing us to build a transaction that spends alice's + // UTXO with bob's key + const bobLockingBytecode = binToHex(addressToLockScript(bobAddress)); + const utxo = { ...provider.addUtxo(aliceAddress, randomUtxo()), lockingBytecode: bobLockingBytecode }; + + const transaction = new TransactionBuilder({ provider }) + .addInput(utxo, new SignatureTemplate(bobPriv).unlockP2PKH()) + .addOutput({ to: aliceAddress, amount: 5000n }) + .build(); + + await expect(provider.sendRawTransaction(transaction)).rejects.toThrow(); + + // the failed transaction should not have updated the utxo set + expect(await provider.getUtxos(aliceAddress)).toHaveLength(1); + }); + + it('should accept invalid transactions when validateTransactions is set to false', async () => { + const nonValidatingProvider = new MockNetworkProvider({ validateTransactions: false }); + const bobLockingBytecode = binToHex(addressToLockScript(bobAddress)); + const utxo = { + ...nonValidatingProvider.addUtxo(aliceAddress, randomUtxo()), + lockingBytecode: bobLockingBytecode, + }; + + const transaction = new TransactionBuilder({ provider: nonValidatingProvider }) + .addInput(utxo, new SignatureTemplate(bobPriv).unlockP2PKH()) + .addOutput({ to: aliceAddress, amount: 5000n }) + .build(); + + await expect(nonValidatingProvider.sendRawTransaction(transaction)).resolves.toBeTruthy(); }); }); diff --git a/packages/cashscript/test/fixture/debugging/debugging_contracts.ts b/packages/cashscript/test/fixture/debugging/debugging_contracts.ts index be048ef29..417f76198 100644 --- a/packages/cashscript/test/fixture/debugging/debugging_contracts.ts +++ b/packages/cashscript/test/fixture/debugging/debugging_contracts.ts @@ -1,4 +1,160 @@ -import { compileString } from 'cashc'; +import { compileFile, compileString } from 'cashc/dist/internal.js'; + +const CONTRACT_TEST_FUNCTION_DEBUGGING = ` +function checkValue(int value) { + console.log("checking", value); + require(value > 0, "value must be positive"); +} + +contract Test() { + function spend(int x) { + checkValue(x); + require(x < 100, "x must be small"); + } +} +`; + +const CONTRACT_TEST_FUNCTION_INTERMEDIATE_RESULTS = ` +function hashTwice(pubkey pk) returns (bytes32) { + bytes32 singleHash = sha256(pk); + console.log(singleHash); + bytes32 doubleHash = sha256(singleHash); + return doubleHash; +} + +contract Test(pubkey owner) { + function spend() { + require(hashTwice(owner).length == 32, "should be 32 bytes"); + } +} +`; + +// Nested function calls, so a require failing in the innermost function produces a call stack +// through the intermediate function into the contract. The Defined variant exercises the same +// stack through the VM's control stack instead of through inline ranges. +const CONTRACT_TEST_NESTED_FUNCTIONS = ` +function assertPositive(int value) { + require(value > 0, "value must be positive"); +} + +function validate(int amount) { + assertPositive(amount); + require(amount < 1000, "amount too large"); +} + +contract Test() { + function spend(int x) { + validate(x); + require(x < 100); + } +} +`; + +// The same nested-call shape as CONTRACT_TEST_NESTED_FUNCTIONS, but with the functions imported +// from another file, so the call stack attributes its hops to the imported file. +const NESTED_HELPERS_SOURCE = ` +function assertPositive(int value) { + require(value > 0, "value must be positive"); +} + +function validate(int amount) { + assertPositive(amount); + require(amount < 1000, "amount too large"); +} +`; + +const CONTRACT_TEST_NESTED_IMPORTED_FUNCTIONS = ` +import "./nested_helpers.cash"; + +contract Test() { + function spend(int x) { + validate(x); + require(x < 100); + } +} +`; + +// The inlined wrapper invokes the defined bigCheck, where the require fails — the smallest shape +// where an inline range and the VM's control stack combine in one call stack. +const CONTRACT_TEST_INLINED_CALLING_DEFINED = ` +function bigCheck(int v) returns (int) { + require(v > 0, "v must be positive"); + return (v * 7 + 3) * (v + 11) - 5; +} + +function wrapper(int v) returns (int) { + return bigCheck(v) + bigCheck(v + 1); +} + +contract Test() { + function spend(int x) { + require(wrapper(x) > 0, "sum must be positive"); + } +} +`; + +// Alternates between shared and inlined callables: spend invokes the defined outerHelper, which +// contains the inlined middle, which invokes the defined deepHelper, which contains the inlined +// innerCheck where the require fails. +const CONTRACT_TEST_MIXED_NESTED_FUNCTIONS = ` +function innerCheck(int v) { + require(v > 0, "v must be positive"); +} + +function deepHelper(int v) returns (int) { + innerCheck(v); + return (v * 7 + 3) * (v + 11) - 5; +} + +function middle(int v) returns (int) { + return deepHelper(v) + deepHelper(v + 1); +} + +function outerHelper(int v) returns (int) { + return middle(v) * 2; +} + +contract Test() { + function spend(int x) { + require(outerHelper(x) + outerHelper(x + 1) > 0, "sum must be positive"); + } +} +`; + +// The require statement inside the (inlined) function spans multiple lines, so its statement can +// only be extracted through the function frame's source map rather than a single source line. +const CONTRACT_TEST_MULTILINE_FUNCTION_REQUIRE = ` +function checkRange(int value) { + require( + value > 0, + "value must be positive" + ); +} + +contract Test() { + function spend(int x) { + checkRange(x); + require(x < 100); + } +} +`; + +// Multi-value return destructuring: the requires only pass when the first declared return value +// binds to the first target (quotient) and the last to the last (remainder), pinning the runtime +// value ordering of the calling convention. +const CONTRACT_TEST_MULTI_RETURN = ` +function divmod(int a, int b) returns (int, int) { + return a / b, a % b; +} + +contract Test() { + function spend(int x) { + int q, int r = divmod(x, 3); + require(q == 4, "quotient should be 4"); + require(r == 1, "remainder should be 1"); + } +} +`; const CONTRACT_TEST_REQUIRES = ` contract Test() { @@ -437,3 +593,32 @@ export const artifactTestMultipleLogs = compileString(CONTRACT_TEST_MULTIPLE_LOG export const artifactTestMultipleConstructorParameters = compileString(CONTRACT_TEST_MULTIPLE_CONSTRUCTOR_PARAMETERS); export const artifactTestRequireInsideLoop = compileString(CONTRACT_TEST_REQUIRE_INSIDE_LOOP); export const artifactTestLogInsideLoop = compileString(CONTRACT_TEST_LOG_INSIDE_LOOP); +export const artifactTestFunctionDebugging = compileString(CONTRACT_TEST_FUNCTION_DEBUGGING); +export const artifactTestFunctionIntermediateResults = compileString(CONTRACT_TEST_FUNCTION_INTERMEDIATE_RESULTS); +export const artifactTestMultiReturn = compileString(CONTRACT_TEST_MULTI_RETURN); +export const artifactTestMultilineFunctionRequire = compileString(CONTRACT_TEST_MULTILINE_FUNCTION_REQUIRE); +export const artifactTestNestedFunctions = compileString(CONTRACT_TEST_NESTED_FUNCTIONS); +export const artifactTestNestedFunctionsDefined = compileString( + CONTRACT_TEST_NESTED_FUNCTIONS, + { disableInlining: true }, +); +export const artifactTestNestedImportedFunctions = compileString( + CONTRACT_TEST_NESTED_IMPORTED_FUNCTIONS, + { files: { './nested_helpers.cash': NESTED_HELPERS_SOURCE } }, +); +export const artifactTestMixedNestedFunctions = compileString(CONTRACT_TEST_MIXED_NESTED_FUNCTIONS); +export const artifactTestInlinedCallingDefined = compileString(CONTRACT_TEST_INLINED_CALLING_DEFINED); + +// Compiled from a file so the imported function (function_helpers.cash) keeps its own source provenance. +export const artifactTestImportedFunctionDebugging = compileFile(new URL('./function_importer.cash', import.meta.url)); + +// Variants with inlining disabled, so single-use functions stay OP_DEFINE'd and are debugged +// through their own frames (attributing to their own source instead of the call site). +export const artifactTestFunctionDebuggingDefined = compileString( + CONTRACT_TEST_FUNCTION_DEBUGGING, + { disableInlining: true }, +); +export const artifactTestImportedFunctionDebuggingDefined = compileFile( + new URL('./function_importer.cash', import.meta.url), + { disableInlining: true }, +); diff --git a/packages/cashscript/test/fixture/debugging/function_helpers.cash b/packages/cashscript/test/fixture/debugging/function_helpers.cash new file mode 100644 index 000000000..4805c1819 --- /dev/null +++ b/packages/cashscript/test/fixture/debugging/function_helpers.cash @@ -0,0 +1,4 @@ +function assertPositive(int value) { + console.log("checking", value); + require(value > 0, "value must be positive"); +} diff --git a/packages/cashscript/test/fixture/debugging/function_importer.cash b/packages/cashscript/test/fixture/debugging/function_importer.cash new file mode 100644 index 000000000..eac2e4b41 --- /dev/null +++ b/packages/cashscript/test/fixture/debugging/function_importer.cash @@ -0,0 +1,8 @@ +import "./function_helpers.cash"; + +contract Test() { + function spend(int x) { + assertPositive(x); + require(x < 100, "x must be small"); + } +} diff --git a/packages/cashscript/test/fixture/libauth-template/fixtures.ts b/packages/cashscript/test/fixture/libauth-template/fixtures.ts index 3facdf713..84684c0f9 100644 --- a/packages/cashscript/test/fixture/libauth-template/fixtures.ts +++ b/packages/cashscript/test/fixture/libauth-template/fixtures.ts @@ -1,9 +1,9 @@ -import { Contract, HashType, MockNetworkProvider, SignatureAlgorithm, SignatureTemplate, TransactionBuilder, randomNFT, randomToken, randomUtxo } from '../../../src/index.js'; +import { Contract, MockNetworkProvider, SighashType, SignatureAlgorithm, SignatureTemplate, TransactionBuilder, randomNFT, randomToken, randomUtxo } from '../../../src/index.js'; import TransferWithTimeout from '../transfer_with_timeout.artifact.js'; import Mecenas from '../mecenas.artifact.js'; import P2PKH from '../p2pkh.artifact.js'; import HoldVault from '../hodl_vault.artifact.js'; -import { aliceAddress, alicePkh, alicePriv, alicePub, bobPkh, bobPriv, bobPub, oracle, oraclePub } from '../vars.js'; +import { aliceAddress, alicePkh, alicePriv, alicePub, bobAddress, bobPkh, bobPriv, bobPub, oracle, oraclePub } from '../vars.js'; import { WalletTemplate, hexToBin } from '@bitauth/libauth'; const provider = new MockNetworkProvider(); @@ -1262,7 +1262,7 @@ export const fixtures: Fixture[] = [ }, }, }, - // TODO: Make it work with different hashtypes and signature algorithms + // TODO: Make it work with different sighash types and signature algorithms // { // name: 'P2PKH (sending NFTs)', // transaction: (() => { @@ -1272,11 +1272,11 @@ export const fixtures: Fixture[] = [ // const to = contract.address; // const amount = 1000n; - // const hashtype = HashType.SIGHASH_SINGLE | HashType.SIGHASH_ANYONECANPAY; + // const sighashType = SighashType.SIGHASH_SINGLE | SighashType.SIGHASH_ANYONECANPAY; // const signatureAlgorithm = SignatureAlgorithm.ECDSA; // const tx = contract.functions - // .spend(alicePub, new SignatureTemplate(alicePriv, hashtype, signatureAlgorithm)) + // .spend(alicePub, new SignatureTemplate(alicePriv, sighashType, signatureAlgorithm)) // .to(to, amount); // return tx; @@ -1290,18 +1290,19 @@ export const fixtures: Fixture[] = [ const contractUtxo = provider.addUtxo(contract.address, randomUtxo()); const p2pkhUtxo = provider.addUtxo(aliceAddress, randomUtxo()); + const bobP2pkhUtxo = provider.addUtxo(bobAddress, randomUtxo()); const to = contract.tokenAddress; const amount = 1000n; const aliceDefaultTemplate = new SignatureTemplate(alicePriv); - const aliceCustomTemplate = new SignatureTemplate(alicePriv, HashType.SIGHASH_NONE, SignatureAlgorithm.ECDSA); - const bobCustomTemplate = new SignatureTemplate(bobPriv, HashType.SIGHASH_ALL, SignatureAlgorithm.ECDSA); + const aliceCustomTemplate = new SignatureTemplate(alicePriv, SighashType.SIGHASH_NONE, SignatureAlgorithm.ECDSA); + const bobCustomTemplate = new SignatureTemplate(bobPriv, SighashType.SIGHASH_ALL, SignatureAlgorithm.ECDSA); const tx = new TransactionBuilder({ provider }) .addInput(p2pkhUtxo, aliceDefaultTemplate.unlockP2PKH()) .addInput(contractUtxo, contract.unlock.spend(alicePub, aliceCustomTemplate)) - .addInput(p2pkhUtxo, bobCustomTemplate.unlockP2PKH()) + .addInput(bobP2pkhUtxo, bobCustomTemplate.unlockP2PKH()) .addOutput({ to, amount }); return tx; diff --git a/packages/cashscript/test/test-util.ts b/packages/cashscript/test/test-util.ts index a809a68d7..e29c158e2 100644 --- a/packages/cashscript/test/test-util.ts +++ b/packages/cashscript/test/test-util.ts @@ -6,9 +6,9 @@ import { } from '@bitauth/libauth'; import PQueue from 'p-queue'; import pRetry from 'p-retry'; -import { Output, Network, Utxo } from '../src/interfaces.js'; +import { Output, Network, SpendableUtxo, Utxo } from '../src/interfaces.js'; import { network as defaultNetwork, funderAddress, funderPriv } from './fixture/vars.js'; -import { getNetworkPrefix, isNonTokenUtxo, libauthOutputToCashScriptOutput } from '../src/utils.js'; +import { addressToLockScript, getNetworkPrefix, isNonTokenUtxo, libauthOutputToCashScriptOutput } from '../src/utils.js'; import { utxoComparator } from '../src/utils.js'; import MockNetworkProvider from '../src/network/MockNetworkProvider.js'; import NetworkProvider from '../src/network/NetworkProvider.js'; @@ -38,7 +38,7 @@ export function getTxOutputs(tx: Transaction, network: Network = defaultNetwork) }); } -export function getLargestUtxo(utxos: Utxo[]): Utxo { +export function getLargestUtxo(utxos: U[]): U { return [...utxos].sort(utxoComparator).reverse()[0]; } @@ -59,7 +59,7 @@ export async function addUtxo( provider: NetworkProvider, address: string, utxo: Utxo, -): Promise { +): Promise { if (provider instanceof MockNetworkProvider) { return provider.addUtxo(address, utxo); } @@ -89,7 +89,7 @@ async function sendLiveAddUtxo( provider: NetworkProvider, address: string, utxo: Utxo, -): Promise { +): Promise { const funderUtxos = (await provider.getUtxos(funderAddress)) .filter(isNonTokenUtxo) .sort(utxoComparator) @@ -107,14 +107,15 @@ async function sendLiveAddUtxo( txid: tx.txid, vout: 0, satoshis: utxo.satoshis, + lockingBytecode: binToHex(addressToLockScript(address)), }; } -export function gatherUtxos( - utxos: Utxo[], +export function gatherUtxos( + utxos: U[], options?: { amount?: bigint, fee?: bigint }, -): { utxos: Utxo[], total: bigint, changeAmount: bigint } { - const targetUtxos: Utxo[] = []; +): { utxos: U[], total: bigint, changeAmount: bigint } { + const targetUtxos: U[] = []; let total = 0n; // 1000 for fees diff --git a/packages/cashscript/test/types/Contract.types.test.ts b/packages/cashscript/test/types/Contract.types.test.ts index 0eb6d8f2f..d94bc1244 100644 --- a/packages/cashscript/test/types/Contract.types.test.ts +++ b/packages/cashscript/test/types/Contract.types.test.ts @@ -119,7 +119,7 @@ const provider = new MockNetworkProvider(); const contract = new Contract(p2pkhArtifact, [alicePkh], { provider }); // it('should not give type errors when using correct function inputs') - contract.unlock.spend(alicePub, new SignatureTemplate(alicePriv)).generateLockingBytecode(); + contract.unlock.spend(alicePub, new SignatureTemplate(alicePriv)).generateUnlockingBytecode; // it('should give type errors when calling a function that does not exist') // @ts-expect-error @@ -137,14 +137,14 @@ const provider = new MockNetworkProvider(); // it('should not perform type checking when cast to any') const contractAsAny = new Contract(p2pkhArtifact as any, [alicePkh, 1000n], { provider }); - contractAsAny.unlock.notAFunction().generateLockingBytecode(); + contractAsAny.unlock.notAFunction().generateUnlockingBytecode; contractAsAny.unlock.spend(); contractAsAny.unlock.spend(1000n, true); // it('should not perform type checking when cannot infer type') // Note: would be very nice if it *could* infer the type from static json const contractFromUnknown = new Contract(p2pkhArtifactJsonNotConst, [alicePkh, 1000n], { provider }); - contractFromUnknown.unlock.notAFunction().generateLockingBytecode(); + contractFromUnknown.unlock.notAFunction().generateUnlockingBytecode; contractFromUnknown.unlock.spend(); contractFromUnknown.unlock.spend(1000n, true); diff --git a/packages/utils/package.json b/packages/utils/package.json index 3a6eba741..b1c7058b4 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@cashscript/utils", - "version": "0.13.3", + "version": "0.14.0-next.5", "description": "CashScript utilities and types", "keywords": [ "bitcoin cash", @@ -43,10 +43,10 @@ "@bitauth/libauth": "^3.1.0-next.8" }, "devDependencies": { - "@vitest/coverage-v8": "^4.0.15", + "@vitest/coverage-v8": "^4.1.10", "eslint": "^8.54.0", "typescript": "^5.9.2", - "vitest": "^4.0.15" + "vitest": "^4.1.10" }, "gitHead": "bf02a4b641d5d03c035d052247a545109c17b708" } diff --git a/packages/utils/src/artifact.ts b/packages/utils/src/artifact.ts index 733a67618..ba2c34d41 100644 --- a/packages/utils/src/artifact.ts +++ b/packages/utils/src/artifact.ts @@ -19,6 +19,29 @@ export interface DebugInformation { logs: readonly LogEntry[]; // log entries generated from `console.log` statements requires: readonly RequireStatement[]; // messages for failing `require` statements sourceTags?: string; // semantic tags for opcodes (e.g. loop update/condition ranges) + functions?: readonly DebugFrame[]; // Debug metadata for each global definition (defined frames first, then inlined ones) + inlineRanges?: string; // runs where inlined callables' bodies were emitted (see `generateInlineRanges`) +} + +export interface DebugFrame { + id?: number; // the function's id, as used with OP_DEFINE and OP_INVOKE in the bytecode; absent for inlined callables + name: string; // the function/constant's name + kind?: 'constant'; // present when the VM function is the lowered form of a global constant; absent for regular functions + inputs: readonly AbiInput[]; // the function's parameters (name and type), mirroring the ABI; for reference + bytecode: string; // hex of the function body bytecode (exactly what OP_DEFINE stores and the VM runs) + sourceMap: string; // frame-local source map (ips starting from 0) + sourceTags?: string; // frame-local semantic tags for opcodes (e.g. loop update/condition ranges) + source?: string; // full text of the defining file; absent means the function lives in the contract's own source file + sourceFile?: string; // originating file name for imported functions; absent means the contract's file + logs: readonly LogEntry[]; // frame-local log entries + requires: readonly RequireStatement[]; // frame-local require statements + inlineRanges?: string; // runs where inlined callables' bodies were emitted within this body (frame-local ips) +} + +export interface InlineRange { + startIp: number; // first ip of the emitted body (inclusive) + endIp: number; // last ip of the emitted body (inclusive) + frame: DebugFrame; // the inlined callable's debug frame } export interface LogEntry { @@ -48,6 +71,9 @@ export interface RequireStatement { message?: string; // custom message for failing `require` statement } +// Any debug entry that lives at an instruction pointer and attributes to a source line +export type DebugEntry = RequireStatement | LogEntry; + export interface Artifact { contractName: string; constructorInputs: readonly AbiInput[]; diff --git a/packages/utils/src/bitauth-script.ts b/packages/utils/src/bitauth-script.ts index 9cd3aab5e..1a137b7a8 100644 --- a/packages/utils/src/bitauth-script.ts +++ b/packages/utils/src/bitauth-script.ts @@ -1,60 +1,266 @@ -import { range } from './data.js'; -import { Script, scriptToBitAuthAsm } from './script.js'; +import { hexToBin } from '@bitauth/libauth'; +import { DebugFrame, DebugInformation } from './artifact.js'; +import { encodeInt, range } from './data.js'; +import { Op, Script, bytecodeToScript, scriptToBitAuthAsm } from './script.js'; import { parseSourceTags, sourceMapToLocationData } from './source-map.js'; -import { FullLocationData, PositionHint, SingleLocationData, SourceTagEntry, SourceTagKind } from './types.js'; +import { FullLocationData, LocationI, PositionHint, SingleLocationData, SourceTagEntry, SourceTagKind } from './types.js'; -export type LineToOpcodesMap = Record; -export type LineToAsmMap = Record; +export function formatBitAuthScript(debug: DebugInformation, sourceCode: string): string { + const sourceLines = sourceCode.split('\n'); -export function buildLineToOpcodesMap( - bytecode: Script, - sourceMapOrLocationData: string | FullLocationData, -): LineToOpcodesMap { - const locationData = typeof sourceMapOrLocationData === 'string' ? sourceMapToLocationData(sourceMapOrLocationData) : sourceMapOrLocationData; + const rows = walkScript({ + script: bytecodeToScript(hexToBin(debug.bytecode)), + sourceMap: debug.sourceMap, + sourceTags: debug.sourceTags, + functions: debug.functions, + sourceLines, + startLine: 1, + endLine: sourceLines.length, + asmIndent: '', + }); - return locationData.reduce((lineToOpcodeMap, singleLocation, index) => { - const opcode = bytecode[index]; - const line = getDisplayLine(singleLocation); + return renderRows(rows); +} - return { - ...lineToOpcodeMap, - [line]: [...(lineToOpcodeMap[line] || []), opcode], - }; - }, {}); +interface WalkParams { + script: Script; + sourceMap: string; + sourceTags?: string; + functions?: readonly DebugFrame[]; + sourceLines: string[]; + startLine: number; + endLine: number; + asmIndent: string; +} + +function walkScript(params: WalkParams): Row[] { + const segments = segmentScript(params); + return renderSegments(segments, params); +} + +interface LineGroupSegment { + kind: 'lineGroup'; + asm: string; + line: number; +} + +interface AnnotationSegment { + kind: 'annotation'; + asm: string; + comment: string; + insertAfterLine: number; +} + +interface FunctionDefinitionSegment { + kind: 'functionDefinition'; + frame: DebugFrame; + location: LocationI; } -export function buildLineToAsmMap(bytecode: Script, sourceMapOrLocationData: string | FullLocationData): LineToAsmMap { - const lineToOpcodesMap = buildLineToOpcodesMap(bytecode, sourceMapOrLocationData); +type Segment = LineGroupSegment | AnnotationSegment | FunctionDefinitionSegment; + +function segmentScript(params: WalkParams): Segment[] { + const { script, sourceLines } = params; + const locationData = sourceMapToLocationData(params.sourceMap); + const tags = parseSourceTags(params.sourceTags ?? ''); + // Only defined (OP_DEFINE'd) frames have a define site in the script; inlined callables are + // listed after them in the functions list purely as documentation and render nothing here + const frames = (params.functions ?? []).filter((frame) => frame.id !== undefined); + + const segments: Segment[] = []; + let index = 0; + + while (index < script.length) { + // Function definitions are always at the start of the script in sets of three: ` OP_DEFINE` per frame + if (index < frames.length * 3) { + segments.push({ kind: 'functionDefinition', frame: frames[index / 3], location: locationData[index].location }); + index += 3; + continue; + } + + const tag = findTagAt(tags, index); + if (tag) { + segments.push(annotationSegment(script, tag, tags, locationData, sourceLines)); + index = tag.endIndex + 1; + continue; + } + + const endIndex = findLineGroupEnd(script, index, locationData, tags); + segments.push({ kind: 'lineGroup', asm: scriptToBitAuthAsm(script.slice(index, endIndex)), line: getDisplayLine(locationData[index]) }); + index = endIndex; + } + + return segments; +} - return Object.fromEntries( - Object.entries(lineToOpcodesMap).map(([lineNumber, opcodeList]) => [lineNumber, scriptToBitAuthAsm(opcodeList)]), +function annotationSegment( + script: Script, + tag: SourceTagEntry, + tags: SourceTagEntry[], + locationData: FullLocationData, + sourceLines: string[], +): AnnotationSegment { + const { insertAfterLine, indent } = deriveAnchor(tag, tags, locationData, sourceLines); + + return { + kind: 'annotation', + asm: scriptToBitAuthAsm(script.slice(tag.startIndex, tag.endIndex + 1)), + comment: `${indent}${tagDescription(tag, locationData, sourceLines)}`, + insertAfterLine, + }; +} + +// A line group runs until the next opcode that belongs to a tagged range or maps to a different source line +function findLineGroupEnd(script: Script, start: number, locationData: FullLocationData, tags: SourceTagEntry[]): number { + const line = getDisplayLine(locationData[start]); + const nextBoundary = range(start + 1, script.length - 1).find((index) => ( + getDisplayLine(locationData[index]) !== line || findTagAt(tags, index) !== undefined + )); + + return nextBoundary ?? script.length; +} + +function findTagAt(tags: SourceTagEntry[], index: number): SourceTagEntry | undefined { + return tags.find((tag) => index >= tag.startIndex && index <= tag.endIndex); +} + +interface Row { + asm: string; + comment: string; +} + +interface RenderState { + rows: Row[]; + lastRenderedLine: number; // the last source line that has been rendered +} + +type RenderContext = WalkParams & { + functionSectionLines: Set; // source lines rendered inside function sections, never as filler +}; + +const BLANK_ROW: Row = { asm: '', comment: '' }; + +function renderSegments(segments: Segment[], params: WalkParams): Row[] { + const context: RenderContext = { + ...params, + functionSectionLines: deriveFunctionSectionLines(segments, params.sourceLines), + }; + + const initialState: RenderState = { rows: [], lastRenderedLine: params.startLine - 1 }; + const finalState = segments.reduce((state, segment) => renderSegment(state, segment, context), initialState); + + // After the last opcode, fill in the remaining source lines (e.g. the contract's closing braces) + return [...finalState.rows, ...fillerRows(finalState.lastRenderedLine, params.endLine, context)]; +} + +function deriveFunctionSectionLines(segments: Segment[], sourceLines: string[]): Set { + const localFunctionSegments = segments.filter( + (segment): segment is FunctionDefinitionSegment => segment.kind === 'functionDefinition' && segment.frame.source === undefined, ); + + return new Set(localFunctionSegments.flatMap(({ location }) => { + let lastLine = location.end.line; + while (lastLine < sourceLines.length && sourceLines[lastLine].trim() === '') lastLine += 1; + return range(location.start.line, lastLine); + })); } -export function formatBitAuthScript(bytecode: Script, sourceMap: string, sourceCode: string, sourceTags?: string): string { - const locationData = sourceMapToLocationData(sourceMap); - const sourceLines = sourceCode.split('\n'); +function renderSegment(state: RenderState, segment: Segment, context: RenderContext): RenderState { + if (segment.kind === 'functionDefinition') return renderFunctionDefinition(state, segment, context); + if (segment.kind === 'annotation') return renderAnnotation(state, segment, context); + return renderLineGroup(state, segment, context); +} + +// The group's row renders its own source line, after filling in the opcode-less lines above it +function renderLineGroup(state: RenderState, segment: LineGroupSegment, context: RenderContext): RenderState { + return { + rows: [ + ...state.rows, + ...fillerRows(state.lastRenderedLine, segment.line - 1, context), + { asm: context.asmIndent + segment.asm, comment: context.sourceLines[segment.line - 1] }, + ], + lastRenderedLine: advanceTo(state.lastRenderedLine, segment.line, context), + }; +} + +// The `>>>` annotation row lands right after its anchor line +function renderAnnotation(state: RenderState, segment: AnnotationSegment, context: RenderContext): RenderState { + return { + rows: [ + ...state.rows, + ...fillerRows(state.lastRenderedLine, segment.insertAfterLine, context), + { asm: context.asmIndent + segment.asm, comment: segment.comment }, + ], + lastRenderedLine: advanceTo(state.lastRenderedLine, segment.insertAfterLine, context), + }; +} - // Splice synthetic annotation lines (e.g. for-loop updates) into source and remap opcode lines - const insertions = buildInsertions(locationData, sourceLines, sourceTags); - const splicedSourceLines = spliceSyntheticSourceLines(sourceLines, insertions); - const splicedLocationData = updateLocationData(locationData, insertions); +function renderFunctionDefinition( + state: RenderState, + segment: FunctionDefinitionSegment, + context: RenderContext, +): RenderState { + const { frame, location } = segment; + const isImported = frame.sourceFile !== undefined; + const sourceLines = isImported ? frame.source!.split('\n') : context.sourceLines; + + const headerRows = isImported + ? [{ asm: '', comment: `>>> imported from ${frame.sourceFile}` }] + : []; + + return { + rows: [...state.rows, ...headerRows, ...buildFunctionSection(frame, location, sourceLines), BLANK_ROW], + lastRenderedLine: state.lastRenderedLine, + }; +} + +// Only called for defined frames (id present), which segmentScript filters on +function buildFunctionSection(frame: DebugFrame, location: LocationI, sourceLines: string[]): Row[] { + const bodyScript = bytecodeToScript(hexToBin(frame.bytecode)); + const defineAsm = scriptToBitAuthAsm([encodeInt(BigInt(frame.id!)), Op.OP_DEFINE]); + const { start, end } = location; + + if (end.line === start.line) { + const asm = ['<', scriptToBitAuthAsm(bodyScript), '>', defineAsm].filter((part) => part !== '').join(' '); + return [{ asm, comment: sourceLines[start.line - 1] }]; + } + + const bodyRows = walkScript({ + script: bodyScript, + sourceMap: frame.sourceMap, + sourceTags: frame.sourceTags, + sourceLines, + startLine: start.line + 1, + endLine: end.line - 1, + asmIndent: ' ', + }); - // Group opcodes by display line and convert to ASM - const lineToAsm = buildLineToAsmMap(bytecode, splicedLocationData); + return [ + { asm: '<', comment: sourceLines[start.line - 1] }, + ...bodyRows, + { asm: `> ${defineAsm}`, comment: sourceLines[end.line - 1] }, + ]; +} - // Format output - const escapedLines = splicedSourceLines.map(escapeCommentChars); - const maxAsmLen = Math.max(...escapedLines.map((_, i) => (lineToAsm[i + 1] || '').length)); - const maxSrcLen = Math.max(...escapedLines.map((l) => l.length)); +function fillerRows(afterLine: number, throughLine: number, context: RenderContext): Row[] { + return range(afterLine + 1, Math.min(throughLine, context.endLine)) + .filter((line) => !context.functionSectionLines.has(line)) + .map((line) => ({ asm: '', comment: context.sourceLines[line - 1] })); +} - return escapedLines.map((src, i) => { - const asm = lineToAsm[i + 1] || ''; - return `${asm.padEnd(maxAsmLen)} /* ${src.padEnd(maxSrcLen)} */`; - }).join('\n'); +function advanceTo(renderedLine: number, line: number, context: RenderContext): number { + return Math.max(renderedLine, Math.min(line, context.endLine)); } -// --- Helpers --- +function renderRows(rows: Row[]): string { + const escapedRows = rows.map((row) => ({ asm: row.asm, comment: escapeCommentChars(row.comment) })); + const maxAsmLength = Math.max(...escapedRows.map((row) => row.asm.length)); + const maxCommentLength = Math.max(...escapedRows.map((row) => row.comment.length)); + + return escapedRows + .map((row) => `${row.asm.padEnd(maxAsmLength)} /* ${row.comment.padEnd(maxCommentLength)} */`) + .join('\n'); +} function getDisplayLine(singleLocation: SingleLocationData): number { const { location, positionHint } = singleLocation; @@ -65,16 +271,6 @@ function escapeCommentChars(text: string): string { return text.replaceAll('/*', '\\/*').replaceAll('*/', '*\\/'); } -// --- Source tag handling (for-loop update annotations) --- - -interface Insertion { - insertAfterLine: number; - annotation: string; - startIndex: number; - endIndex: number; -} - -// Where a synthetic annotation line is spliced, and the indentation it's rendered with. interface Anchor { insertAfterLine: number; indent: string; @@ -92,20 +288,6 @@ const EPILOGUE_KINDS = [ SourceTagKind.LOOP_CONDITION, ]; -function buildInsertions( - locationData: FullLocationData, - sourceLines: string[], - sourceTags?: string, -): Insertion[] { - const tags = (sourceTags ? parseSourceTags(sourceTags) : []); - - return tags.map((tag) => { - const { insertAfterLine, indent } = deriveAnchor(tag, tags, locationData, sourceLines); - const annotation = `${indent}${tagDescription(tag, locationData, sourceLines)}`; - return { insertAfterLine, annotation, startIndex: tag.startIndex, endIndex: tag.endIndex }; - }); -} - function deriveAnchor( tag: SourceTagEntry, tags: SourceTagEntry[], @@ -120,15 +302,15 @@ function deriveAnchor( const firstBodyOpcode = lastPrologueOpcode + 1; const firstBodyLine = getDisplayLine(locationData[firstBodyOpcode]); + // `insertAfterLine` of `firstBodyLine - 1` lands all the prologue annotations directly above the + // first body statement, at its indentation. return { - // `insertAfterLine` splices *after* a line, so `firstBodyLine - 1` lands all the prologue - // annotations directly above the first body statement, at its indentation. insertAfterLine: firstBodyLine - 1, indent: lineIndent(sourceLines, firstBodyLine), }; } - // Scope cleanup and loop-back condition tags always get inserted right before the scope's closing brace. + // Scope cleanup and loop-back condition tags always land right before the scope's closing brace. if (EPILOGUE_KINDS.includes(tag.kind)) { const braceLine = getDisplayLine(locationData[tag.startIndex]); return { @@ -143,40 +325,6 @@ function deriveAnchor( }; } -function spliceSyntheticSourceLines(sourceLines: string[], insertions: Insertion[]): string[] { - return insertions.reduceRight( - (lines, ins) => [...lines.slice(0, ins.insertAfterLine), ins.annotation, ...lines.slice(ins.insertAfterLine)], - sourceLines, - ); -} - -function updateLocationData(locationData: FullLocationData, insertions: Insertion[]): FullLocationData { - return insertions.reduceRight((location, insertion) => { - return location.map((entry, opcodeIndex) => { - const currentLineNumber = getDisplayLine(location[opcodeIndex]); - const updatedLineNumber = getUpdatedLineNumber(currentLineNumber, insertion, opcodeIndex); - if (updatedLineNumber === currentLineNumber) return entry; - - return { - location: { - start: { line: updatedLineNumber, column: 0 }, - end: { line: updatedLineNumber, column: 0 }, - }, - positionHint: PositionHint.START, - }; - }); - }, locationData); -} - -const getUpdatedLineNumber = (currentLineNumber: number, insertion: Insertion, opcodeIndex: number): number => { - const newLineNumber = insertion.insertAfterLine + 1; - const inTagRange = opcodeIndex >= insertion.startIndex && opcodeIndex <= insertion.endIndex; - - if (inTagRange) return newLineNumber; - if (currentLineNumber > insertion.insertAfterLine) return currentLineNumber + 1; - return currentLineNumber; -}; - // e.g. ">>> for-loop update (i = i + 1)" function tagDescription(tag: SourceTagEntry, locationData: FullLocationData, sourceLines: string[]): string { switch (tag.kind) { diff --git a/packages/utils/src/cashproof-optimisations.ts b/packages/utils/src/cashproof-optimisations.ts deleted file mode 100644 index 59f64d040..000000000 --- a/packages/utils/src/cashproof-optimisations.ts +++ /dev/null @@ -1,136 +0,0 @@ -export default ` -# This file can be run with CashProof to prove that the optimisations preserve exact functionality -# This includes most of CashScript's bytecode optimisations, although some are incompatible with CashProof - -# Hardcoded arithmetic -# OP_NOT OP_IF <=> OP_NOTIF; -OP_1 OP_ADD <=> OP_1ADD; -OP_1 OP_SUB <=> OP_1SUB; -OP_1 OP_NEGATE <=> OP_1NEGATE; -OP_0 OP_NUMEQUAL OP_NOT <=> OP_0NOTEQUAL; -OP_NUMEQUAL OP_NOT <=> OP_NUMNOTEQUAL; -OP_SHA256 OP_SHA256 <=> OP_HASH256; -OP_SHA256 OP_RIPEMD160 <=> OP_HASH160; - -# Hardcoded stack ops -OP_2 OP_PICK OP_1 OP_PICK OP_3 OP_PICK <=> OP_3DUP OP_SWAP; -OP_2 OP_PICK OP_2 OP_PICK OP_2 OP_PICK <=> OP_3DUP; - -OP_0 OP_PICK OP_2 OP_PICK <=> OP_2DUP OP_SWAP; -OP_2 OP_PICK OP_4 OP_PICK <=> OP_2OVER OP_SWAP; -OP_3 OP_PICK OP_3 OP_PICK <=> OP_2OVER; - -OP_2 OP_ROLL OP_3 OP_ROLL <=> OP_2SWAP OP_SWAP; -OP_3 OP_ROLL OP_3 OP_ROLL <=> OP_2SWAP; -OP_4 OP_ROLL OP_5 OP_ROLL <=> OP_2ROT OP_SWAP; -OP_5 OP_ROLL OP_5 OP_ROLL <=> OP_2ROT; - -OP_0 OP_PICK <=> OP_DUP; -OP_1 OP_PICK <=> OP_OVER; -OP_0 OP_ROLL <=> ; -OP_1 OP_ROLL <=> OP_SWAP; -OP_2 OP_ROLL <=> OP_ROT; -OP_DROP OP_DROP <=> OP_2DROP; - -# Secondary effects -OP_DUP OP_SWAP <=> OP_DUP; -OP_SWAP OP_SWAP <=> ; -OP_2SWAP OP_2SWAP <=> ; -OP_ROT OP_ROT OP_ROT <=> ; -OP_2ROT OP_2ROT OP_2ROT <=> ; -OP_OVER OP_OVER <=> OP_2DUP; -OP_DUP OP_DROP <=> ; -OP_DUP OP_NIP <=> ; - -# Enabling secondary effects -OP_DUP OP_OVER <=> OP_DUP OP_DUP; - -# Merge OP_VERIFY -OP_EQUAL OP_VERIFY <=> OP_EQUALVERIFY; -OP_NUMEQUAL OP_VERIFY <=> OP_NUMEQUALVERIFY; -OP_CHECKSIG OP_VERIFY <=> OP_CHECKSIGVERIFY; -# OP_CHECKMULTISIG OP_VERIFY <=> OP_CHECKMULTISIGVERIFY; -OP_CHECKDATASIG OP_VERIFY <=> OP_CHECKDATASIGVERIFY; - -# Remove/replace extraneous OP_SWAP -# OP_SWAP OP_AND <=> OP_AND; -# OP_SWAP OP_OR <=> OP_OR; -# OP_SWAP OP_XOR <=> OP_XOR; -OP_SWAP OP_ADD <=> OP_ADD; -OP_SWAP OP_EQUAL <=> OP_EQUAL; -OP_SWAP OP_NUMEQUAL <=> OP_NUMEQUAL; -OP_SWAP OP_NUMNOTEQUAL <=> OP_NUMNOTEQUAL; -OP_SWAP OP_GREATERTHANOREQUAL <=> OP_LESSTHANOREQUAL; -OP_SWAP OP_LESSTHANOREQUAL <=> OP_GREATERTHANOREQUAL; -OP_SWAP OP_GREATERTHAN <=> OP_LESSTHAN; -OP_SWAP OP_LESSTHAN <=> OP_GREATERTHAN; -OP_SWAP OP_DROP <=> OP_NIP; -OP_SWAP OP_NIP <=> OP_DROP; - -# Remove/replace extraneous OP_DUP -# OP_DUP OP_AND <=> ; -# OP_DUP OP_OR <=> ; -OP_DUP OP_DROP <=> ; -OP_DUP OP_NIP <=> ; - -# Random optimisations (don't know what I'm targeting with this) -OP_2DUP OP_DROP <=> OP_OVER; -OP_2DUP OP_NIP <=> OP_DUP; -OP_CAT OP_DROP <=> OP_2DROP; -OP_NIP OP_DROP <=> OP_2DROP; - -# Far-fetched stuff -OP_DUP OP_ROT OP_SWAP OP_DROP <=> OP_SWAP; -OP_OVER OP_ROT OP_SWAP OP_DROP <=> OP_SWAP; -OP_2 OP_PICK OP_ROT OP_SWAP OP_DROP <=> OP_SWAP; -OP_3 OP_PICK OP_ROT OP_SWAP OP_DROP <=> OP_SWAP; -OP_4 OP_PICK OP_ROT OP_SWAP OP_DROP <=> OP_SWAP; -OP_5 OP_PICK OP_ROT OP_SWAP OP_DROP <=> OP_SWAP; -OP_6 OP_PICK OP_ROT OP_SWAP OP_DROP <=> OP_SWAP; -OP_7 OP_PICK OP_ROT OP_SWAP OP_DROP <=> OP_SWAP; -OP_8 OP_PICK OP_ROT OP_SWAP OP_DROP <=> OP_SWAP; -OP_9 OP_PICK OP_ROT OP_SWAP OP_DROP <=> OP_SWAP; -OP_10 OP_PICK OP_ROT OP_SWAP OP_DROP <=> OP_SWAP; -OP_11 OP_PICK OP_ROT OP_SWAP OP_DROP <=> OP_SWAP; -OP_12 OP_PICK OP_ROT OP_SWAP OP_DROP <=> OP_SWAP; -OP_13 OP_PICK OP_ROT OP_SWAP OP_DROP <=> OP_SWAP; -OP_14 OP_PICK OP_ROT OP_SWAP OP_DROP <=> OP_SWAP; -OP_15 OP_PICK OP_ROT OP_SWAP OP_DROP <=> OP_SWAP; -OP_16 OP_PICK OP_ROT OP_SWAP OP_DROP <=> OP_SWAP; - -OP_DUP OP_ROT OP_DROP <=> OP_NIP OP_DUP; -OP_OVER OP_ROT OP_DROP <=> OP_SWAP; -OP_2 OP_PICK OP_ROT OP_DROP <=> OP_NIP OP_OVER; - -OP_0 OP_NIP <=> OP_DROP OP_0; -OP_1 OP_NIP <=> OP_DROP OP_1; -OP_2 OP_NIP <=> OP_DROP OP_2; -OP_3 OP_NIP <=> OP_DROP OP_3; -OP_4 OP_NIP <=> OP_DROP OP_4; -OP_5 OP_NIP <=> OP_DROP OP_5; -OP_6 OP_NIP <=> OP_DROP OP_6; -OP_7 OP_NIP <=> OP_DROP OP_7; -OP_8 OP_NIP <=> OP_DROP OP_8; -OP_9 OP_NIP <=> OP_DROP OP_9; -OP_10 OP_NIP <=> OP_DROP OP_10; -OP_11 OP_NIP <=> OP_DROP OP_11; -OP_12 OP_NIP <=> OP_DROP OP_12; -OP_13 OP_NIP <=> OP_DROP OP_13; -OP_14 OP_NIP <=> OP_DROP OP_14; -OP_15 OP_NIP <=> OP_DROP OP_15; -OP_16 OP_NIP <=> OP_DROP OP_16; - -OP_2 OP_PICK OP_SWAP OP_2 OP_PICK OP_NIP <=> OP_DROP OP_2DUP; - -# .slice(0, x) optimisation & .slice(x, y.length) optimisation -OP_0 OP_SPLIT OP_NIP <=> ; -OP_SIZE OP_SPLIT OP_DROP <=> ; - -# These are new optimisations that we cannot prove since CashProof doesn't work any more -# ////////////////////////////////////////////////////////////////////////////////////// - -OP_LESSTHAN OP_NOT <=> OP_GREATERTHANOREQUAL; -OP_GREATERTHAN OP_NOT <=> OP_LESSTHANOREQUAL; -OP_LESSTHANOREQUAL OP_NOT <=> OP_GREATERTHAN; -OP_GREATERTHANOREQUAL OP_NOT <=> OP_LESSTHAN; -`; diff --git a/packages/utils/src/optimisations.ts b/packages/utils/src/optimisations.ts index 651a682e3..fb52c677c 100644 --- a/packages/utils/src/optimisations.ts +++ b/packages/utils/src/optimisations.ts @@ -1,4 +1,6 @@ -const provableOptimisations = [ +// Note: the order in which these optimisations are applied can impact the output, so entries should +// not be reordered without carefully verifying the compiled bytecode of existing contracts. +export const optimisationReplacements = [ // Hardcoded arithmetic ['OP_1 OP_ADD', 'OP_1ADD'], ['OP_1 OP_SUB', 'OP_1SUB'], @@ -49,6 +51,7 @@ const provableOptimisations = [ // Remove/replace extraneous OP_SWAP ['OP_SWAP OP_ADD', 'OP_ADD'], + ['OP_SWAP OP_MUL', 'OP_MUL'], // This was added to keep the old behaviour while explicitly disallowing partial matches in the optimisation regex ['OP_SWAP OP_EQUALVERIFY', 'OP_EQUALVERIFY'], ['OP_SWAP OP_EQUAL', 'OP_EQUAL'], @@ -118,39 +121,36 @@ const provableOptimisations = [ // .slice(0, x) optimisation & .slice(x, y.length) optimisation ['OP_0 OP_SPLIT OP_NIP', ''], ['OP_SIZE OP_SPLIT OP_DROP', ''], -] as [string, string][]; -const unprovableOptimisations = [ // Hardcoded arithmetic - // CashProof can't prove OP_IF without parameters ['OP_NOT OP_IF', 'OP_NOTIF'], + // Merge OP_VERIFY - // CashProof can't prove OP_CHECKMULTISIG without specifying N ['OP_CHECKMULTISIG OP_VERIFY', 'OP_CHECKMULTISIGVERIFY'], + // Remove/replace extraneous OP_SWAP - // CashProof can't prove bitwise operators ['OP_SWAP OP_AND', 'OP_AND'], ['OP_SWAP OP_OR', 'OP_OR'], ['OP_SWAP OP_XOR', 'OP_XOR'], // Remove/replace extraneous OP_DUP - // CashProof can't prove bitwise operators ['OP_DUP OP_AND', ''], ['OP_DUP OP_OR', ''], - // These are new optimisations that we cannot prove since CashProof doesn't work any more - // ////////////////////////////////////////////////////////////////////////////////////// - - // TODO: Enable this optimisation when we overhaul the type system - // (right now bool(4) == true => false, but !!bool(4) == true => true) so can't replace OP_NOT OP_NOT with '' - // ['OP_NOT OP_NOT', ''] - + // Invert comparison operators instead of negating them ['OP_LESSTHAN OP_NOT', 'OP_GREATERTHANOREQUAL'], ['OP_GREATERTHAN OP_NOT', 'OP_LESSTHANOREQUAL'], ['OP_LESSTHANOREQUAL OP_NOT', 'OP_GREATERTHAN'], ['OP_GREATERTHANOREQUAL OP_NOT', 'OP_LESSTHAN'], -] as [string, string][]; -// Note: we moved these optimisations into a single file, but kept the exact same order as before, -// because the order in which optimisations are applied can impact the output. -export const optimisationReplacements = [...provableOptimisations, ...unprovableOptimisations]; + // This can get emitted by tuple destructuring + ['OP_TOALTSTACK OP_FROMALTSTACK', ''], + + // unsafe_bool(4) == true => false, but !!unsafe_bool(4) == true => true) so we can't replace OP_NOT OP_NOT with '' + // in the general case, but when it is followed by a consuming instruction that does not differentiate between + // true and truthy values (e.g. OP_IF, OP_UNTIL, OP_VERIFY), we can replace OP_NOT OP_NOT with '' + // Note that technically OP_NOT OP_NOT would also do a VM-number check, which gets removed by this optimisation + ['OP_NOT OP_NOT OP_UNTIL', 'OP_UNTIL'], + ['OP_NOT OP_NOTIF', 'OP_IF'], + ['OP_NOT OP_NOT OP_VERIFY', 'OP_VERIFY'], +] as [string, string][]; diff --git a/packages/utils/src/script.ts b/packages/utils/src/script.ts index c5892e5ce..cb6077716 100644 --- a/packages/utils/src/script.ts +++ b/packages/utils/src/script.ts @@ -8,11 +8,10 @@ import { OpcodesBch, AuthenticationInstruction, } from '@bitauth/libauth'; -import OptimisationsEquivFile from './cashproof-optimisations.js'; import { optimisationReplacements } from './optimisations.js'; import { range } from './data.js'; import { FullLocationData, PositionHint, SingleLocationData, SourceTagEntry, SourceTagKind } from './types.js'; -import { LogEntry, RequireStatement } from './artifact.js'; +import { InlineRange, LogEntry, RequireStatement } from './artifact.js'; export const Op = OpcodesBch; export type Op = number; @@ -154,12 +153,13 @@ export function generateContractBytecodeScript(baseScript: Script, encodedConstr return [...encodedConstructorArgs.slice().reverse(), ...baseScript]; } -interface OptimiseBytecodeResult { +export interface OptimiseBytecodeResult { script: Script; locationData: FullLocationData; logs: LogEntry[]; requires: RequireStatement[]; sourceTags: SourceTagEntry[]; + inlineRanges: InlineRange[]; } export function optimiseBytecode( @@ -168,30 +168,24 @@ export function optimiseBytecode( logs: LogEntry[], requires: RequireStatement[], sourceTags: SourceTagEntry[], + inlineRanges: InlineRange[], constructorParamLength: number, runs: number = 1000, ): OptimiseBytecodeResult { for (let i = 0; i < runs; i += 1) { - const oldScript = script; - const { - script: newScript, - locationData: newLocationData, - logs: newLogs, - requires: newRequires, - sourceTags: newSourceTags, - } = replaceOps(script, locationData, logs, requires, sourceTags, constructorParamLength, optimisationReplacements); + const result = replaceOps( + script, locationData, logs, requires, sourceTags, inlineRanges, constructorParamLength, optimisationReplacements, + ); // Break on fixed point - if (scriptToAsm(oldScript) === scriptToAsm(newScript)) break; + if (!result.changed) break; - script = newScript; - locationData = newLocationData; - logs = newLogs; - requires = newRequires; - sourceTags = newSourceTags; + ({ script, locationData, logs, requires, sourceTags, inlineRanges } = result); } - return { script, locationData, logs, requires, sourceTags: reconcileScopeCleanupTags(script, sourceTags) }; + return { + script, locationData, logs, requires, sourceTags: reconcileScopeCleanupTags(script, sourceTags), inlineRanges, + }; } const SCOPE_CLEANUP_OPCODES = [Op.OP_DROP, Op.OP_NIP, Op.OP_2DROP]; @@ -214,63 +208,8 @@ function reconcileScopeCleanupTags(script: Script, sourceTags: SourceTagEntry[]) }); } -export function optimiseBytecodeOld(script: Script, runs: number = 1000): Script { - const optimisations = OptimisationsEquivFile - // Split by line and filter all line comments (#) - .split('\n') - .map((equiv) => equiv.trim()) - .filter((equiv) => !equiv.startsWith('#')) - // Join back the lines, and split on semicolon - .join('') - .split(';') - // Parse all optimisations in .equiv file - .map((equiv) => equiv.trim()) - .map((equiv) => equiv.split('<=>').map((part) => part.trim())) - .filter((equiv) => equiv.length === 2); - - for (let i = 0; i < runs; i += 1) { - const oldScript = script; - script = replaceOpsOld(script, optimisations); - - // Break on fixed point - if (scriptToAsm(oldScript) === scriptToAsm(script)) break; - } - - return script; -} - -function replaceOpsOld(script: Script, optimisations: string[][]): Script { - let asm = scriptToAsm(script); - - // Apply all optimisations in the cashproof file - optimisations.forEach(([pattern, replacement]) => { - asm = asm.replace(new RegExp(pattern, 'g'), replacement); - }); - - // Add optimisations that are not compatible with CashProof - // CashProof can't prove OP_IF without parameters - asm = asm.replace(/OP_NOT OP_IF/g, 'OP_NOTIF'); - // CashProof can't prove OP_CHECKMULTISIG without specifying N - asm = asm.replace(/OP_CHECKMULTISIG OP_VERIFY/g, 'OP_CHECKMULTISIGVERIFY'); - // CashProof can't prove bitwise operators - asm = asm.replace(/OP_SWAP OP_AND/g, 'OP_AND'); - asm = asm.replace(/OP_SWAP OP_OR/g, 'OP_OR'); - asm = asm.replace(/OP_SWAP OP_XOR/g, 'OP_XOR'); - asm = asm.replace(/OP_DUP OP_AND/g, ''); - asm = asm.replace(/OP_DUP OP_OR/g, ''); - - // Remove any double spaces as a result of opcode removal - asm = asm.replace(/\s+/g, ' ').trim(); - - return asmToScript(asm); -} - -interface ReplaceOpsResult { - script: Script; - locationData: FullLocationData; - logs: LogEntry[]; - requires: RequireStatement[]; - sourceTags: SourceTagEntry[]; +interface ReplaceOpsResult extends OptimiseBytecodeResult { + changed: boolean; } function replaceOps( @@ -279,33 +218,42 @@ function replaceOps( logs: LogEntry[], requires: RequireStatement[], sourceTags: SourceTagEntry[], + inlineRanges: InlineRange[], constructorParamLength: number, - optimisations: string[][], + optimisations: [string, string][], ): ReplaceOpsResult { - let asm = scriptToAsm(script); - let newLocationData = [...locationData]; + const originalAsm = scriptToAsm(script); + let asm = originalAsm; + const newLocationData = [...locationData]; let newLogs = [...logs]; let newRequires = [...requires]; let newSourceTags = [...sourceTags]; + let newInlineRanges = [...inlineRanges]; optimisations.forEach(([pattern, replacement]) => { - let processedAsm = ''; - let asmToSearch = asm; - - // We add a space or end of string to the end of the pattern to ensure that we match the whole pattern - // (no partial matches) - const regex = new RegExp(`${pattern}(\\s|$)`, 'g'); - - let matchIndex = asmToSearch.search(regex); - while (matchIndex !== -1) { - // We add the part before the match to the processed asm - processedAsm = mergeAsm(processedAsm, asmToSearch.slice(0, matchIndex)); - - // We count the number of spaces in the processed asm + 1, which is equal to the script index - // We do the same thing to calculate the number of opcodes in the pattern and replacement - const scriptIndex = processedAsm === '' ? 0 : [...processedAsm.matchAll(/\s+/g)].length + 1; - const patternLength = [...pattern.matchAll(/\s+/g)].length + 1; - const replacementLength = replacement === '' ? 0 : [...replacement.matchAll(/\s+/g)].length + 1; + const patternTokens = pattern.split(/\s+/); + const patternLength = patternTokens.length; + const replacementLength = replacement === '' ? 0 : replacement.split(/\s+/).length; + const lengthDiff = patternLength - replacementLength; + + // (?=\s|$) requires the pattern to end at a token boundary (no partial matches) without consuming the separator + const regex = new RegExp(`${pattern}(?=\\s|$)`, 'g'); + + // Most rules match nothing on any given script, and must leave the ASM untouched. + const matches = [...asm.matchAll(regex)]; + if (matches.length === 0) return; + + // Make a mapping *once* that maps the character offset of every opcode/token to its script index + const scriptIndexAtCharacterOffset = new Map(); + asm.split(' ').reduce((characterOffset, token, scriptIndex) => { + scriptIndexAtCharacterOffset.set(characterOffset, scriptIndex); + return characterOffset + token.length + 1; + }, 0); + + // Process the matches right to left: replacing a pattern only shifts the metadata positions + // that come after it, so the indices of the remaining (earlier) matches stay valid as-is. + for (const match of matches.reverse()) { + const scriptIndex = scriptIndexAtCharacterOffset.get(match.index)!; // We get the locationData entries for every opcode in the pattern const patternLocations = newLocationData.slice(scriptIndex, scriptIndex + patternLength); @@ -336,31 +284,25 @@ function replaceOps( const replacementLocations = new Array(replacementLength).fill(mergedLocation); newLocationData.splice(scriptIndex, patternLength, ...replacementLocations); - const lengthDiff = patternLength - replacementLength; // 2 or 1 - // The IP of an opcode in the script is its index within the script + the constructor parameters, because // the constructor parameters still have to get added to the front of the script when a new Contract is created. const scriptIp = scriptIndex + constructorParamLength; - newRequires = newRequires.map((require) => { - // We calculate the new ip of the require by subtracting the length diff between the matched pattern and replacement - const newCalculatedRequireIp = require.ip - lengthDiff; + // Positions after the replaced pattern shift back by the length difference; positions inside + // the replaced pattern clamp to the pattern's start. (Positions inside a pattern are impossible + // for the current set of optimisations, but the clamp future-proofs the code.) + const adjustPosition = (position: number, patternStart: number): number => ( + position >= patternStart ? Math.max(patternStart, position - lengthDiff) : position + ); - return { - ...require, - // If the require is within the pattern, we want to make sure that the new ip is at least the scriptIp - // Note that this is impossible for the current set of optimisations, but future proofs the code - ip: require.ip >= scriptIp ? Math.max(scriptIp, newCalculatedRequireIp) : require.ip, - }; - }); + newRequires = newRequires.map((require) => ({ + ...require, + ip: adjustPosition(require.ip, scriptIp), + })); newLogs = newLogs.map((log) => { - // We calculate the new ip of the log by subtracting the length diff between the matched pattern and replacement - const newCalculatedLogIp = log.ip - lengthDiff; - return { - // If the log is within the pattern, we want to make sure that the new ip is at least the scriptIp - ip: log.ip >= scriptIp ? Math.max(scriptIp, newCalculatedLogIp) : log.ip, + ip: adjustPosition(log.ip, scriptIp), line: log.line, data: log.data.map((data) => { if (typeof data === 'string') return data; @@ -375,7 +317,7 @@ function replaceOps( } const addedTransformationsCount = data.ip - scriptIp; - const addedTransformations = [...pattern.split(/\s+/g)].slice(0, addedTransformationsCount).join(' '); + const addedTransformations = patternTokens.slice(0, addedTransformationsCount).join(' '); const newTransformations = data.transformations ? `${addedTransformations} ${data.transformations}` : addedTransformations; return { @@ -387,38 +329,33 @@ function replaceOps( }; }); - // Source tags use raw script indices (no constructor offset), so we adjust using scriptIndex directly + // Source tags use raw script indices (no constructor offset), so they adjust against scriptIndex newSourceTags = newSourceTags.map((tag) => ({ ...tag, - startIndex: tag.startIndex >= scriptIndex ? Math.max(scriptIndex, tag.startIndex - lengthDiff) : tag.startIndex, - endIndex: tag.endIndex >= scriptIndex ? Math.max(scriptIndex, tag.endIndex - lengthDiff) : tag.endIndex, + startIndex: adjustPosition(tag.startIndex, scriptIndex), + endIndex: adjustPosition(tag.endIndex, scriptIndex), })); - // We add the replacement to the processed asm - processedAsm = mergeAsm(processedAsm, replacement); - - // We do not add the matched pattern anywhere since it gets replaced - - // We set the asmToSearch to the part after the match - asmToSearch = asmToSearch.slice(matchIndex + pattern.length).trim(); + // Inline ranges use ip coordinates (like requires), so both bounds adjust against scriptIp + newInlineRanges = newInlineRanges.map((inlineRange) => ({ + ...inlineRange, + startIp: adjustPosition(inlineRange.startIp, scriptIp), + endIp: adjustPosition(inlineRange.endIp, scriptIp), + })); - // Find the next match - matchIndex = asmToSearch.search(regex); } - // We add the remaining asm to the processed asm - processedAsm = mergeAsm(processedAsm, asmToSearch); - - // We replace the original asm with the processed asm so that the next optimisation can use the updated asm - asm = processedAsm; + asm = asm.replace(regex, replacement).replace(/\s+/g, ' ').trim(); }); return { script: asmToScript(asm), + changed: asm !== originalAsm, locationData: newLocationData, logs: newLogs, requires: newRequires, sourceTags: newSourceTags, + inlineRanges: newInlineRanges, }; } @@ -453,9 +390,3 @@ const getLowestStartLocation = (locations: SingleLocationData[]): SingleLocation return lowest; }, locations[0]); }; - -const mergeAsm = (asm1: string, asm2: string): string => { - // We merge two ASM strings by adding a space between them, and removing any duplicate spaces - // or trailing/leading spaces, which might have been introduced due to regex matching / replacements / empty asm strings - return `${asm1} ${asm2}`.replace(/\s+/g, ' ').trim(); -}; diff --git a/packages/utils/src/source-map.ts b/packages/utils/src/source-map.ts index 3f7e883c9..e1a4720c1 100644 --- a/packages/utils/src/source-map.ts +++ b/packages/utils/src/source-map.ts @@ -1,4 +1,5 @@ import { FullLocationData, PositionHint, SingleLocationData, SourceTagEntry, SourceTagKind } from './types.js'; +import { DebugFrame, InlineRange } from './artifact.js'; /* * The source mappings for the bytecode use the following notation (similar to Solidity): @@ -141,3 +142,34 @@ export function generateSourceTags(entries: SourceTagEntry[]): string { if (entries.length === 0) return ''; return entries.map((entry) => `${entry.startIndex}:${entry.endIndex}:${entry.kind}`).join(';'); } + +/* + * Format: "startIp:endIp:name;..." — the runs where inlined callables' bodies were emitted within + * one program, referencing each callable's debug frame by its (globally unique) name + */ +export function generateInlineRanges(entries: InlineRange[]): string { + return entries + .filter((entry) => entry.endIp >= entry.startIp) // a body optimised down to nothing leaves no run + .map((entry) => `${entry.startIp}:${entry.endIp}:${entry.frame.name}`) + .join(';'); +} + +export function parseAndResolveInlineRanges( + inlineRanges?: string, + frames?: readonly DebugFrame[], +): InlineRange[] { + if (!inlineRanges || !frames) return []; + + return parseInlineRanges(inlineRanges).flatMap(({ startIp, endIp, frameName }) => { + const frame = frames.find((candidate) => candidate.name === frameName); + return frame ? [{ startIp, endIp, frame }] : []; + }); +} + +function parseInlineRanges(inlineRanges: string): { startIp: number, endIp: number, frameName: string }[] { + if (!inlineRanges) return []; + return inlineRanges.split(';').map((segment) => { + const [startStr, endStr, frameName] = segment.split(':'); + return { startIp: Number(startStr), endIp: Number(endStr), frameName }; + }); +} diff --git a/packages/utils/src/types.ts b/packages/utils/src/types.ts index 3215217dc..a36821c79 100644 --- a/packages/utils/src/types.ts +++ b/packages/utils/src/types.ts @@ -28,12 +28,11 @@ export class BytesType { export class TupleType { constructor( - public leftType: Type, - public rightType: Type, + public elementTypes: Type[], ) { } toString(): string { - return `(${this.leftType}, ${this.rightType})`; + return `(${this.elementTypes.join(', ')})`; } } @@ -46,6 +45,7 @@ export enum PrimitiveType { SIG = 'sig', DATASIG = 'datasig', ANY = 'any', + VOID = 'void', } const ExplicitlyCastableTo: { [key in PrimitiveType]: PrimitiveType[] } = { @@ -56,11 +56,15 @@ const ExplicitlyCastableTo: { [key in PrimitiveType]: PrimitiveType[] } = { [PrimitiveType.SIG]: [PrimitiveType.SIG], [PrimitiveType.DATASIG]: [PrimitiveType.DATASIG], [PrimitiveType.ANY]: [], + [PrimitiveType.VOID]: [], }; export function explicitlyCastable(from?: Type, to?: Type): boolean { if (!from || !to) return false; + // `void` is not a real value type, so it can never participate in a cast + if (from === PrimitiveType.VOID || to === PrimitiveType.VOID) return false; + // Tuples can't be cast if (from instanceof TupleType || to instanceof TupleType) return false; @@ -111,10 +115,12 @@ export function explicitlyCastable(from?: Type, to?: Type): boolean { export function implicitlyCastable(actual?: Type, expected?: Type): boolean { if (!actual || !expected) return false; + // `void` is not a real value type, so it can never be assigned to or from (not even to `any`) + if (actual === PrimitiveType.VOID || expected === PrimitiveType.VOID) return false; + if (actual instanceof TupleType && expected instanceof TupleType) { - const leftIsCompatible = implicitlyCastable(actual.leftType, expected.leftType); - const rightIsCompatible = implicitlyCastable(actual.rightType, expected.rightType); - return leftIsCompatible && rightIsCompatible; + return actual.elementTypes.length === expected.elementTypes.length + && actual.elementTypes.every((elementType, i) => implicitlyCastable(elementType, expected.elementTypes[i])); } // Can't cast between Tuple and non-Tuple diff --git a/packages/utils/test/bitauth-script.test.ts b/packages/utils/test/bitauth-script.test.ts index 97d59b241..a4be46d02 100644 --- a/packages/utils/test/bitauth-script.test.ts +++ b/packages/utils/test/bitauth-script.test.ts @@ -1,7 +1,9 @@ -import { asmToScript } from '../src/script.js'; -import { buildLineToAsmMap, formatBitAuthScript } from '../src/bitauth-script.js'; -import { fixtures } from './fixtures/bitauth-script.fixture.js'; -import { compileString } from 'cashc'; +import { binToHex, createCompilerBch } from '@bitauth/libauth'; +import { Artifact } from '../src/artifact.js'; +import { asmToScript, scriptToBytecode } from '../src/script.js'; +import { formatBitAuthScript } from '../src/bitauth-script.js'; +import { FunctionFixture, fixtures, functionFixtures } from './fixtures/bitauth-script.fixture.js'; +import { compileFile, compileString } from 'cashc/dist/internal.js'; describe('Libauth Script formatting', () => { fixtures.forEach((fixture) => { @@ -16,17 +18,68 @@ describe('Libauth Script formatting', () => { expect(artifact.bytecode).toEqual(fixture.asmBytecode); }); - it('should build a line-to-asm map', () => { - expect(buildLineToAsmMap(scriptBytecode, fixture.sourceMap)).toEqual(fixture.expectedLineToAsmMap); - }); - it('should format script as debugging output for BitAuth IDE', () => { const expectedBitAuthScript = fixture.expectedBitAuthScript.replace(/^\n+/, '').replace(/\n+$/, ''); - const formattedBitAuthScript = formatBitAuthScript( - scriptBytecode, fixture.sourceMap, fixture.sourceCode, fixture.sourceTags, - ); + const debugInformation = { + bytecode: binToHex(scriptToBytecode(scriptBytecode)), + sourceMap: fixture.sourceMap, + logs: [], + requires: [], + ...(fixture.sourceTags ? { sourceTags: fixture.sourceTags } : {}), + }; + const formattedBitAuthScript = formatBitAuthScript(debugInformation, fixture.sourceCode); expect(formattedBitAuthScript).toBe(expectedBitAuthScript); + expectBitAuthScriptToCompileTo(formattedBitAuthScript, debugInformation.bytecode); + }); + }); + }); + + describe('Bytecode order preservation', () => { + it('should emit opcodes in bytecode order even for non-monotonic source maps', () => { + // OP_3 maps back to line 3 after line-4 opcodes — grouping by source line would reorder execution. + // The formatted output is executed, so bytecode order must win over source-line grouping. + const scriptBytecode = asmToScript('OP_1 OP_2 OP_ADD OP_3 OP_NUMEQUAL'); + const debugInformation = { + bytecode: binToHex(scriptToBytecode(scriptBytecode)), + sourceMap: '3:8:3:9;4:8:4:9;:12::13;3:12:3:13;5:8:5:9', + logs: [], + requires: [], + }; + const sourceCode = 'line 1\nline 2\nline 3\nline 4\nline 5'; + + const formattedBitAuthScript = formatBitAuthScript(debugInformation, sourceCode); + expectBitAuthScriptToCompileTo(formattedBitAuthScript, debugInformation.bytecode); + }); + }); + + describe('User-defined function formatting', () => { + const compileFixture = (fixture: FunctionFixture): Artifact => (fixture.file + ? compileFile(new URL(`./fixtures/${fixture.file}`, import.meta.url), fixture.compilerOptions) + : compileString(fixture.sourceCode!, fixture.compilerOptions)); + + functionFixtures.forEach((fixture) => { + describe(fixture.name, () => { + it('should format function definitions as source-mapped push groups', () => { + const artifact = compileFixture(fixture); + const expectedBitAuthScript = fixture.expectedBitAuthScript.replace(/^\n+/, '').replace(/\n+$/, ''); + expect(formatBitAuthScript(artifact.debug!, artifact.source)).toBe(expectedBitAuthScript); + }); + + it('should compile back to the exact original bytecode', () => { + const artifact = compileFixture(fixture); + const formattedBitAuthScript = formatBitAuthScript(artifact.debug!, artifact.source); + expectBitAuthScriptToCompileTo(formattedBitAuthScript, artifact.debug!.bytecode); + }); }); }); }); }); + +// The formatted output is executed by the BitAuth IDE (and hashed into the P2SH address), so it must +// compile back to the exact bytecode it was generated from +function expectBitAuthScriptToCompileTo(bitAuthScript: string, expectedBytecodeHex: string): void { + const compiler = createCompilerBch({ scripts: { formatted: bitAuthScript } }); + const result = compiler.generateBytecode({ scriptId: 'formatted', data: {} }); + if (!result.success) throw new Error(`BitAuth Script failed to compile: ${JSON.stringify(result.errors)}`); + expect(binToHex(result.bytecode)).toBe(expectedBytecodeHex); +} diff --git a/packages/utils/test/fixtures/bitauth-script.fixture.ts b/packages/utils/test/fixtures/bitauth-script.fixture.ts index 3150831ca..e3bea837d 100644 --- a/packages/utils/test/fixtures/bitauth-script.fixture.ts +++ b/packages/utils/test/fixtures/bitauth-script.fixture.ts @@ -1,12 +1,13 @@ /* eslint-disable max-len */ +import { InternalCompilerOptions } from 'cashc/dist/internal.js'; + export interface Fixture { name: string; sourceCode: string; asmBytecode: string; sourceMap: string; sourceTags?: string; - expectedLineToAsmMap: Record; expectedBitAuthScript: string; } @@ -33,22 +34,6 @@ contract TransferWithTimeout(bytes20 senderPkh, bytes20 recipientPkh, int timeou asmBytecode: 'OP_3 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_5 OP_ROLL OP_5 OP_PICK OP_CHECKSIGVERIFY OP_4 OP_ROLL OP_HASH160 OP_ROT OP_EQUAL OP_NIP OP_NIP OP_NIP OP_ELSE OP_3 OP_ROLL OP_1 OP_NUMEQUALVERIFY OP_DUP deadbeefdeadbeefdeadbeefdeadbeefdeadbeef OP_EQUALVERIFY OP_2 OP_PICK OP_3 OP_PICK OP_NUMEQUALVERIFY OP_4 OP_PICK OP_5 OP_PICK OP_EQUALVERIFY OP_3 OP_PICK OP_4 OP_PICK OP_EQUALVERIFY OP_4 OP_ROLL OP_4 OP_PICK OP_CHECKSIGVERIFY OP_3 OP_ROLL OP_HASH160 OP_EQUALVERIFY OP_SWAP OP_CHECKLOCKTIMEVERIFY OP_2DROP OP_1 OP_ENDIF', sourceMap: '3:2:6:3;;;;;4:21:4:22;;:24::33;;:4::36:1;5:20:5:29:0;;:12::30:1;:34::46:0;:4::48:1;3:45:6:3;;;:2;8::16::0;;;;9:12:9:21;:25::67;:4::69:1;10:12:10:19:0;;:23::30;;:4::32:1;11:12:11:13:0;;:17::18;;:4::20:1;12:12:12:21:0;;:25::34;;:4::36:1;13:21:13:22:0;;:24::33;;:4::36:1;14:20:14:29:0;;:12::30:1;:4::45;15:23:15:30:0;:4::32:1;8:44:16:3;;2:0:17:1', sourceTags: '15:17:sc', - expectedLineToAsmMap: { - 3: 'OP_3 OP_PICK OP_0 OP_NUMEQUAL OP_IF', - 4: 'OP_5 OP_ROLL OP_5 OP_PICK OP_CHECKSIGVERIFY', - 5: 'OP_4 OP_ROLL OP_HASH160 OP_ROT OP_EQUAL', - 6: 'OP_NIP OP_NIP OP_NIP OP_ELSE', - 8: 'OP_3 OP_ROLL OP_1 OP_NUMEQUALVERIFY', - 9: 'OP_DUP <0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef> OP_EQUALVERIFY', - 10: 'OP_2 OP_PICK OP_3 OP_PICK OP_NUMEQUALVERIFY', - 11: 'OP_4 OP_PICK OP_5 OP_PICK OP_EQUALVERIFY', - 12: 'OP_3 OP_PICK OP_4 OP_PICK OP_EQUALVERIFY', - 13: 'OP_4 OP_ROLL OP_4 OP_PICK OP_CHECKSIGVERIFY', - 14: 'OP_3 OP_ROLL OP_HASH160 OP_EQUALVERIFY', - 15: 'OP_SWAP OP_CHECKLOCKTIMEVERIFY', - 16: 'OP_2DROP OP_1', - 17: 'OP_ENDIF', - }, expectedBitAuthScript: ` /* */ /* contract TransferWithTimeout(bytes20 senderPkh, bytes20 recipientPkh, int timeout) { */ @@ -110,26 +95,6 @@ contract Mecenas(bytes20 recipient, bytes20 funder, int pledge/*, int period */) asmBytecode: 'OP_3 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_0 OP_OUTPUTBYTECODE 76a914 OP_ROT OP_CAT 88ac OP_CAT OP_EQUALVERIFY e803 OP_INPUTINDEX OP_UTXOVALUE OP_DUP OP_4 OP_PICK OP_SUB OP_2 OP_PICK OP_SUB OP_DUP OP_5 OP_PICK OP_4 OP_PICK OP_ADD OP_LESSTHANOREQUAL OP_IF OP_0 OP_OUTPUTVALUE OP_2OVER OP_SWAP OP_SUB OP_NUMEQUALVERIFY OP_ELSE OP_0 OP_OUTPUTVALUE OP_5 OP_PICK OP_NUMEQUALVERIFY OP_1 OP_OUTPUTBYTECODE OP_INPUTINDEX OP_UTXOBYTECODE OP_EQUALVERIFY OP_1 OP_OUTPUTVALUE OP_OVER OP_NUMEQUALVERIFY OP_ENDIF OP_2DROP OP_2DROP OP_2DROP OP_1 OP_ELSE OP_3 OP_ROLL OP_1 OP_NUMEQUALVERIFY OP_3 OP_PICK OP_HASH160 OP_ROT OP_EQUALVERIFY OP_2SWAP OP_CHECKSIG OP_NIP OP_NIP OP_ENDIF', sourceMap: '9:4:28:5;;;;;13:27:13:28;:16::45:1;:49::84:0;:74::83;:49::84:1;;;:8::86;15:23:15:27:0;16:37:16:58;:27::65:1;17:26:17:38:0;:41::47;;:26:::1;:50::58:0;;:26:::1;21:12:21:23:0;:27::33;;:36::44;;:27:::1;:12;:46:23:9:0;22:31:22:32;:20::39:1;:43::66:0;;::::1;:12::68;23:15:27:9:0;24:31:24:32;:20::39:1;:43::49:0;;:12::51:1;25:31:25:32:0;:20::49:1;:63::84:0;:53::101:1;:12::103;26:31:26:32:0;:20::39:1;:43::54:0;:12::56:1;23:15:27:9;9:23:28:5;;;;:4;30::33::0;;;;31:24:31:26;;:16::27:1;:31::37:0;:8::39:1;32:25:32:30:0;:8::33:1;30:39:33:5;;8:0:34:1', sourceTags: '69:70:sc', - expectedLineToAsmMap: { - 9: 'OP_3 OP_PICK OP_0 OP_NUMEQUAL OP_IF', - 13: 'OP_0 OP_OUTPUTBYTECODE <0x76a914> OP_ROT OP_CAT <0x88ac> OP_CAT OP_EQUALVERIFY', - 15: '<0xe803>', - 16: 'OP_INPUTINDEX OP_UTXOVALUE', - 17: 'OP_DUP OP_4 OP_PICK OP_SUB OP_2 OP_PICK OP_SUB', - 21: 'OP_DUP OP_5 OP_PICK OP_4 OP_PICK OP_ADD OP_LESSTHANOREQUAL OP_IF', - 22: 'OP_0 OP_OUTPUTVALUE OP_2OVER OP_SWAP OP_SUB OP_NUMEQUALVERIFY', - 23: 'OP_ELSE', - 24: 'OP_0 OP_OUTPUTVALUE OP_5 OP_PICK OP_NUMEQUALVERIFY', - 25: 'OP_1 OP_OUTPUTBYTECODE OP_INPUTINDEX OP_UTXOBYTECODE OP_EQUALVERIFY', - 26: 'OP_1 OP_OUTPUTVALUE OP_OVER OP_NUMEQUALVERIFY', - 27: 'OP_ENDIF', - 28: 'OP_2DROP OP_2DROP OP_2DROP OP_1 OP_ELSE', - 30: 'OP_3 OP_ROLL OP_1 OP_NUMEQUALVERIFY', - 31: 'OP_3 OP_PICK OP_HASH160 OP_ROT OP_EQUALVERIFY', - 32: 'OP_2SWAP OP_CHECKSIG', - 33: 'OP_NIP OP_NIP', - 34: 'OP_ENDIF', - }, expectedBitAuthScript: ` /* pragma cashscript >=0.8.0; */ /* */ @@ -208,17 +173,6 @@ contract HodlVault( asmBytecode: 'OP_6 OP_ROLL OP_SIZE OP_8 OP_EQUALVERIFY OP_DUP OP_4 OP_SPLIT OP_SWAP OP_BIN2NUM OP_SWAP OP_BIN2NUM OP_OVER OP_6 OP_ROLL OP_GREATERTHANOREQUAL OP_VERIFY OP_SWAP OP_CHECKLOCKTIMEVERIFY OP_DROP OP_4 OP_ROLL OP_GREATERTHANOREQUAL OP_VERIFY OP_4 OP_ROLL OP_SWAP OP_3 OP_ROLL OP_CHECKDATASIGVERIFY OP_CHECKSIG', sourceMap: '15:8:15:28;;;;;18:49:18:62;:69::70;:49::71:1;19:30:19:44:0;:26::45:1;20:24:20:32:0;:20::33:1;23:16:23:27:0;:31::39;;:16:::1;:8::41;24:27:24:38:0;:8::40:1;;27:25:27:36:0;;:16:::1;:8::38;30:29:30::0;;:40::53;:55::63;;:8::66:1;31::31:45', sourceTags: '0:4:pv', - expectedLineToAsmMap: { - 15: 'OP_6 OP_ROLL OP_SIZE OP_8 OP_EQUALVERIFY', - 18: 'OP_DUP OP_4 OP_SPLIT', - 19: 'OP_SWAP OP_BIN2NUM', - 20: 'OP_SWAP OP_BIN2NUM', - 23: 'OP_OVER OP_6 OP_ROLL OP_GREATERTHANOREQUAL OP_VERIFY', - 24: 'OP_SWAP OP_CHECKLOCKTIMEVERIFY OP_DROP', - 27: 'OP_4 OP_ROLL OP_GREATERTHANOREQUAL OP_VERIFY', - 30: 'OP_4 OP_ROLL OP_SWAP OP_3 OP_ROLL OP_CHECKDATASIGVERIFY', - 31: 'OP_CHECKSIG', - }, expectedBitAuthScript: ` /* // This contract forces HODLing until a certain price target has been reached */ /* // A minimum block is provided to ensure that oracle price entries from before this block are disregarded */ @@ -280,17 +234,6 @@ contract ForWhileNested() { asmBytecode: 'OP_0 OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_2 OP_PICK OP_2 OP_PICK OP_ADD OP_OVER OP_ADD OP_3 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_DUP OP_1ADD OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_OVER OP_1ADD OP_ROT OP_DROP OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_4 OP_NUMEQUAL', sourceMap: '3:18:3:19;5:21:5:22;:8:13:9;:24:5:25;:28::29;:24:::1;;;:42:13:9:0;6:20:6:21;8:12:12:13;:19:8:20;:23::24;:19:::1;;;:26:12:13:0;9:22:9:25;;:28::29;;:22:::1;:32::33:0;:22:::1;:16::34;;;;;;;10:20:10:21:0;:::25:1;:16::26;8:26:12:13;;:12;;5:35:5:36:0;:::40:1;:31;;::13:9;:42;;:8;;;15:23:15:24:0;:8::26:1', sourceTags: '34:37:lc;38:42:fu;43:46:lc;47:47:sc', - expectedLineToAsmMap: { - 3: 'OP_0', - 5: 'OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_OVER OP_1ADD OP_ROT OP_DROP', - 6: 'OP_0', - 8: 'OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF', - 9: 'OP_2 OP_PICK OP_2 OP_PICK OP_ADD OP_OVER OP_ADD OP_3 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK', - 10: 'OP_DUP OP_1ADD OP_NIP', - 12: 'OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL', - 13: 'OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP', - 15: 'OP_4 OP_NUMEQUAL', - }, expectedBitAuthScript: ` /* contract ForWhileNested() { */ /* function spend() { */ @@ -339,20 +282,6 @@ OP_4 OP_NUMEQUAL asmBytecode: 'OP_0 OP_0 OP_BEGIN OP_DUP OP_3 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_2 OP_PICK OP_2 OP_PICK OP_ADD OP_OVER OP_ADD OP_3 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_DUP OP_1ADD OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_DUP OP_1ADD OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_0 OP_BEGIN OP_DUP OP_1ADD OP_NIP OP_DUP OP_3 OP_GREATERTHANOREQUAL OP_UNTIL OP_ADD OP_12 OP_NUMEQUAL', sourceMap: '3:20:3:21;5:21:5:22;:8:10:9;:24:5:25;:28::29;:24:::1;;;6:19:10:9:0;7:25:7:26;:12:9:13;:28:7:29;:32::33;:28:::1;;;:46:9:13:0;8:24:8:29;;:32::33;;:24:::1;:36::37:0;:24:::1;:16::38;;;;;;;7:39:7:40:0;:::44:1;:35;:46:9:13;;:12;;;6::6::0;:::17:1;5:31;6:19:10:9;;5:8;;;12:20:12:21:0;13:8:15:28;14:20:14:25;:::29:1;:12::30;15:17:15:22:0;:25::26;13:8::28:1;;17:16:17:29;:33::35:0;:8::37:1', sourceTags: '31:33:fu;34:37:lc;38:38:sc;39:41:fu;42:45:lc;46:46:sc', - expectedLineToAsmMap: { - 3: 'OP_0', - 5: 'OP_0 OP_BEGIN OP_DUP OP_3 OP_LESSTHAN OP_DUP OP_TOALTSTACK', - 6: 'OP_IF OP_DUP OP_1ADD OP_NIP', - 7: 'OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_DUP OP_1ADD OP_NIP', - 8: 'OP_2 OP_PICK OP_2 OP_PICK OP_ADD OP_OVER OP_ADD OP_3 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK', - 9: 'OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP', - 10: 'OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP', - 12: 'OP_0', - 13: 'OP_BEGIN', - 14: 'OP_DUP OP_1ADD OP_NIP', - 15: 'OP_DUP OP_3 OP_GREATERTHANOREQUAL OP_UNTIL', - 17: 'OP_ADD OP_12 OP_NUMEQUAL', - }, expectedBitAuthScript: ` /* contract NestedForWithDoWhile() { */ /* function spend() { */ @@ -382,25 +311,23 @@ OP_ADD OP_12 OP_NUMEQUAL `.replace(/^\n+/, '').replace(/\n+$/, ''), }, { - name: 'ParameterCheck (parameter type check)', + name: 'ParameterCheck (parameter type check + unused parameter drop)', sourceCode: `contract ParameterCheck() { function spend( bytes8 tag, + bytes8 unused padding, ) { require(tag.length == 8); } }`, - asmBytecode: 'OP_SIZE OP_8 OP_EQUALVERIFY OP_SIZE OP_NIP OP_8 OP_NUMEQUAL', - sourceMap: '3:8:3:18;;;5:16:5:26:1;;:30::31:0;:8::33:1', - sourceTags: '0:2:pv', - expectedLineToAsmMap: { - 3: 'OP_SIZE OP_8 OP_EQUALVERIFY', - 5: 'OP_SIZE OP_NIP OP_8 OP_NUMEQUAL', - }, + asmBytecode: 'OP_NIP OP_SIZE OP_8 OP_EQUALVERIFY OP_SIZE OP_NIP OP_8 OP_NUMEQUAL', + sourceMap: '4:8:4:29;3::3:18;;;6:16:6:26:1;;:30::31:0;:8::33:1', + sourceTags: '1:3:pv', expectedBitAuthScript: ` /* contract ParameterCheck() { */ /* function spend( */ /* bytes8 tag, */ +OP_NIP /* bytes8 unused padding, */ /* ) { */ OP_SIZE OP_8 OP_EQUALVERIFY /* >>> parameter type check (bytes8 tag) */ OP_SIZE OP_NIP OP_8 OP_NUMEQUAL /* require(tag.length == 8); */ @@ -418,10 +345,6 @@ OP_SIZE OP_NIP OP_8 OP_NUMEQUAL /* require(tag.length == 8); asmBytecode: 'OP_TXLOCKTIME OP_CHECKLOCKTIMEVERIFY OP_DROP OP_TXLOCKTIME OP_1 OP_GREATERTHANOREQUAL', sourceMap: '2:21:2:21;::::1;;3:16:3:27:0;:31::32;:8::34:1', sourceTags: '0:2:lg', - expectedLineToAsmMap: { - 2: 'OP_TXLOCKTIME OP_CHECKLOCKTIMEVERIFY OP_DROP', - 3: 'OP_TXLOCKTIME OP_1 OP_GREATERTHANOREQUAL', - }, expectedBitAuthScript: ` /* contract LocktimeGuard() { */ /* function spend() { */ @@ -442,11 +365,6 @@ OP_TXLOCKTIME OP_1 OP_GREATERTHANOREQUAL /* require(tx.locktime >= 1 asmBytecode: 'OP_SIZE OP_8 OP_EQUALVERIFY OP_TXLOCKTIME OP_CHECKLOCKTIMEVERIFY OP_DROP OP_SIZE OP_NIP OP_8 OP_NUMEQUALVERIFY OP_TXLOCKTIME OP_1 OP_GREATERTHANOREQUAL', sourceMap: '2:19:2:29;;;:31::31;::::1;;3:16:3:26;;:30::31:0;:8::33:1;4:16:4:27:0;:31::32;:8::34:1', sourceTags: '0:2:pv;3:5:lg', - expectedLineToAsmMap: { - 2: 'OP_SIZE OP_8 OP_EQUALVERIFY OP_TXLOCKTIME OP_CHECKLOCKTIMEVERIFY OP_DROP', - 3: 'OP_SIZE OP_NIP OP_8 OP_NUMEQUALVERIFY', - 4: 'OP_TXLOCKTIME OP_1 OP_GREATERTHANOREQUAL', - }, expectedBitAuthScript: ` /* contract ParameterLocktimeGuard() { */ /* function spend(bytes8 tag) { */ @@ -472,13 +390,6 @@ OP_TXLOCKTIME OP_1 OP_GREATERTHANOREQUAL /* require(tx.locktime >= 1 asmBytecode: 'OP_DUP OP_0 OP_GREATERTHAN OP_IF OP_DUP OP_1ADD OP_DUP OP_0 OP_GREATERTHAN OP_VERIFY OP_DROP OP_ENDIF OP_0 OP_GREATERTHAN', sourceMap: '3:12:3:13;:16::17;:12:::1;:19:6:9:0;4:20:4:21;:::25:1;5::5:21:0;:24::25;:20:::1;:12::27;3:19:6:9;;7:20:7:21:0;:8::23:1', sourceTags: '10:10:sc', - expectedLineToAsmMap: { - 3: 'OP_DUP OP_0 OP_GREATERTHAN OP_IF', - 4: 'OP_DUP OP_1ADD', - 5: 'OP_DUP OP_0 OP_GREATERTHAN OP_VERIFY', - 6: 'OP_DROP OP_ENDIF', - 7: 'OP_0 OP_GREATERTHAN', - }, expectedBitAuthScript: ` /* contract ScopeCleanup() { */ /* function spend(int x) { */ @@ -493,3 +404,258 @@ OP_0 OP_GREATERTHAN /* require(x > 0); */ `.replace(/^\n+/, '').replace(/\n+$/, ''), }, ]; + +// Contracts with user-defined functions render each definition as a source-mapped `<...>` push group. +// These fixtures are compiled at test time (compileString for same-file functions, compileFile for imports). +export interface FunctionFixture { + name: string; + sourceCode?: string; // compiled with compileString when set + file?: string; // compiled with compileFile, relative to this fixtures directory (used for imports) + compilerOptions?: InternalCompilerOptions; + expectedBitAuthScript: string; +} + +export const functionFixtures: FunctionFixture[] = [ + { + name: 'LocalFunctions (same-file functions with loop + recursion)', + compilerOptions: { disableInlining: true }, + sourceCode: ` +function sumTo(int n) returns (int) { + int sum = 0; + for (int i = 0; i < n; i = i + 1) { + sum = sum + i; + } + return sum; +} + +function fib(int n) returns (int) { + int result = n; + if (n >= 2) { + result = fib(n - 1) + fib(n - 2); + } + return result; +} + +contract LocalFunctions() { + function spend() { + require(sumTo(5) == 10, 'sum mismatch'); + require(fib(7) == 13, 'fib mismatch'); + } +} +`.replace(/^\n+/, '').replace(/\n+$/, ''), + // fib is recursive, so it receives its id before the callee-first compilation pass and is + // defined first; sumTo follows in id order. + expectedBitAuthScript: ` +< /* function fib(int n) returns (int) { */ + OP_DUP OP_DUP /* int result = n; */ + OP_2 OP_GREATERTHANOREQUAL OP_IF /* if (n >= 2) { */ + OP_OVER OP_1SUB OP_0 OP_INVOKE OP_2 OP_PICK OP_2 OP_SUB OP_0 OP_INVOKE OP_ADD OP_NIP /* result = fib(n - 1) + fib(n - 2); */ + OP_ENDIF /* } */ + /* return result; */ + OP_NIP /* >>> scope cleanup */ +> OP_0 OP_DEFINE /* } */ + /* */ +< /* function sumTo(int n) returns (int) { */ + OP_0 /* int sum = 0; */ + OP_0 OP_BEGIN OP_DUP OP_3 OP_PICK OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF /* for (int i = 0; i < n; i = i + 1) { */ + OP_2DUP OP_ADD OP_ROT OP_DROP OP_SWAP /* sum = sum + i; */ + OP_DUP OP_1ADD OP_NIP /* >>> for-loop update (i = i + 1) */ + OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL /* >>> loop condition check */ + OP_DROP /* >>> scope cleanup */ + /* } */ + /* return sum; */ + OP_NIP /* >>> scope cleanup */ +> OP_1 OP_DEFINE /* } */ + /* */ + /* contract LocalFunctions() { */ + /* function spend() { */ +OP_5 OP_1 OP_INVOKE OP_10 OP_NUMEQUALVERIFY /* require(sumTo(5) == 10, 'sum mismatch'); */ +OP_7 OP_0 OP_INVOKE OP_13 OP_NUMEQUAL /* require(fib(7) == 13, 'fib mismatch'); */ + /* } */ + /* } */ +`.replace(/^\n+/, '').replace(/\n+$/, ''), + }, + { + name: 'ImportedFunctions (two imported functions from one file)', + file: 'function-imports/importer.cash', + compilerOptions: { disableInlining: true }, + expectedBitAuthScript: ` + /* >>> imported from helpers.cash */ +< /* function double(int x) returns (int) { */ + OP_2 OP_MUL /* return x * 2; */ +> OP_0 OP_DEFINE /* } */ + /* */ + /* >>> imported from helpers.cash */ +< /* function addChecked(int a, int b) returns (int) { */ + OP_OVER OP_ADD /* int sum = a + b; */ + OP_DUP OP_ROT OP_GREATERTHANOREQUAL OP_VERIFY /* require(sum >= a, "overflow"); */ + /* return sum; */ +> OP_1 OP_DEFINE /* } */ + /* */ + /* import "./helpers.cash"; */ + /* */ + /* contract ImportedFunctions() { */ + /* function spend(int x) { */ +OP_DUP OP_0 OP_INVOKE /* int doubled = double(x); */ +OP_SWAP OP_1 OP_INVOKE OP_15 OP_NUMEQUAL /* require(addChecked(doubled, x) == 15, "sum mismatch"); */ + /* } */ + /* } */ + /* */ +`.replace(/^\n+/, '').replace(/\n+$/, ''), + }, + { + // The single-byte 0x81 body (lone OP_BIN2NUM) gets minimally encoded as the opcode OP_1NEGATE at the + // define site, so it must be matched to its frame by push-data equality rather than element shape. + name: 'MinimalBody (single-byte function body, minimally encoded define site)', + compilerOptions: { disableInlining: true }, + sourceCode: ` +function toInt(bytes b) returns (int) { + return int(b); +} + +function double(int x) returns (int) { + return x * 2; +} + +contract MinimalBody() { + function spend(bytes8 b) { + require(toInt(b) > 0, 'not positive'); + require(double(3) == 6, 'bad double'); + } +} +`.replace(/^\n+/, '').replace(/\n+$/, ''), + expectedBitAuthScript: ` +< /* function toInt(bytes b) returns (int) { */ + OP_BIN2NUM /* return int(b); */ +> OP_0 OP_DEFINE /* } */ + /* */ +< /* function double(int x) returns (int) { */ + OP_2 OP_MUL /* return x * 2; */ +> OP_1 OP_DEFINE /* } */ + /* */ + /* contract MinimalBody() { */ + /* function spend(bytes8 b) { */ +OP_SIZE OP_8 OP_EQUALVERIFY /* >>> parameter type check (bytes8 b) */ +OP_0 OP_INVOKE OP_0 OP_GREATERTHAN OP_VERIFY /* require(toInt(b) > 0, 'not positive'); */ +OP_3 OP_1 OP_INVOKE OP_6 OP_NUMEQUAL /* require(double(3) == 6, 'bad double'); */ + /* } */ + /* } */ +`.replace(/^\n+/, '').replace(/\n+$/, ''), + }, + { + // A large constant used twice is lowered to a shared definition rendered as a define push group + // on its declaration line; the small constant ONE stays inlined at its use site (as OP_1ADD). + name: 'GlobalConstants (shared and inlined constants)', + sourceCode: ` +bytes32 constant HASH = 0x3333333333333333333333333333333333333333333333333333333333333333; +int constant ONE = 1; + +contract GlobalConstants(bytes32 first, bytes32 second) { + function spend(int n) { + require(first == HASH, 'first mismatch'); + require(second == HASH, 'second mismatch'); + require(n + ONE == 2, 'n mismatch'); + } +} +`.replace(/^\n+/, '').replace(/\n+$/, ''), + expectedBitAuthScript: ` +< <0x3333333333333333333333333333333333333333333333333333333333333333> > OP_0 OP_DEFINE /* bytes32 constant HASH = 0x3333333333333333333333333333333333333333333333333333333333333333; */ + /* */ + /* int constant ONE = 1; */ + /* */ + /* contract GlobalConstants(bytes32 first, bytes32 second) { */ + /* function spend(int n) { */ +OP_0 OP_INVOKE OP_EQUALVERIFY /* require(first == HASH, 'first mismatch'); */ +OP_0 OP_INVOKE OP_EQUALVERIFY /* require(second == HASH, 'second mismatch'); */ +OP_1ADD OP_2 OP_NUMEQUAL /* require(n + ONE == 2, 'n mismatch'); */ + /* } */ + /* } */ +`.replace(/^\n+/, '').replace(/\n+$/, ''), + }, + { + // A single-use function is inlined at the call site instead of defined: no define push groups, + // and the emitted body opcodes are attributed to the call site (the function's own source + // renders as bare comment lines). + name: 'InlinedFunction (single-use function inlined at the call site)', + sourceCode: ` +function double(int x) returns (int) { + return x * 2; +} + +contract InlinedFunction() { + function spend(int n) { + require(double(n) == 10, 'mismatch'); + } +} +`.replace(/^\n+/, '').replace(/\n+$/, ''), + expectedBitAuthScript: ` + /* function double(int x) returns (int) { */ + /* return x * 2; */ + /* } */ + /* */ + /* contract InlinedFunction() { */ + /* function spend(int n) { */ +OP_2 OP_MUL OP_10 OP_NUMEQUAL /* require(double(n) == 10, 'mismatch'); */ + /* } */ + /* } */ +`.replace(/^\n+/, '').replace(/\n+$/, ''), + }, + { + name: 'AfterContract (function defined below the contract in the same file)', + compilerOptions: { disableInlining: true }, + sourceCode: ` +contract AfterContract() { + function spend(int x) { + require(double(x) == 10, 'mismatch'); + } +} + +function double(int a) returns (int) { + return a * 2; +} +`.replace(/^\n+/, '').replace(/\n+$/, ''), + expectedBitAuthScript: ` +< /* function double(int a) returns (int) { */ + OP_2 OP_MUL /* return a * 2; */ +> OP_0 OP_DEFINE /* } */ + /* */ + /* contract AfterContract() { */ + /* function spend(int x) { */ +OP_0 OP_INVOKE OP_10 OP_NUMEQUAL /* require(double(x) == 10, 'mismatch'); */ + /* } */ + /* } */ + /* */ +`.replace(/^\n+/, '').replace(/\n+$/, ''), + }, + { + // Global constants are zero-argument VM functions: each definition renders as a define push + // group on the constant's declaration line, and each use compiles to an OP_INVOKE. + name: 'GlobalConstants (constants as zero-argument function definitions)', + compilerOptions: { disableInlining: true }, + sourceCode: ` +bytes32 constant HASH = 0x3333333333333333333333333333333333333333333333333333333333333333; +int constant ONE = 1; + +contract GlobalConstants(bytes32 first, bytes32 second) { + function spend(int n) { + require(first == HASH, 'first mismatch'); + require(second == HASH, 'second mismatch'); + require(n + ONE == 2, 'n mismatch'); + } +} +`.replace(/^\n+/, '').replace(/\n+$/, ''), + expectedBitAuthScript: ` +< <0x3333333333333333333333333333333333333333333333333333333333333333> > OP_0 OP_DEFINE /* bytes32 constant HASH = 0x3333333333333333333333333333333333333333333333333333333333333333; */ + /* */ +< OP_1 > OP_1 OP_DEFINE /* int constant ONE = 1; */ + /* */ + /* contract GlobalConstants(bytes32 first, bytes32 second) { */ + /* function spend(int n) { */ +OP_0 OP_INVOKE OP_EQUALVERIFY /* require(first == HASH, 'first mismatch'); */ +OP_0 OP_INVOKE OP_EQUALVERIFY /* require(second == HASH, 'second mismatch'); */ +OP_1 OP_INVOKE OP_ADD OP_2 OP_NUMEQUAL /* require(n + ONE == 2, 'n mismatch'); */ + /* } */ + /* } */ +`.replace(/^\n+/, '').replace(/\n+$/, ''), + }, +]; diff --git a/packages/utils/test/fixtures/function-imports/helpers.cash b/packages/utils/test/fixtures/function-imports/helpers.cash new file mode 100644 index 000000000..9ca385c15 --- /dev/null +++ b/packages/utils/test/fixtures/function-imports/helpers.cash @@ -0,0 +1,9 @@ +function double(int x) returns (int) { + return x * 2; +} + +function addChecked(int a, int b) returns (int) { + int sum = a + b; + require(sum >= a, "overflow"); + return sum; +} diff --git a/packages/utils/test/fixtures/function-imports/importer.cash b/packages/utils/test/fixtures/function-imports/importer.cash new file mode 100644 index 000000000..c2834589e --- /dev/null +++ b/packages/utils/test/fixtures/function-imports/importer.cash @@ -0,0 +1,8 @@ +import "./helpers.cash"; + +contract ImportedFunctions() { + function spend(int x) { + int doubled = double(x); + require(addChecked(doubled, x) == 15, "sum mismatch"); + } +} diff --git a/website/docs/compiler/artifacts.md b/website/docs/compiler/artifacts.md index fc5cfcb1a..766dc907f 100644 --- a/website/docs/compiler/artifacts.md +++ b/website/docs/compiler/artifacts.md @@ -27,6 +27,8 @@ interface Artifact { logs: LogEntry[] // log entries generated from `console.log` statements requires: RequireStatement[] // messages for failing `require` statements sourceTags?: string // semantic tags for opcodes (e.g. loop update/condition ranges) + functions?: DebugFrame[] // debug metadata for each global definition (defined frames first, then inlined ones) + inlineRanges?: string // "startIp:endIp:name;..." — runs where inlined callables' bodies were emitted } updatedAt: string // Last datetime this artifact was updated (in ISO format) fingerprint?: string // SHA256 of the normalized bytecode pattern (BCH bytecode fingerprinting standard) @@ -61,6 +63,21 @@ interface RequireStatement { message?: string; // custom message for failing `require` statement } +interface DebugFrame { + id?: number; // the function's id, as used with OP_DEFINE / OP_INVOKE in the bytecode (absent for inlined callables) + name: string; // the source definition's name + kind?: 'constant'; // present when the VM function implements a global constant; absent for regular functions + inputs: AbiInput[]; // the function's parameters (name and type) + bytecode: string; // hex-encoded bytecode of the function body (exactly what OP_DEFINE stores) + sourceMap: string; // source map of the function body (instruction pointers starting from 0) + sourceTags?: string; // semantic tags for opcodes within the function body + source?: string; // full source code of the defining file (only present for imported functions) + sourceFile?: string; // file name the function is imported from (absent for the contract's own file) + logs: LogEntry[]; // log entries within the function body + requires: RequireStatement[]; // messages for failing `require` statements within the function body + inlineRanges?: string; // runs where inlined callables' bodies were emitted within this body (frame-local ips) +} + interface CompilerOptions { enforceFunctionParameterTypes?: boolean; // Enforce function parameter types (default: true) enforceLocktimeGuard?: boolean; // Enforce the tx.locktime guard (default: true) diff --git a/website/docs/compiler/compiler.md b/website/docs/compiler/compiler.md index de6e48c89..262083fc0 100644 --- a/website/docs/compiler/compiler.md +++ b/website/docs/compiler/compiler.md @@ -82,9 +82,13 @@ Compiles a CashScript contract from a source file. This compile method is handy const P2PKH = compileFile(new URL('p2pkh.cash', import.meta.url)); ``` +:::note +If the contract uses `import` directives to pull in [user-defined functions](/docs/language/contracts#user-defined-functions) from other files, `compileFile` resolves those imports relative to the source file's directory automatically. Package imports such as `import "@example/math-lib/math.cash"` are resolved from `node_modules` directories, walking up from the importing file's directory like Node.js module resolution. +::: + ### compileString() ```ts -compileString(sourceCode: string, compilerOptions?: CompilerOptions): Artifact +compileString(sourceCode: string, compilerOptions?: CompilerOptions & { files?: Record }): Artifact ``` Compiles a CashScript contract from a source code string. This compile method is handy in a browser compilation setting like the [CashScript Playground](https://playground.cashscript.org/) where testing contracts can be quickly compiled and discarded. The method is also useful if no source file is locally available (e.g. the source code is retrieved with a REST API). @@ -97,6 +101,47 @@ const source = await result.text(); const P2PKH = compileString(source); ``` +`compileString` never reads from the filesystem, so `import` directives that pull in [top-level definitions](/docs/language/contracts#importing-functions-and-constants-from-other-files) are resolved from the `files` compiler option instead. Its keys are the import paths relative to the main source (using forward slashes), and its values are the corresponding source code strings. + +```ts +const mathSource = ` +function double(int a) returns (int) { + return a * 2; +} +`; + +const source = ` +import "./math.cash"; + +contract Doubler() { + function spend(int x) { + require(double(x) == 8); + } +} +`; + +const Doubler = compileString(source, { files: { './math.cash': mathSource } }); +``` + +:::note +Imports inside imported files are resolved relative to the *importing* file, but their keys in `files` remain relative to the main source. For example, if `lib/a.cash` contains `import "./b.cash";`, that file must be provided under the key `lib/b.cash`. Package imports such as `import "@example/math-lib/math.cash"` are looked up verbatim, so they must be provided under exactly that key. +::: + +### Compilation Warnings + +Some issues, such as unused variables that are not marked [`unused`](/docs/language/contracts#intentionally-unused-values), do not prevent compilation but produce a compiler warning instead. By default these warnings are printed with `console.warn`. When compiling from JavaScript, a custom `warningListener` can be passed as a compiler option to capture the structured warnings instead. It is called for each warning. The default listener is exported as `defaultWarningListener`, so a custom listener can compose with it to keep the standard console output. + +```ts +import { compileString, defaultWarningListener } from 'cashc'; + +const P2PKH = compileString(source, { + warningListener: (warning) => { + defaultWarningListener(warning); // still print the warning to the console + myDiagnostics.push(warning); + }, +}); +``` + ### Compiler Options ```ts interface CompilerOptions { diff --git a/website/docs/compiler/grammar.md b/website/docs/compiler/grammar.md index 93f5d8d7d..05a4fb8fc 100644 --- a/website/docs/compiler/grammar.md +++ b/website/docs/compiler/grammar.md @@ -7,7 +7,7 @@ description: ANTLR4 language grammar for CashScript grammar CashScript; sourceFile - : pragmaDirective* contractDefinition EOF + : pragmaDirective* importDirective* topLevelDefinition* EOF ; pragmaDirective @@ -30,11 +30,25 @@ versionOperator : '^' | '~' | '>=' | '>' | '<' | '<=' | '=' ; +importDirective + : 'import' StringLiteral ';' + ; + +topLevelDefinition + : globalFunctionDefinition + | constantDefinition + | contractDefinition + ; + +globalFunctionDefinition + : 'function' Identifier parameterList ('returns' '(' typeName ')')? functionBody + ; + contractDefinition - : 'contract' Identifier parameterList '{' functionDefinition* '}' + : 'contract' Identifier parameterList '{' contractFunctionDefinition* '}' ; -functionDefinition +contractFunctionDefinition : 'function' Identifier parameterList functionBody ; @@ -47,7 +61,7 @@ parameterList ; parameter - : typeName Identifier + : typeName modifier* Identifier ; block @@ -66,7 +80,17 @@ nonControlStatement | assignStatement | timeOpStatement | requireStatement + | functionCallStatement | consoleStatement + | returnStatement + ; + +functionCallStatement + : functionCall + ; + +returnStatement + : 'return' expression ; controlStatement @@ -79,7 +103,13 @@ variableDefinition ; tupleAssignment - : typeName Identifier ',' typeName Identifier '=' expression + : tupleTarget (',' tupleTarget)+ '=' expression + | '(' tupleTarget (',' tupleTarget)+ ')' '=' expression + ; + +tupleTarget + : typeName Identifier + | Identifier ; assignStatement @@ -140,7 +170,7 @@ consoleParameterList ; functionCall - : Identifier expressionList // Only built-in functions are accepted + : Identifier expressionList // Built-in global functions and user-defined global functions ; expressionList @@ -177,6 +207,7 @@ expression modifier : 'constant' + | 'unused' ; literal @@ -203,6 +234,10 @@ typeCast | UnsafeCast ; +constantDefinition + : typeName 'constant' Identifier '=' expression ';' + ; + VersionLiteral : [0-9]+ '.' [0-9]+ '.' [0-9]+ ; diff --git a/website/docs/compiler/script-limits.md b/website/docs/compiler/script-limits.md index f13d43cf3..6ba9b62b3 100644 --- a/website/docs/compiler/script-limits.md +++ b/website/docs/compiler/script-limits.md @@ -28,18 +28,27 @@ If your local state grows larger than the allowed maximum, one option is to hash ### Operation cost limit -Bitcoin Cash enforces an operation cost limit (op-cost) per transaction input. This determines the computational budget available for operations in a contract. The op-cost is based on script length: longer input scripts allow for a higher compute budget. +Bitcoin Cash enforces an operation cost limit (op-cost) per transaction input. This determines the computational budget available for operations in a contract. The op-cost is based on script length: longer input scripts allow for a higher compute budget. You can find the exact op-cost per operation in the [op-cost-table][op-cost-table]. #### Buying compute budget -Since longer input scripts allow for a larger compute budget, some contracts use zero-padding (adding non-functional bytes) to "buy" more computation power without changing logic. You can find the exact op-cost per operation in the [op-cost-table][op-cost-table]. +When running into op-cost limits, one option is to "buy" more compute budget by adding padding to the unlocking bytecode, since op-cost is calculated as `(41 + unlockingBytecodeLength) * 800`, adding 800 op-cost budget per byte. Because this increases the transaction size, this also increases the transaction fee. -```ts -function maxOperationCost(unlockingBytecodeLength) { - return (41n + unlockingBytecodeLength) * 800n; +In CashScript, this padding can be added with an [`unused` parameter](/docs/language/contracts#intentionally-unused-values) in the constructor or spending function. An `unused` constructor parameter becomes part of the contract bytecode, so it buys a fixed amount of extra budget. An `unused` function parameter is provided by the spender at transaction time, so the amount of padding can be chosen dynamically per transaction. + +```solidity +contract HeavyComputation(pubkey pk, bytes unused fixedPadding) { + function spend(sig s, bytes unused dynamicPadding) { + // ... heavy computation ... + require(checkSig(s, pk)); + } } ``` +:::tip +Due to compiler optimisations, it is most efficient to place `unused` parameters at the end of the parameter list. +::: + ### Other contract-related limits - Signature operation count (SigChecks): Limits the number of signature verifications (`OP_CHECKSIG`, `OP_CHECKDATASIG`) per transaction to ensure efficient validation. diff --git a/website/docs/guides/cashtokens.md b/website/docs/guides/cashtokens.md index 0c0393af0..fefd2e2c9 100644 --- a/website/docs/guides/cashtokens.md +++ b/website/docs/guides/cashtokens.md @@ -20,6 +20,7 @@ interface Utxo { vout: number; satoshis: bigint; token?: TokenDetails; + lockingBytecode?: string; } interface TokenDetails { diff --git a/website/docs/guides/debugging.md b/website/docs/guides/debugging.md index daa4131a9..17f4cf28f 100644 --- a/website/docs/guides/debugging.md +++ b/website/docs/guides/debugging.md @@ -52,31 +52,43 @@ const uri = transactionBuilder.getBitauthUri(); It is unsafe to debug transactions on mainnet using the BitAuth IDE as private keys will be exposed to BitAuth IDE and transmitted over the network. ::: -The Bitauth IDE will show you the two-way mapping between the CashScript contract code generated opcodes. Here is [a Bitauth IDE link][BitauthIDE] for the basic `TransferWithTimeout` contract as an example: +The Bitauth IDE will show you the two-way mapping between the CashScript contract code and the generated opcodes. User-defined functions are included with the same mapping: each function definition is rendered as a push group annotated with the function's own source lines, and imported functions are annotated with the file they are imported from. + +Here is [a Bitauth IDE link][BitauthIDE] for an example `HalfTimeVault` contract, which uses a `half()` function imported from `math.cash`. Note the source-mapped function definition (`OP_DEFINE`) at the top, the `OP_INVOKE` call sites, and the `>>>` annotation rows: ```js -// "TransferWithTimeout" contract constructor parameters +// "HalfTimeVault" contract constructor parameters // int = <0x90d003> - // pubkey = <0x038f55548d7f3d183cebb8ee77036feeb408f4a5030fb486717659bb944fe5eb4c> - // pubkey = <0x0218d4166169298d42c1f763e243e4b5bc3df8e11690aa953b17a6e02902625f90> + // pubkey = <0x034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa> // bytecode - /* pragma cashscript ~0.11.0; */ - /* */ - /* contract TransferWithTimeout(pubkey sender, pubkey recipient, int timeout) { */ - /* // Require recipient's signature to match */ -OP_3 OP_PICK OP_0 OP_NUMEQUAL OP_IF /* function transfer(sig recipientSig) { */ -OP_4 OP_ROLL OP_ROT OP_CHECKSIG /* require(checkSig(recipientSig, recipient)); */ -OP_NIP OP_NIP OP_NIP OP_ELSE /* } */ - /* */ - /* // Require timeout time to be reached and sender's signature to match */ -OP_3 OP_ROLL OP_1 OP_NUMEQUALVERIFY /* function timeout(sig senderSig) { */ -OP_3 OP_ROLL OP_SWAP OP_CHECKSIGVERIFY /* require(checkSig(senderSig, sender)); */ -OP_SWAP OP_CHECKLOCKTIMEVERIFY /* require(tx.time >= timeout); */ -OP_2DROP OP_1 /* } */ -OP_ENDIF /* } */ + /* >>> function half (imported from math.cash) */ +< /* function half(int amount) returns (int) { */ + OP_2 OP_DIV /* return amount / 2; */ +> OP_0 OP_DEFINE /* } */ + /* */ + /* pragma cashscript ^0.14.0; */ + /* */ + /* import "./math.cash"; */ + /* */ + /* contract HalfTimeVault(pubkey owner, int timeout) { */ + /* // Early claims must leave half the coins in the vault */ +OP_2 OP_PICK OP_0 OP_NUMEQUAL OP_IF /* function claimEarly(sig ownerSig) { */ +OP_3 OP_ROLL OP_SWAP OP_CHECKSIGVERIFY /* require(checkSig(ownerSig, owner)); */ +OP_0 OP_INVOKE OP_CHECKLOCKTIMEVERIFY OP_DROP /* require(tx.time >= half(timeout)); */ +OP_INPUTINDEX OP_UTXOVALUE /* int vaultValue = tx.inputs[this.activeInputIndex].value; */ +OP_0 OP_OUTPUTVALUE OP_SWAP OP_0 OP_INVOKE OP_GREATERTHANOREQUAL /* require(tx.outputs[0].value >= half(vaultValue)); */ +OP_NIP /* >>> scope cleanup */ +OP_ELSE /* } */ + /* */ + /* // After the full timeout, the owner can claim everything */ +OP_ROT OP_1 OP_NUMEQUALVERIFY /* function claim(sig ownerSig) { */ +OP_ROT OP_SWAP OP_CHECKSIGVERIFY /* require(checkSig(ownerSig, owner)); */ +OP_CHECKLOCKTIMEVERIFY OP_DROP /* require(tx.time >= timeout); */ +OP_1 /* } */ +OP_ENDIF /* } */ ``` -[BitauthIDE]: https://ide.bitauth.com/import-template/eJzFWAtv2zYQ_isCN2BJ4drUW8raAK3jNkbSJEvcFUMdGBR1stXakidRWYIg--07SrIk23LgDhlGBKEiHu-7x3fUMY_k55TPYMHIEZkJsUyPer3Qh64XCpaJWZfHi558gEiEnIkwjl4LWCznTMDrO9ot9na_pXFEOsSHlCfhUkqhuuFiGScCfCVI4oXCWTorVlEwYgtAiT6-u8nfKR8hgoRJ6RPwsuk0jKbKqATCDWm2LJSRo6_kff90olHNnFCT3HbIHSRpjkg7RJopQkjJ0SMZJSxKA0i-hGI2ChcQZ2ISRstM0MmSJWiBwI1ScN3sfhyJhHGh8ARyhxUWoQ9ZxPM_GlsrP1qQlIMcSvmJHkrzc_2pNL7NqnnMv6NU25JYNzyLclnpNUtC5s0LV1OIfEhuwum2O-N6cUxq65U4qH0akxJmTGqnap0dIh6W8tUZPJCnTrmyG2oTR8zCVOFlWDcBau1f2HwO4oQJJkES4OEyxHy24VSLe0LVynaglf63YVWh2QtppWgHzirkmE8f7rfhymqoMpOLKSJW4B54lpdCCbShqRUPR4N77RXRTjAUXrI0hZ2U3dgGd2yeyVK93YxEWyHgaq_XZF1beY2jNxUF5TkTxUkyScNpxESWQBfdnOBeVJ1O5HMm7uP0WEG9KDOOxhE-bYQxjKqEofL1AOY7w0gob5U36vFYnmZFKNL2-i5q9qm9aFchlDMeZKMiM0stnenas6fGVoRaZDBavDqi4igVScZFnKxHroxs0yt671KfUh2de1PVQy6wzLzv8FDIUN0JTNM0HN8OdF91dA6e5wDYNtWtAMAzqBMYzKQ6DTzDsWzVtkzX81zDCMDEZX5cJW5buaY6vqFalmq5mouPGlcD29JBM3QwPNPjuh84oOIyZcw1dU-1mQVUc6lmaWbg0uMys96DAB77MI6U_UbvlbJM2HTBGh8i5W_aVdUu_XVPHc3xqvcj0C86fgy64koLlQ7K5BTp6qxyVbGjk3On5NKh8vgvvMZcXcOfWZhArfaXVKnqWJ5tCyb4bB-vL68muoK_rob9MzlT-evi86fBb5_fncvn4YcGdFX9ovT8AFFrK_BYkS7tMUpoQyJcX56fF_NITv3TQf_sZvhxw2s5ksLtA-yQ-HcEO2gid2o7Dg-f4V8JfTG8UramwfnNYDvgT_t4tK_Xe4r-vwzPp5pmq--OnCW7PMk8hlnw80au4PpOCjZptsq12qTZ74Pr4Yc_GtA1zcqikiyrvlx7UmwX9M2Xd1dNnpXoz9Gsgu6UVjxLsDXoNbTzy_7ZaPhpUDvcDHgTWtx382Afv63Oiv2P1BJaO7m-vCqCvXP8FwyXdXRxUpwbzwyEfjHYFfSeoi9fXKRoCyHCK0S8b2NYdXgoXjYwg_JV26dFaW3_1pvddxG2tQzveKCU2mUZ_TUDWZOyvS4_00XvW3SHsiftSk2ywUVTVo1AfQtCxS_RaZC1Kwh5ic6INO4ZZNWOke1LAVFlY8mzJEHs97KJPIVwOsNd2vprGWtypNqGhViaa3QIfr7zhC6T8A4zc1b-2bgfEstwLddjhqepaJvuO6pl2hq1uUZdz0HjTUvXLIeCYXsAGmO2a_HA9j3K8YfbJL9V5N9UxotMPpI8yfKy8EhkUx5j3zAsnEGjVm9G9Z5TbMLQFgBuBa5lqrYWcNc3HGrqAQ-Au6aqqtRmjgm6r_nUU33VBWmyjUmjlu5z3fAC2S_jIQQRh4ts4cnkGxgH17LzaBQdPHbg7yuWfCXpPBbk9gkvKnJR5CHUTIqjsHTlx9ZWYlvMVQ1HM1Xug61xmfvA9wPbMTBArmcyZpgOhs-2ASzHYTLjkthww0ScIqMxWdRFpKfmvy00WY1xlnC4fA5-ZXmbSsNxnm4xL_8AUE0PwQ== +[BitauthIDE]: https://ide.bitauth.com/import-template/eJzdWG1z2jgQ_isa331IOhSMwYDTlhlKaMMkhRwhaW9Cj5GFHHw1ts-WaTKZ_vfblV_AvKTQJDc38YfE2NLuo2dXj9Z7r_wesimfUeVImQrhh0elkj3hRdMWNBLTIvNmJbzhrrAZFbbnvhZ85jtU8NdztRjPLf4deq5SUCY8ZIHt4ygw1535XiD4hFiBNyOMhtP4LQx06YzDiDY8u5DPyEfu8gCMTsgxN6ObG9u9IcPEEUwIIz82phxdK-_bJ2NN1WpjVVe-FpQ5D0LpUS0oCFPYPFSO7pUT6lhDe8avaOSIse36kVDHPg3At4ApOCQPuO25IqBMEBZwuVRCXUAfuUz-WJqarSDngxxIJ-Q39RAhS8vg5noFSa1S12tlatXVqmGZrMpptc40A_7TSpUaNa1crWs1GFFnVFet2qQ20alhVmsN1axUzerY8dg3cJC3yhxqzzo0cO7SpUauHIgM0cCmphPT4n0Hpi_sm_X1j7J3I2WxWuJZCw5GysLPSFnwkBktKOLOxyen_E75UYhfbHW16kdM7ZCwJAwr1hemP1PH4eKYCooeBFDgRWKTj-TVjl5SQ1v8pBwAvRN-u-4uSeSMKjmMCI_wW84imcWJoxVLG_3BtZRCq8m8Ndgw0qdhiJFeTbv1OXxOnQj3F6RIAm0xaFMyw4BSaSUHNu2Pkfs2TQhUCNcLgnFo37hURAEvwirHwDMYD8d4H4lbL2wSsAxjRu7IhbsVFm03ixfYzvMnZ9quIO_IW7U5Qh2KyQAOnmXnQWCewyyEDv-D8A3jdPC1cFrRtmjNWkxybyEsKV94E4ogYsIL8iFK8j3H362hTlS1AjTGEZQv_cj8xu_i92qlalV03Zwws86YCovWGtyqMMZNo1bWJ4Zaa1RNUzc1WKvV0A1qqpZqAjVqXW_Uy5SCbRli805w5k34yCWPvUqvSLPZXCTNFLiA_M2dPzOKpxkcOIebbbwqwZqfAEgOBOwhQejMi1xxSAIO6e-GuLHg1_02GwiEkP75WMM_x92rXwSCV-wyQUBKRHuzhw0E0kQMqgTS-dDtdX4FyI-9J21k5JFXwsj_Aogf0JsZXSqJyF9qsVwtqruG58UxEu9VkLFiKduoI2WfbH15nGQKnpP2g0SNpTwXpHAnQr6mKU_JCAh2XBbIsz8ksygUxOF0zmO1hS8EAGyDvMFZjT_msiJeAEkF7bzbPs1EpXf5qfPHZesM77sfdgKS6euiCDmAuiHmAwqOB5Q1A1JBf4P-mfR78bl1jv_bJ5326UX341Vn0P3w5245EvB_IjvgB_AVxL6B84MURSHGc3i4JYUTIJKEbu-qf9rJIJz126fD7qdOggOld9A_3xGIuC1iNpDmu_j8SVNjG44USLd3fjns9o47X9Dh5fBL_6p1drmr3C8BwXyUob-C2pJDzQCIZCEZXmPZXYR8tue8i0-6WL19LWIRyt_kGOlfDgFOjGApQitsfRx0WsPOYHjS6vUHcRptZiQpNq_VxFnGzgLoEkEJkF53I-f7M4KVScg8H_YH7Bc38ne1kQDpnF3sf-yuAnnUGfzilDURtJaFH4QoVlbkOKmMFuQTuX3hhE50hnDoMtxBBkNbYhGaQX-IiVhelrKfysfDgra7lskrD2RvKXtyQdtTwfYStFTLHq4KEiDlHdw9_67B7ds7_tnJ9jCQl1Q-Q18Dv1u5Cw0pb6e-RtajgLHJd3AneZSvi5bqgXzrIt-labnQj6HQV-SwpaUd3HrfpzzgcV8oKcjjpk3c1sB-ShEtYWcGcKRfrni_2hVSVBiYNL2Up_hcxvZQ2uECg_H3OfYgoAiUDPqBPQc2oN-20t5Tyo-8sAsF3gPqhnhsI3_3SnyYQ3sJXMG5CiVffJArRzosPHkyXMw5gUoesGhs0rAMQ6tNrIrJjWpZNynnRoVrDd1Sdd3grFHWGKyt0mjUaB1IME3e4Gq5DsQZNex3gCRwl_FeNDOR3iq0WIxaHf6mHR9Q5_dZbK6V0PGE8vUHdLfwJbKoHJU1XVWhUZzUBHIda1PvF72V52ndenCaBNBmlxFbTqf_IHMwprIMuqDCCyHllSPDAE6Qp6yVruE-9aKA8f5DRKUcr1ksg0E0Cd7-BdDnHZk= diff --git a/website/docs/guides/optimization.md b/website/docs/guides/optimization.md index e19e0b09f..8252f7c02 100644 --- a/website/docs/guides/optimization.md +++ b/website/docs/guides/optimization.md @@ -184,14 +184,13 @@ You can create an `Artifact` for a fully hand-written contract so it becomes pos In the [addInput() method][addInput()] on the TransactionBuilder you can provide a custom `Unlocker` ```ts -transactionBuilder.addInput(utxo: Utxo, unlocker: Unlocker, options?: InputOptions): this +transactionBuilder.addInput(utxo: SpendableUtxo, unlocker: Unlocker, options?: InputOptions): this ``` the `Unlocker` interface is the following: ```ts interface Unlocker { - generateLockingBytecode: () => Uint8Array; generateUnlockingBytecode: (options: GenerateUnlockingBytecodeOptions) => Uint8Array; } diff --git a/website/docs/language/contracts.md b/website/docs/language/contracts.md index b6e45949c..5712dd939 100644 --- a/website/docs/language/contracts.md +++ b/website/docs/language/contracts.md @@ -60,7 +60,7 @@ contract TransferWithTimeout(pubkey sender, pubkey recipient, int timeout) { ``` :::note -The functions described here are top-level contract functions, which act as the entry points for spending from the contract. CashScript does not yet support user-defined reusable functions that can be called from within other functions. Callable functions are likely coming in CashScript v0.14, which would also allow function calls inside loops and loops inside reusable functions. +The functions described here are top-level contract functions, which act as the entry points for spending from the contract. CashScript also supports user-defined reusable functions that are declared at the top level of a file (outside the contract) and can be called from contract functions and from each other. See [User-defined functions](#user-defined-functions) below. ::: ### Function Arguments @@ -79,6 +79,164 @@ In CashScript the types for the function arguments are **not** enforced automati The typings for function arguments are enforced by default for boolean values and bounded bytes types such as `bytes20` and `bytes32`. ::: +## User-defined functions +Reusable functions are declared at the **top level** of a `.cash` file, outside the contract. The compiler chooses between inlining their bodies and sharing them with the BCH VM's native function opcodes (`OP_DEFINE`/`OP_INVOKE`), based on the resulting bytecode size. Single-use functions are inlined; recursive functions remain shared definitions. + +A function may return a **single value** using a `returns (T)` clause, and is called from contract functions or from other top-level functions: + +```solidity +pragma cashscript ^0.14.0; + +function double(int a) returns (int) { + return a * 2; +} + +function addThenDouble(int a, int b) returns (int) { + return double(a + b); +} + +contract Example() { + function spend(int x) { + require(addThenDouble(x, 1) == 8); + } +} +``` + +A function without a `returns` clause is a **void** function — it performs only `require` checks and is called as a statement: + +```solidity +function requirePositive(int a) { + require(a > 0); +} + +contract Example() { + function spend(int x) { + requirePositive(x); + require(x < 100); + } +} +``` + +A function can also return **multiple values** by declaring `returns (T1, T2, ...)` and returning a comma-separated list. A call to such a function must be destructured into exactly one variable per return value: + +```solidity +function divmod(int a, int b) returns (int, int) { + return a / b, a % b; +} + +contract Example() { + function spend(int x) { + int quotient, int remainder = divmod(x, 3); + require(quotient == 4); + require(remainder == 1); + } +} +``` + +You can also destructure the return value into existing variables instead of declaring new ones: + +```solidity +function nextFib(int a, int b) returns (int, int) { + return b, a + b; +} + +contract Example() { + function spend(int fib5) { + int current = 0; + int next = 1; + for (int i = 0; i < 5; i = i + 1) { + (current, next) = nextFib(current, next); + } + require(current == fib5); + } +} +``` + +Declarations and reassignments can be mixed freely in a single destructuring (e.g. `(int fresh, current, next) = step(current, next);`). Inside loops and branches, listing declarations before reassignments compiles to slightly smaller bytecode. + +:::info +`checkSig`, `checkMultiSig` and `this.activeBytecode` cannot be used inside a user-defined function, since they would apply to the function body rather than the contract. Use them in a contract function instead (`checkDataSig` is allowed). +::: + +### Limitations +This first version of user-defined functions is intentionally limited in scope: + +- A value-returning function must end with a single `return` statement (no early or conditional returns — compute into a variable and return it at the end). +- A void function must end with a `require` statement, just like contract functions (when it ends with an if-statement or loop, every branch must end with a `require`). + +:::note +Recursive and mutually recursive functions are allowed and compile fine. At runtime the VM control stack is limited to 100 entries, shared between recursion depth and nested `if` and loop blocks, so excessively deep recursion will fail when the contract gets spent. +::: + +## Global constants +Global constants are declared at the **top level** of a `.cash` file, outside the contract, and can be used by contract functions and user-defined functions. Their initialiser is evaluated at compile time and must resolve to a single constant value: literals, references to previously declared constants, integer arithmetic (`+`, `-`, `*`, `/`, `%`) and string/bytes concatenation (`+`) are supported. Other expressions, such as casts, comparisons or function calls, are not. + +```solidity +int constant MAX_ATTEMPTS = 3; +int constant TIMEOUT = 2 hours; +int constant EXTENDED_TIMEOUT = TIMEOUT + 30 minutes; +bytes32 constant EMPTY_HASH = 0x0000000000000000000000000000000000000000000000000000000000000000; + +contract Example() { + function spend(int attempts) { + require(attempts < MAX_ATTEMPTS); + require(tx.time >= EXTENDED_TIMEOUT); + } +} +``` + +The resolved value must be assignable to the declared type. Constants may reference other constants, as long as those are declared (or imported) before they are used. Global constants are immutable, and their names share the global namespace with user-defined functions and built-in symbols. Parameters and local variables cannot shadow them. + +Global constants do not become constructor arguments or mutable stack variables. The compiler treats them like zero-argument value-returning functions internally: small values and one-use constants are generally inlined, while larger values used repeatedly can be shared with `OP_DEFINE`/`OP_INVOKE`. + +## Importing functions and constants from other files +Top-level functions and constants can be split across files and pulled in with an `import` directive, which makes the imported functions and constants available as if they were declared locally. All `import` directives must appear at the **top of the file** after any `pragma` directives and before any constant, function or contract definitions. Cyclic imports are not allowed and result in a compile error. + +Import paths starting with `./`, `../` or `/` are resolved relative to the importing file: from the filesystem when compiling with [`compileFile`](/docs/compiler#compilefile), or from the `files` compiler option when using [`compileString`](/docs/compiler#compilestring). + +```solidity +// math.cash +int constant FACTOR = 2; + +function double(int a) returns (int) { + return a * FACTOR; +} +``` + +```solidity +// main.cash +pragma cashscript ^0.14.0; +import "./math.cash"; + +contract Main() { + function spend(int x) { + require(double(x) == 8); + } +} +``` + +Imported function and constant names share a single global namespace, so a name may only be defined once across the whole import graph. Files reached through more than one import path (diamond imports) are resolved once. + +Imported files can declare their own [`pragma` directives](#pragma), and every pragma across the whole import graph — the main file and all (transitively) imported files — must be satisfied by the compiler version. + +### Importing from npm packages +Import paths that do *not* start with `./`, `../` or `/` are treated as package imports and are resolved from `node_modules`, mirroring Node.js module resolution. This makes it possible to publish reusable CashScript functions as npm packages and import them by package name. The import path must contain the full path of the `.cash` file within the package. + +```solidity +pragma cashscript ^0.14.0; +import "@example/math-lib/math.cash"; + +contract Main() { + function spend(int x) { + require(double(x) == 8); + } +} +``` + +Relative imports *inside* an imported package are resolved relative to the package's own files, so packages can split their functions across multiple internal files. + +When compiling with [`compileString`](/docs/compiler#compilestring), package imports are looked up verbatim in the `files` compiler option. The example above would require a `files` key of `@example/math-lib/math.cash`. + ## Statements CashScript functions are made up of a collection of statements that determine whether money may be spent from the contract. @@ -107,7 +265,7 @@ contract P2PKH(bytes20 pkh) { Variables can be declared by specifying their type and name. All variables need to be initialised at the time of their declaration, but can be reassigned later on — unless specifying the `constant` keyword. Since CashScript is strongly typed and has no type inference, it is not possible to use keywords such as `var` or `let` to declare variables. :::note -CashScript disallows variable shadowing and unused variables. +CashScript disallows variable shadowing, and the compiler emits a warning for unused variables unless they are explicitly marked `unused`. ::: #### Example @@ -116,6 +274,25 @@ int myNumber = 3000; string constant myString = 'Bitcoin Cash'; ``` +### Intentionally unused values + +A parameter or local variable that is declared but never used results in a compiler warning. If the variable is intended to be unused, this warning can be silenced by marking the variable as `unused`. Unused parameters and local variables are dropped from the stack immediately after their declaration, and no [parameter type enforcement](/docs/compiler#enforcefunctionparametertypes) is generated for unused parameters. + +Some use cases for intentionally unused values include padding the contract bytecode in order to get a higher opcost budget, or nonces in order to differentiate between similar contracts. + +The compiler also warns when a value is assigned to a variable that is never read afterwards, since such an assignment has no effect on the contract. + +#### Example + +```solidity +contract Versioned(bytes unused reserved) { + function spend(int value, bytes unused padding) { + int unused discarded = value + 1; + require(value == 1); + } +} +``` + ### Variable assignment After their initial declaration, any variable can be reassigned later on. CashScript supports regular assignment with `=`, the compound assignment operators `+=` and `-=`, and the increment and decrement operators `++` and `--`. The compound and increment/decrement operators are only valid on `int` variables. @@ -255,18 +432,18 @@ contract P2PKH(bytes20 pkh) { ## Scope -CashScript uses nested scopes for parameters, variables and global functions. There cannot be two identical names within the same scope or within a nested scope. +CashScript uses nested scopes for global constants, parameters, variables and global functions. There cannot be two identical names within the same scope or within a nested scope. There are the following scopes in the nesting order: -- **Global scope** - contains global functions and global variables (e.g. `sha256`, `hash160`, `checkSig`, etc.) +- **Global scope** - contains global constants, global functions and built-in symbols (e.g. `sha256`, `hash160`, `checkSig`, etc.) - **Contract scope** - contains contract parameters - **Function scope** - contains function parameters and local variables - **Local scope** - contains local variables introduced by control flow blocks (e.g. `if`, `else`) #### Example ```solidity -// Global scope (contains global functions and global variables like sha256, hash160, checkSig, etc.) +// Global scope (contains global constants, functions and built-in symbols like sha256, hash160, checkSig, etc.) // Contract scope (contains contract parameters - sender, recipient, timeout) contract TransferWithTimeout( diff --git a/website/docs/language/types.md b/website/docs/language/types.md index cdcdf61fb..4263202f0 100644 --- a/website/docs/language/types.md +++ b/website/docs/language/types.md @@ -127,7 +127,7 @@ Operators: - `!=` (inequality) ### Transaction Signature -`sig`: Byte sequence representing a transaction signature. Generally 65 bytes long. +`sig`: Byte sequence representing a transaction signature. Generally 65 bytes long: a 64-byte Schnorr signature followed by a single sighash flag byte that indicates which parts of the transaction were signed. See [HashType](/docs/sdk/signature-templates#hashtype) for the meaning of this byte. Operators: @@ -161,13 +161,21 @@ string cash = bitcoinCash.split(8)[1]; It is not supported to use a variable for the tupleIndex. Instead you can assign both sides of the tuple as shown below and use either element conditional on the value of the variable. ::: -It is also possible to assign both sides of the tuple at once with a destructuring syntax: +It is also possible to assign both sides of the tuple at once with a destructuring syntax, allowing both new variable declarations and reassignments: ```solidity string hello, string world = "Hello World".split(6); require(hello + "World" == "Hello " + world); ``` +Declarations and reassignments can be mixed freely in a single destructuring (e.g. `(bytes fresh, existing) = x.split(1);`). The target list may optionally be wrapped in parentheses. + +Newly declared targets accept the same modifiers as regular variable declarations: `constant` prevents later reassignment, while `unused` discards the value immediately, which is useful when only part of the tuple is needed: + +```solidity +bytes unused ignored, bytes constant tail = someBytes.split(4); +``` + ## Type Casting Type casting can be done both explicitly and implicitly depending on the type. `pubkey`, `sig` and `datasig` can be implicitly cast to `bytes`, meaning they can be used anywhere where you would normally use a `bytes` type. Explicit type casting can be done with a broader range of types, but is still limited. The syntax of this explicit type casting is illustrated below: diff --git a/website/docs/releases/migration-notes.md b/website/docs/releases/migration-notes.md index 8c2864e9b..e182753af 100644 --- a/website/docs/releases/migration-notes.md +++ b/website/docs/releases/migration-notes.md @@ -2,6 +2,64 @@ title: Migration Notes --- +## v0.13 to v0.14 + +### CashScript SDK + +#### SignatureTemplate + +The `getHashType()`, `getPublicKey()` and `getSignatureAlgorithm()` are now simple `sighashType`, `publicKey` and `signatureAlgorithm` properties. + +```ts +// before +const hashType = signatureTemplate.getHashType(); +const publicKey = signatureTemplate.getPublicKey(); +const signatureAlgorithm = signatureTemplate.getSignatureAlgorithm(); + +// after +const sighashType = signatureTemplate.sighashType; +const publicKey = signatureTemplate.publicKey; +const signatureAlgorithm = signatureTemplate.signatureAlgorithm; +``` + +Note that `getHashType()` returned the sighash type with the BCH fork ID flag applied, while the `sighashType` property returns the configured sighash type as it was passed to the constructor. + +The `bchForkId` parameter has been removed from `generateSignature()`. A signature without the BCH fork ID flag is invalid under BCH consensus rules, so the flag is now always applied when signing. + +```ts +// before +const signature = signatureTemplate.generateSignature(sighash, bchForkId); + +// after +const signature = signatureTemplate.generateSignature(sighash); +``` + +The `HashType` enum has been renamed to `SighashType`. + +```ts +// before +const signatureTemplate = new SignatureTemplate(wif, HashType.SIGHASH_ALL | HashType.SIGHASH_UTXOS); + +// after +const signatureTemplate = new SignatureTemplate(wif, SighashType.SIGHASH_ALL | SighashType.SIGHASH_UTXOS); +``` + +#### UTXOs must include their locking bytecode + +UTXOs returned by network providers now include a `lockingBytecode` field with the actual locking bytecode of the UTXO, and input UTXOs passed to the `TransactionBuilder` are required to include this field. + +If you fetch UTXOs from a standard network provider, no code changes are needed. If you construct UTXOs manually (e.g. from your own indexer or persisted data), you need to add the `lockingBytecode` field: + +```ts +// before +const utxo = { txid, vout, satoshis }; + +// after +const utxo = { txid, vout, satoshis, lockingBytecode }; +``` + +Since the spent UTXO's `lockingBytecode` is now the source of truth for the locking script, the `generateLockingBytecode()` method was removed from the `Unlocker` interface. If you implement custom unlockers, remove the `generateLockingBytecode()` method from your implementation. + ## v0.12 to v0.13 ### cashc compiler diff --git a/website/docs/releases/release-notes.md b/website/docs/releases/release-notes.md index 41dcb20b1..441502143 100644 --- a/website/docs/releases/release-notes.md +++ b/website/docs/releases/release-notes.md @@ -2,10 +2,39 @@ title: Release Notes --- +## v0.14.0-next.5 + +This release contains several breaking changes, please refer to the [migration notes](/docs/releases/migration-notes) for more information. + +#### cashc compiler +- :sparkles: Add support for user-defined reusable functions, including multiple return values. +- :sparkles: Add support for top-level global constants, including simple arithmetic / concatenation operations. +- :sparkles: Add support for `import` directives to share user-defined functions across files, including imports from `node_modules`. +- :sparkles: Add support for reassigning existing variables in tuple destructuring (e.g. `(a, b) = swap(a, b)`), optionally mixed with fresh declarations. +- :sparkles: Add `unused` modifier for parameters or variables that are intentionally unused. +- :hammer_and_wrench: Update `compileString` to take an optional `files` object for filesystem-free import resolution. +- :hammer_and_wrench: Unused variables that are not marked `unused` now produce a compiler warning instead of a compilation error. Warnings are printed with `console.warn`, or passed to the new `warningListener` compiler option. +- :hammer_and_wrench: Add a compiler warning for values assigned to a variable that are never read afterwards. +- :bug: Fix bug where date literal parsing was different per locale, it now uses UTC. +- :racehorse: Add new `OP_SWAP OP_MUL`, `OP_NOT OP_NOT` and `TO_ALTSTACK OP_FROMALTSTACK` optimisations. +- :racehorse: Greatly improve compiler speed for very large contracts. + +#### CashScript SDK +- :sparkles: Add support for debugging user-defined functions, including stack traces for nested functions. +- :sparkles: Add a `validateTransactions` option (default: `true`) to the `MockNetworkProvider` to validate sent transactions against the BCH VM. +- :hammer_and_wrench: MockNetworkProvider now warns instead of errors when resubmitting an already-seen transaction. +- :hammer_and_wrench: Add `lockingBytecode` field to the `Utxo` interface, set automatically on all UTXOs returned by network providers. +- :hammer_and_wrench: **BREAKING**: Replace the `SignatureTemplate`'s `getHashType()`, `getPublicKey()` and `getSignatureAlgorithm()` methods with the `sighashType`, `publicKey` and `signatureAlgorithm` properties. +- :hammer_and_wrench: **BREAKING**: Remove the `bchForkId` parameter from `SignatureTemplate`'s `generateSignature()` method, since BCH consensus rules always require the fork ID flag. +- :hammer_and_wrench: **BREAKING**: Rename the `HashType` enum to `SighashType`. +- :hammer_and_wrench: **BREAKING**: The `TransactionBuilder` now requires UTXOs to include their `lockingBytecode`, and validates it against the provided unlocker. +- :boom: **BREAKING**: Remove `generateLockingBytecode()` from the `Unlocker` interface. + + ## v0.13.3 #### CashScript SDK -- :bug: Fix issue where `getTransactionSize()` undersized inputs when using `placeholderP2PKHUnlocker()` +- :bug: Fix issue where `getTransactionSize()` undersized inputs when using `placeholderP2PKHUnlocker()`. ## v0.13.2 diff --git a/website/docs/sdk/electrum-network-provider.md b/website/docs/sdk/electrum-network-provider.md index 51e37f936..25fa5ac78 100644 --- a/website/docs/sdk/electrum-network-provider.md +++ b/website/docs/sdk/electrum-network-provider.md @@ -54,16 +54,21 @@ const provider = new ElectrumNetworkProvider('chipnet', { hostname }); ### getUtxos() ```ts -async provider.getUtxos(address: string): Promise; +async provider.getUtxos(address: string): Promise; ``` Returns all UTXOs on specific address. Both confirmed and unconfirmed UTXOs are included. ```ts +interface SpendableUtxo extends Utxo { + lockingBytecode: string; +} + interface Utxo { txid: string; vout: number; satoshis: bigint; token?: TokenDetails; + lockingBytecode?: string; } interface TokenDetails { @@ -82,7 +87,7 @@ const userUtxos = await provider.getUtxos(userAddress) ``` ### getUtxosForLockingBytecode() ```ts -async provider.getUtxosForLockingBytecode(lockingBytecode: Uint8Array | string): Promise; +async provider.getUtxosForLockingBytecode(lockingBytecode: Uint8Array | string): Promise; ``` Returns all UTXOs for a specific locking bytecode. Both confirmed and unconfirmed UTXOs are included. diff --git a/website/docs/sdk/instantiation.md b/website/docs/sdk/instantiation.md index 858aa066c..d6ef2d8c5 100644 --- a/website/docs/sdk/instantiation.md +++ b/website/docs/sdk/instantiation.md @@ -161,17 +161,22 @@ const contractBalance = await contract.getBalance() ### getUtxos() ```ts -async contract.getUtxos(): Promise +async contract.getUtxos(): Promise ``` Returns all UTXOs that can be spent by the contract. Both confirmed and unconfirmed UTXOs are included. ```ts +interface SpendableUtxo extends Utxo { + lockingBytecode: string; +} + interface Utxo { txid: string; vout: number; satoshis: bigint; token?: TokenDetails; + lockingBytecode?: string; } ``` diff --git a/website/docs/sdk/network-provider.md b/website/docs/sdk/network-provider.md index 24062f68b..cd21ad9bd 100644 --- a/website/docs/sdk/network-provider.md +++ b/website/docs/sdk/network-provider.md @@ -24,16 +24,21 @@ const connectedNetwork = provider.network; ### getUtxos() ```ts -async provider.getUtxos(address: string): Promise; +async provider.getUtxos(address: string): Promise; ``` Returns all UTXOs on specific address. Both confirmed and unconfirmed UTXOs are included. ```ts +interface SpendableUtxo extends Utxo { + lockingBytecode: string; +} + interface Utxo { txid: string; vout: number; satoshis: bigint; token?: TokenDetails; + lockingBytecode?: string; } interface TokenDetails { @@ -46,6 +51,8 @@ interface TokenDetails { } ``` +The `lockingBytecode` field contains the hex-encoded locking bytecode of the UTXO. It is set automatically on all UTXOs returned by network providers, and is required by the `TransactionBuilder` to spend the UTXO. + #### Example ```ts const userUtxos = await provider.getUtxos(userAddress) @@ -53,7 +60,7 @@ const userUtxos = await provider.getUtxos(userAddress) ### getUtxosForLockingBytecode() ```ts -async provider.getUtxosForLockingBytecode(lockingBytecode: Uint8Array | string): Promise; +async provider.getUtxosForLockingBytecode(lockingBytecode: Uint8Array | string): Promise; ``` Returns all UTXOs for a specific locking bytecode. Both confirmed and unconfirmed UTXOs are included. @@ -98,7 +105,7 @@ const txId = await provider.sendRawTransaction(txHex) ## Custom NetworkProviders -A big strength of the NetworkProvider setup is that it allows you to implement custom providers. So if you want to use a new or different BCH indexer for network information, it is simple to add support for it by creating your own `NetworkProvider` adapter by implementing the [NetworkProvider interface](https://github.com/CashScript/cashscript/blob/master/packages/cashscript/src/network/NetworkProvider.ts). +A big strength of the NetworkProvider setup is that it allows you to implement custom providers. So if you want to use a new or different BCH indexer for network information, it is simple to add support for it by creating your own `NetworkProvider` adapter by implementing the [NetworkProvider interface](https://github.com/CashScript/cashscript/blob/master/packages/cashscript/src/network/NetworkProvider.ts). Note that UTXOs returned by a `NetworkProvider` must include their `lockingBytecode`, which is required by the `TransactionBuilder` to spend them. You can create a PR to add your custom `NetworkProvider` to the CashScript codebase to share this functionality with others. It is required to have basic automated tests for any new `NetworkProvider`. diff --git a/website/docs/sdk/other-network-providers.md b/website/docs/sdk/other-network-providers.md index 019a50d8a..e66d1e3f6 100644 --- a/website/docs/sdk/other-network-providers.md +++ b/website/docs/sdk/other-network-providers.md @@ -15,11 +15,6 @@ The `MockNetworkProvider` has extra methods to enable this local emulation such You can read more about the `MockNetworkProvider` and automated tests on the [testing setup](/docs/sdk/testing-setup) page. ```ts -interface MockNetworkProviderOptions { - updateUtxoSet?: boolean; - vmTarget?: VmTarget; -} - interface MockNetworkProvider extends NetworkProvider { options: MockNetworkProviderOptions; vmTarget: VmTarget; @@ -29,17 +24,27 @@ interface MockNetworkProvider extends NetworkProvider { // Hardcode the block height setBlockHeight(newBlockHeight: number): void; - // Add a UTXO to the UTXO set of the mock network - addUtxo(addressOrLockingBytecode: string, utxo: Utxo): Utxo; + // Add a UTXO to the UTXO set of the mock network, returns the UTXO including its locking bytecode + addUtxo(addressOrLockingBytecode: string, utxo: Utxo): SpendableUtxo; // Reset the UTXO set and transaction list of the mock network reset(): void; } ``` -The `updateUtxoSet` option is used to determine whether the UTXO set should be updated after a transaction is sent. If `updateUtxoSet` is `true` (default), the UTXO set will be updated to reflect the new state of the mock network. If `updateUtxoSet` is `false`, the UTXO set will not be updated. +### Options + +```ts +interface MockNetworkProviderOptions { + updateUtxoSet?: boolean; + validateTransactions?: boolean; + vmTarget?: VmTarget; +} +``` -The `vmTarget` option defaults to the current VM of `BCH_2026_05`, but this can be changed to test your contract against different BCH virtual machine targets. +- `updateUtxoSet` (default `true`) — update the in-memory UTXO set after a transaction is sent, consuming the spent UTXOs and adding the transaction's outputs. +- `validateTransactions` (default `true`) — evaluate sent transactions against the BCH VM using the actual locking bytecode of the spent UTXOs, rejecting transactions that a real node would reject. Requires `updateUtxoSet`. +- `vmTarget` (default `BCH_2026_05`) — the BCH virtual machine version used for local debugging and transaction validation. #### Example ```ts diff --git a/website/docs/sdk/signature-templates.md b/website/docs/sdk/signature-templates.md index 9f9889663..da6a4dc29 100644 --- a/website/docs/sdk/signature-templates.md +++ b/website/docs/sdk/signature-templates.md @@ -16,7 +16,7 @@ In place of a signature, a `SignatureTemplate` can be passed, which will generat ```ts new SignatureTemplate( signer: Keypair | Uint8Array | string, - hashtype?: HashType, + sighashType?: SighashType, signatureAlgorithm?: SignatureAlgorithm ) ``` @@ -37,39 +37,54 @@ const transferDetails = await new TransactionBuilder({ provider }) .send(); ``` -The `hashtype` and `signatureAlgorithm` options are covered under ['Advanced Usage'](/docs/sdk/signature-templates#advanced-usage). +The `sighashType` and `signatureAlgorithm` options are covered under ['Advanced Usage'](/docs/sdk/signature-templates#advanced-usage). -## SignatureTemplate Methods +## SignatureTemplate Properties -### unlockP2PKH() +### privateKey -Importantly the `SignatureTemplate` can also be used to generate the `Unlocker` for a P2PKH UTXO in the following way: +The `SignatureTemplate` exposes the private key it signs with as a property. Whichever format the `signer` was passed in (WIF, hex string, `Uint8Array` or a `Keypair` object), it is decoded to raw private key bytes. ```ts -signatureTemplate.unlockP2PKH(): Unlocker +signatureTemplate.privateKey: Uint8Array ``` -#### Example +### publicKey + +The `SignatureTemplate` exposes the matching public key as a property: + ```ts -import { aliceTemplate, aliceAddress, transactionBuilder } from './somewhere.js'; +signatureTemplate.publicKey: Uint8Array +``` -const aliceUtxos = await provider.getUtxos(aliceAddress); -transactionBuilder.addInput(aliceUtxos[0], aliceTemplate.unlockP2PKH()); +### sighashType + +The configured sighash type is exposed as a property. Note that the BCH fork ID flag is always applied on top of this value when signing, since it is required by BCH consensus. Its possible values are covered under ['Advanced Usage'](/docs/sdk/signature-templates#sighashtype). + +### signatureAlgorithm + +The configured signature algorithm is exposed as a property. Its possible values are covered under ['Advanced Usage'](/docs/sdk/signature-templates#signaturealgorithm). + +```ts +signatureTemplate.signatureAlgorithm: SignatureAlgorithm ``` -### getPublicKey() +## SignatureTemplate Methods -The `SignatureTemplate` also has a helper method to get the matching PublicKey in the following way: +### unlockP2PKH() + +Importantly the `SignatureTemplate` can also be used to generate the `Unlocker` for a P2PKH UTXO in the following way: ```ts -signatureTemplate.getPublicKey(): Uint8Array +signatureTemplate.unlockP2PKH(): Unlocker ``` #### Example ```ts -import { aliceTemplate } from './somewhere.js'; +import { aliceTemplate, aliceAddress, transactionBuilder } from './somewhere.js'; -const alicePublicKey = aliceTemplate.getPublicKey() +const aliceUtxos = await provider.getUtxos(aliceAddress); +transactionBuilder.addInput(aliceUtxos[0], aliceTemplate.unlockP2PKH()); ``` ### signMessageHash() @@ -91,12 +106,12 @@ const signature = aliceTemplate.signMessageHash(sha256(hexToBin('000000000000000 ## Advanced Usage -### HashType +### SighashType -The default `hashtype` is `HashType.SIGHASH_ALL | HashType.SIGHASH_UTXOS` because this is the most secure option for smart contract use cases. +The default `sighashType` is `SighashType.SIGHASH_ALL | SighashType.SIGHASH_UTXOS` because this is the most secure option for smart contract use cases. ```ts -export enum HashType { +export enum SighashType { SIGHASH_ALL = 0x01, SIGHASH_NONE = 0x02, SIGHASH_SINGLE = 0x03, @@ -105,15 +120,25 @@ export enum HashType { } ``` +`SIGHASH_ALL`, `SIGHASH_NONE` and `SIGHASH_SINGLE` choose which outputs are signed, while `SIGHASH_UTXOS` and `SIGHASH_ANYONECANPAY` are modifiers that can be OR'd on top to change what else is committed to. For a full technical breakdown of the signing serialization and every valid flag combination, see the [Bitcoin Cash transaction signing reference][tx-signing]. + +| Flag | Value | Commits to | Typical use | +| --- | --- | --- | --- | +| `SIGHASH_ALL` | `0x01` | all inputs and **all** outputs | sign the exact transaction | +| `SIGHASH_NONE` | `0x02` | all inputs, but **no** outputs | let the outputs be decided after signing | +| `SIGHASH_SINGLE` | `0x03` | all inputs, and only the **one** output at the same index as the signed input | pair a single input to a single output | +| `SIGHASH_UTXOS` | `0x20` | *(modifier)* additionally commits to the full contents of the UTXOs being spent | recommended for all contracts — see below | +| `SIGHASH_ANYONECANPAY` | `0x80` | *(modifier)* only the **current** input, allowing other inputs to be added | crowdfunding-style transactions where anyone can add an input | + #### Example ```ts const wif = 'L4vmKsStbQaCvaKPnCzdRArZgdAxTqVx8vjMGLW5nHtWdRguiRi1'; const signatureTemplate = new SignatureTemplate( - wif, HashType.SIGHASH_ALL | HashType.SIGHASH_UTXOS + wif, SighashType.SIGHASH_ALL | SighashType.SIGHASH_UTXOS ); -const configuredHashType = signatureTemplate.getHashType() +const configuredSighashType = signatureTemplate.sighashType ``` ### SignatureAlgorithm @@ -131,13 +156,14 @@ export enum SignatureAlgorithm { ```ts const wif = 'L4vmKsStbQaCvaKPnCzdRArZgdAxTqVx8vjMGLW5nHtWdRguiRi1'; -const hashType = HashType.SIGHASH_ALL | HashType.SIGHASH_UTXOS +const sighashType = SighashType.SIGHASH_ALL | SighashType.SIGHASH_UTXOS const signatureAlgorithm = SignatureAlgorithm.SCHNORR -const signatureTemplate = new SignatureTemplate(wif, hashType,signatureAlgorithm); +const signatureTemplate = new SignatureTemplate(wif, sighashType,signatureAlgorithm); -const configuredSignatureAlgorithm = signatureTemplate.getSignatureAlgorithm() +const configuredSignatureAlgorithm = signatureTemplate.signatureAlgorithm ``` [wif]: https://en.bitcoin.it/wiki/Wallet_import_format [ecpair]: https://bchjs.fullstack.cash/#api-ECPair [privatekey]: https://github.com/bitpay/bitcore/blob/master/packages/bitcore-lib-cash/docs/privatekey.md +[tx-signing]: https://documentation.cash/protocol/blockchain/transaction/transaction-signing.html diff --git a/website/docs/sdk/transaction-builder.md b/website/docs/sdk/transaction-builder.md index 6871f3179..ea322a7f6 100644 --- a/website/docs/sdk/transaction-builder.md +++ b/website/docs/sdk/transaction-builder.md @@ -54,7 +54,7 @@ The `allowImplicitFungibleTokenBurn` option is used to specify whether implicit ### addInput() ```ts -transactionBuilder.addInput(utxo: Utxo, unlocker: Unlocker, options?: InputOptions): this +transactionBuilder.addInput(utxo: SpendableUtxo, unlocker: Unlocker, options?: InputOptions): this ``` Adds a single input UTXO to the transaction that can be unlocked using the provided unlocker. The unlocker can be derived from a `SignatureTemplate` or a `Contract` instance's spending functions. The `InputOptions` object can be used to specify the sequence number of the input. The default sequence number is `0xfffffffe` (non-final sequence number). @@ -76,12 +76,12 @@ transactionBuilder.addInput(aliceUtxos[0], aliceTemplate.unlockP2PKH()); ### addInputs() ```ts -transactionBuilder.addInputs(utxos: Utxo[], unlocker: Unlocker, options?: InputOptions): this +transactionBuilder.addInputs(utxos: SpendableUtxo[], unlocker: Unlocker, options?: InputOptions): this transactionBuilder.addInputs(utxos: UnlockableUtxo[]): this ``` ```ts -interface UnlockableUtxo extends Utxo { +interface UnlockableUtxo extends SpendableUtxo { unlocker: Unlocker; options?: InputOptions; } diff --git a/yarn.lock b/yarn.lock index a8290daa9..6403d44da 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,20 +9,20 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/helper-string-parser@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" - integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== +"@babel/helper-string-parser@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz#7f0871d99824d23137d60f86fcf6130fd5a1b51f" + integrity sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw== "@babel/helper-validator-identifier@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== -"@babel/helper-validator-identifier@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" - integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== +"@babel/helper-validator-identifier@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz#bd87084ced0c796ec46bda492de6e83d29e89fc2" + integrity sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg== "@babel/highlight@^7.10.4": version "7.10.4" @@ -33,20 +33,20 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.5.tgz#0b0225ee90362f030efd644e8034c99468893b08" - integrity sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ== +"@babel/parser@^7.29.7": + version "7.29.8" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.8.tgz#9653716a2f10c677b98fbc63d4bfb000c302cf17" + integrity sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA== dependencies: - "@babel/types" "^7.28.5" + "@babel/types" "^7.29.8" -"@babel/types@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.5.tgz#10fc405f60897c35f07e85493c932c7b5ca0592b" - integrity sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA== +"@babel/types@^7.29.7", "@babel/types@^7.29.8": + version "7.29.8" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.8.tgz#1229eef31d85156d70fa3f4cd859376d0eaf6863" + integrity sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg== dependencies: - "@babel/helper-string-parser" "^7.27.1" - "@babel/helper-validator-identifier" "^7.28.5" + "@babel/helper-string-parser" "^7.29.7" + "@babel/helper-validator-identifier" "^7.29.7" "@bcoe/v8-coverage@^1.0.2": version "1.0.2" @@ -58,98 +58,111 @@ resolved "https://registry.yarnpkg.com/@bitauth/libauth/-/libauth-3.1.0-next.8.tgz#d130e5db6c3c8b24731c8d04c4091be07f48b0ee" integrity sha512-Pm+Ju+YP3JeBLLTiVrBnia2wwE4G17r4XqpvPRMcklElJTe8J6x3JgKRg1by0Xm3ZY6UFxACkEAoSA+x419/zA== -"@cspell/cspell-bundled-dicts@9.2.0": - version "9.2.0" - resolved "https://registry.yarnpkg.com/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-9.2.0.tgz#5bbc41142d03038457c637a99689e83e88014dbe" - integrity sha512-e4qb78SQWqHkRw47W8qFJ3RPijhSLkADF+T0oH8xl3r/golq1RGp2/KrWOqGRRofUSTiIKYqaMX7mbAyFnOxyA== +"@cspell/cspell-bundled-dicts@10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-10.0.1.tgz#c85650d13a208ab31173dcdc6809ada1983d7cde" + integrity sha512-WvkSDNX4Uyyj/ZgbPO6L38iFNMfK1EqsH1FteRiI2qLz6QZMXRFrIt12OqiWIplzZDDaVpBH9FCJOPJll0fjCQ== dependencies: "@cspell/dict-ada" "^4.1.1" "@cspell/dict-al" "^1.1.1" - "@cspell/dict-aws" "^4.0.12" - "@cspell/dict-bash" "^4.2.1" - "@cspell/dict-companies" "^3.2.2" - "@cspell/dict-cpp" "^6.0.9" + "@cspell/dict-aws" "^4.0.17" + "@cspell/dict-bash" "^4.2.2" + "@cspell/dict-companies" "^3.2.11" + "@cspell/dict-cpp" "^7.0.2" "@cspell/dict-cryptocurrencies" "^5.0.5" - "@cspell/dict-csharp" "^4.0.7" - "@cspell/dict-css" "^4.0.18" - "@cspell/dict-dart" "^2.3.1" - "@cspell/dict-data-science" "^2.0.9" - "@cspell/dict-django" "^4.1.5" - "@cspell/dict-docker" "^1.1.15" - "@cspell/dict-dotnet" "^5.0.10" + "@cspell/dict-csharp" "^4.0.8" + "@cspell/dict-css" "^4.1.1" + "@cspell/dict-dart" "^2.3.2" + "@cspell/dict-data-science" "^2.0.13" + "@cspell/dict-django" "^4.1.6" + "@cspell/dict-docker" "^1.1.17" + "@cspell/dict-dotnet" "^5.0.13" "@cspell/dict-elixir" "^4.0.8" - "@cspell/dict-en-common-misspellings" "^2.1.3" - "@cspell/dict-en-gb-mit" "^3.1.5" - "@cspell/dict-en_us" "^4.4.15" - "@cspell/dict-filetypes" "^3.0.13" + "@cspell/dict-en-common-misspellings" "^2.1.12" + "@cspell/dict-en-gb-mit" "^3.1.22" + "@cspell/dict-en_us" "^4.4.33" + "@cspell/dict-filetypes" "^3.0.18" "@cspell/dict-flutter" "^1.1.1" - "@cspell/dict-fonts" "^4.0.5" + "@cspell/dict-fonts" "^4.0.6" "@cspell/dict-fsharp" "^1.1.1" - "@cspell/dict-fullstack" "^3.2.7" + "@cspell/dict-fullstack" "^3.2.9" "@cspell/dict-gaming-terms" "^1.1.2" - "@cspell/dict-git" "^3.0.7" - "@cspell/dict-golang" "^6.0.23" + "@cspell/dict-git" "^3.1.0" + "@cspell/dict-golang" "^6.0.26" "@cspell/dict-google" "^1.0.9" "@cspell/dict-haskell" "^4.0.6" - "@cspell/dict-html" "^4.0.12" - "@cspell/dict-html-symbol-entities" "^4.0.4" + "@cspell/dict-html" "^4.0.15" + "@cspell/dict-html-symbol-entities" "^4.0.5" "@cspell/dict-java" "^5.0.12" "@cspell/dict-julia" "^1.1.1" "@cspell/dict-k8s" "^1.0.12" "@cspell/dict-kotlin" "^1.1.1" - "@cspell/dict-latex" "^4.0.4" + "@cspell/dict-latex" "^5.1.0" "@cspell/dict-lorem-ipsum" "^4.0.5" "@cspell/dict-lua" "^4.0.8" "@cspell/dict-makefile" "^1.0.5" - "@cspell/dict-markdown" "^2.0.12" - "@cspell/dict-monkeyc" "^1.0.11" - "@cspell/dict-node" "^5.0.8" - "@cspell/dict-npm" "^5.2.12" - "@cspell/dict-php" "^4.0.15" + "@cspell/dict-markdown" "^2.0.16" + "@cspell/dict-monkeyc" "^1.0.12" + "@cspell/dict-node" "^5.0.9" + "@cspell/dict-npm" "^5.2.38" + "@cspell/dict-php" "^4.1.1" "@cspell/dict-powershell" "^5.0.15" - "@cspell/dict-public-licenses" "^2.0.14" - "@cspell/dict-python" "^4.2.19" + "@cspell/dict-public-licenses" "^2.0.16" + "@cspell/dict-python" "^4.2.26" "@cspell/dict-r" "^2.1.1" - "@cspell/dict-ruby" "^5.0.9" - "@cspell/dict-rust" "^4.0.12" - "@cspell/dict-scala" "^5.0.8" - "@cspell/dict-shell" "^1.1.1" - "@cspell/dict-software-terms" "^5.1.4" + "@cspell/dict-ruby" "^5.1.1" + "@cspell/dict-rust" "^4.1.2" + "@cspell/dict-scala" "^5.0.9" + "@cspell/dict-shell" "^1.1.2" + "@cspell/dict-software-terms" "^5.2.2" "@cspell/dict-sql" "^2.2.1" "@cspell/dict-svelte" "^1.0.7" "@cspell/dict-swift" "^2.0.6" "@cspell/dict-terraform" "^1.1.3" "@cspell/dict-typescript" "^3.2.3" "@cspell/dict-vue" "^3.0.5" + "@cspell/dict-zig" "^1.0.0" -"@cspell/cspell-json-reporter@9.2.0": - version "9.2.0" - resolved "https://registry.yarnpkg.com/@cspell/cspell-json-reporter/-/cspell-json-reporter-9.2.0.tgz#c602929ca904dfd0d6e6377cebe79b7eddeef0a0" - integrity sha512-qHdkW8eyknCSDEsqCG8OHBMal03LQf21H2LVWhtwszEQ4BQRKcWctc+VIgkO69F/jLaN2wi/yhhMufXWHAEzIg== +"@cspell/cspell-json-reporter@10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@cspell/cspell-json-reporter/-/cspell-json-reporter-10.0.1.tgz#d64f9f4fe7c6ab70c5f8c625879e5e13a7fa4669" + integrity sha512-/nes1RGILec3WCBcoMOd0byNTBtnJuPaVz/+ZzqYkLtY7x58VMcBG5kyP6hPyN8cIwjRADE/SR43gwdXuqk/FA== dependencies: - "@cspell/cspell-types" "9.2.0" + "@cspell/cspell-types" "10.0.1" -"@cspell/cspell-pipe@9.2.0": - version "9.2.0" - resolved "https://registry.yarnpkg.com/@cspell/cspell-pipe/-/cspell-pipe-9.2.0.tgz#271ec44c0b64d55fd053c6410ca3604c91ba4a93" - integrity sha512-RO3adcsr7Ek+4511nyEOWDhOYYU1ogRs1Mo5xx3kDIdcKAJzhFdGry35T2wqft4dPASLCXcemBrhoS+hdQ+z+Q== +"@cspell/cspell-performance-monitor@10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@cspell/cspell-performance-monitor/-/cspell-performance-monitor-10.0.1.tgz#944e3ad0cf0b7b03755c807146c394b09d3d1560" + integrity sha512-9tVcHXwRnbazUv4WSG0h3MqV4+LgmLNgSALAQUflPPW0EMxTf7C4Dmv9cgxJyCEQrdnVKCr58nPPaahhz9LJUg== -"@cspell/cspell-resolver@9.2.0": - version "9.2.0" - resolved "https://registry.yarnpkg.com/@cspell/cspell-resolver/-/cspell-resolver-9.2.0.tgz#c6ab91877abb812ef9f87c4d361b1a14f60bef16" - integrity sha512-0Xvwq0iezfO71Alw+DjsGxacAzydqOAxdXnY4JknHuxt2l8GTSMjRwj65QAflv3PN6h1QoRZEeWdiKtusceWAw== +"@cspell/cspell-pipe@10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@cspell/cspell-pipe/-/cspell-pipe-10.0.1.tgz#caccc9dc8f937d7f7b4ffa949de48152bb3f9659" + integrity sha512-HPeXMD9AZ3V/qPkvQaPcak+C7cJ2z7JTHN8smd6J8L2aThLRky2cHc2OyeaHPSHB7WA47b4z2n5u5nawZhv5VQ== + +"@cspell/cspell-resolver@10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@cspell/cspell-resolver/-/cspell-resolver-10.0.1.tgz#8efc586e10e85f165a0e817ddbfd8774af73e99b" + integrity sha512-PIzkZHD1fGUQx1XteK2d1iQ0Mzq/maYcoB4jkvAiiR6WqP3MWYNKFdI9z+R5pOq5KgMfW+5Ig1q0oSR6h8irlA== dependencies: - global-directory "^4.0.1" + global-directory "^5.0.0" -"@cspell/cspell-service-bus@9.2.0": - version "9.2.0" - resolved "https://registry.yarnpkg.com/@cspell/cspell-service-bus/-/cspell-service-bus-9.2.0.tgz#72b960f37f74b26aba2910143c832b2b8732335c" - integrity sha512-ZDvcOTFk3cCVW+OjlkljeP7aSuV8tIguVn+GMco1/A+961hsEP20hngK9zJtyfpXqyvJKtvCVlyzS+z8VRrZGg== +"@cspell/cspell-service-bus@10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@cspell/cspell-service-bus/-/cspell-service-bus-10.0.1.tgz#3eaa35cc075a4fd5273833c9c8993e0f1891d307" + integrity sha512-y6NcIGP2IdXaBL4PVH8vxsr7K27wzz3Ech87UtUtrDSXAiVEOvXgAIknEOUVp59rTlUE8Rn4IRURC6f/hgMyfw== -"@cspell/cspell-types@9.2.0": - version "9.2.0" - resolved "https://registry.yarnpkg.com/@cspell/cspell-types/-/cspell-types-9.2.0.tgz#5767b839722f9402dc5ad4e8bea86138ecb84eeb" - integrity sha512-hL4ltFwiARpFxlfXt4GiTWQxIFyZp4wrlp7dozZbitYO6QlYc5fwQ8jBc5zFUqknuH4gx/sCMLNXhAv3enNGZQ== +"@cspell/cspell-types@10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@cspell/cspell-types/-/cspell-types-10.0.1.tgz#607361589364d41313b005650313aab6e7c26b03" + integrity sha512-kLgLShnWADDVreKC63pBrWkcvxgZzFIfO34Jhx/SWfuOIA3cD8AXT+HjyuLfoGJ7mUb58hv2kUziKzEy4INb1w== + +"@cspell/cspell-worker@10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@cspell/cspell-worker/-/cspell-worker-10.0.1.tgz#bdf5050b9678b6f5a9a0aefd6218ca57402bbf92" + integrity sha512-L2bJerfuYOls2wEknm8FmynLtj/G7O4UqX9I/HznRggEW6i2yZIxagDetpVDNowpyavNHJ3SJtUFiyMiZc16Sw== + dependencies: + cspell-lib "10.0.1" "@cspell/dict-ada@^4.1.1": version "4.1.1" @@ -161,127 +174,127 @@ resolved "https://registry.yarnpkg.com/@cspell/dict-al/-/dict-al-1.1.1.tgz#d6581e7801daa0f4e7512d3431e7f00c1e7d53e1" integrity sha512-sD8GCaZetgQL4+MaJLXqbzWcRjfKVp8x+px3HuCaaiATAAtvjwUQ5/Iubiqwfd1boIh2Y1/3EgM3TLQ7Q8e0wQ== -"@cspell/dict-aws@^4.0.12": - version "4.0.14" - resolved "https://registry.yarnpkg.com/@cspell/dict-aws/-/dict-aws-4.0.14.tgz#80c81765ebea0e9b755326d281af084b282a4f7e" - integrity sha512-qLPR+OFmpzyUcuUYyCQFIURDDUGIlQsdGirPyvaIrXxs2giCKG97cAuFz5EleL3/Lo7uJAVDw0lt4Ka7wIRhjQ== +"@cspell/dict-aws@^4.0.17": + version "4.0.17" + resolved "https://registry.yarnpkg.com/@cspell/dict-aws/-/dict-aws-4.0.17.tgz#73dba92ce69868babe114d6e436a5e4dd45b6c6c" + integrity sha512-ORcblTWcdlGjIbWrgKF+8CNEBQiLVKdUOFoTn0KPNkAYnFcdPP0muT4892h7H4Xafh3j72wqB4/loQ6Nti9E/w== -"@cspell/dict-bash@^4.2.1": - version "4.2.1" - resolved "https://registry.yarnpkg.com/@cspell/dict-bash/-/dict-bash-4.2.1.tgz#0666f547bb7fa0c62c0e5b65a87e64a852864a71" - integrity sha512-SBnzfAyEAZLI9KFS7DUG6Xc1vDFuLllY3jz0WHvmxe8/4xV3ufFE3fGxalTikc1VVeZgZmxYiABw4iGxVldYEg== +"@cspell/dict-bash@^4.2.2": + version "4.2.3" + resolved "https://registry.yarnpkg.com/@cspell/dict-bash/-/dict-bash-4.2.3.tgz#43d62dfa879ed6c5941d20ca6655b24392aea388" + integrity sha512-ljUZoKHbDqw5Sx0qpL2qTUlmkmr+vhZH/sCNrNaBZKTbdgiswErSnIF1jRbGmEitJNxHRHWsuZyVgnTGfVO1Yw== dependencies: - "@cspell/dict-shell" "1.1.1" + "@cspell/dict-shell" "1.2.0" -"@cspell/dict-companies@^3.2.2": - version "3.2.3" - resolved "https://registry.yarnpkg.com/@cspell/dict-companies/-/dict-companies-3.2.3.tgz#7b4eb9d9122ccd7379ed221d74433b6011327748" - integrity sha512-7ekwamRYeS7G3I3LEKM3t0WIyAytCbsx2I2h2z2eEvF+b3TmtJVcV7UI7BScLue3bep4sPB/b4CV3BUv3QfyzQ== +"@cspell/dict-companies@^3.2.11": + version "3.2.12" + resolved "https://registry.yarnpkg.com/@cspell/dict-companies/-/dict-companies-3.2.12.tgz#d49d74e9f98d3bbdc48ac9c5a24b4d601886c6c1" + integrity sha512-mjiz/N3zWOCsz5VfwMUydSl7uW0OU9H2PnbCNc3RV44Vj6Q59CSp6EYGSGZQxrXU1gpsuZUrwr6QCjNjFOOg5A== -"@cspell/dict-cpp@^6.0.9": - version "6.0.9" - resolved "https://registry.yarnpkg.com/@cspell/dict-cpp/-/dict-cpp-6.0.9.tgz#965ae9f7cf9e45bce8eb742ab39550183223ab9b" - integrity sha512-Xdq9MwGh0D5rsnbOqFW24NIClXXRhN11KJdySMibpcqYGeomxB2ODFBuhj1H7azO7kVGkGH0Okm4yQ2TRzBx0g== +"@cspell/dict-cpp@^7.0.2": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@cspell/dict-cpp/-/dict-cpp-7.1.0.tgz#286c1ae63e4401c2b44a652b1d5d283ffed1ce0a" + integrity sha512-rcjycobioQUd9Jm/Y1n8U+sq6ytpZ0iV8AYIo+vyEFfutC5x7g6hL6Fh3bCdh6IporGHRqfEutGK/4sfopD2ZA== "@cspell/dict-cryptocurrencies@^5.0.5": version "5.0.5" resolved "https://registry.yarnpkg.com/@cspell/dict-cryptocurrencies/-/dict-cryptocurrencies-5.0.5.tgz#843a6ac45216227f5436c442a8683c1571e57160" integrity sha512-R68hYYF/rtlE6T/dsObStzN5QZw+0aQBinAXuWCVqwdS7YZo0X33vGMfChkHaiCo3Z2+bkegqHlqxZF4TD3rUA== -"@cspell/dict-csharp@^4.0.7": - version "4.0.7" - resolved "https://registry.yarnpkg.com/@cspell/dict-csharp/-/dict-csharp-4.0.7.tgz#5c6d4c3cc55173d0891f66864df4fc9c2561c115" - integrity sha512-H16Hpu8O/1/lgijFt2lOk4/nnldFtQ4t8QHbyqphqZZVE5aS4J/zD/WvduqnLY21aKhZS6jo/xF5PX9jyqPKUA== +"@cspell/dict-csharp@^4.0.8": + version "4.0.8" + resolved "https://registry.yarnpkg.com/@cspell/dict-csharp/-/dict-csharp-4.0.8.tgz#27f6d5873f4dde77c03c78bb7d3c51bc8d8d78c1" + integrity sha512-qmk45pKFHSxckl5mSlbHxmDitSsGMlk/XzFgt7emeTJWLNSTUK//MbYAkBNRtfzB4uD7pAFiKgpKgtJrTMRnrQ== -"@cspell/dict-css@^4.0.18": - version "4.0.18" - resolved "https://registry.yarnpkg.com/@cspell/dict-css/-/dict-css-4.0.18.tgz#fd88cf2742a75d3fab2fbfdc6fc48fa79a6ebb13" - integrity sha512-EF77RqROHL+4LhMGW5NTeKqfUd/e4OOv6EDFQ/UQQiFyWuqkEKyEz0NDILxOFxWUEVdjT2GQ2cC7t12B6pESwg== +"@cspell/dict-css@^4.1.1": + version "4.1.2" + resolved "https://registry.yarnpkg.com/@cspell/dict-css/-/dict-css-4.1.2.tgz#d913cee821acf75616d1298d152341638ef3d85d" + integrity sha512-+ylGoKdwZ2sVOCOnU2Eq5wDZx+RaVX3HoKyNHGGsFvhSw6IidQ6tH/mAPKBDofViHJoWCPNlklE0lTr6MDG3QA== -"@cspell/dict-dart@^2.3.1": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@cspell/dict-dart/-/dict-dart-2.3.1.tgz#46bb78863fd72d59bdbd370a082b0f5044dfb1f3" - integrity sha512-xoiGnULEcWdodXI6EwVyqpZmpOoh8RA2Xk9BNdR7DLamV/QMvEYn8KJ7NlRiTSauJKPNkHHQ5EVHRM6sTS7jdg== +"@cspell/dict-dart@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@cspell/dict-dart/-/dict-dart-2.3.2.tgz#aae782dcf6c673857945b9bbe03beeaa79649222" + integrity sha512-sUiLW56t9gfZcu8iR/5EUg+KYyRD83Cjl3yjDEA2ApVuJvK1HhX+vn4e4k4YfjpUQMag8XO2AaRhARE09+/rqw== -"@cspell/dict-data-science@^2.0.9": - version "2.0.9" - resolved "https://registry.yarnpkg.com/@cspell/dict-data-science/-/dict-data-science-2.0.9.tgz#d943de6f569f2cb3ae9cead5c72c92ad75d657ff" - integrity sha512-wTOFMlxv06veIwKdXUwdGxrQcK44Zqs426m6JGgHIB/GqvieZQC5n0UI+tUm5OCxuNyo4OV6mylT4cRMjtKtWQ== +"@cspell/dict-data-science@^2.0.13", "@cspell/dict-data-science@^2.0.16": + version "2.0.16" + resolved "https://registry.yarnpkg.com/@cspell/dict-data-science/-/dict-data-science-2.0.16.tgz#370d4fdfcfaadb7176b6ca47e2e8603166e1c4a1" + integrity sha512-M72mxv5asuAnORurz4iXRJ+Tw9XBq6eu7D2Ne7biP0Z1RciKGNxXWu9JycA/KlVvK1hAlKj/fANlXhuEWpXKFg== -"@cspell/dict-django@^4.1.5": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@cspell/dict-django/-/dict-django-4.1.5.tgz#47b25dfcb24b2891e16e14223169dd978989ffca" - integrity sha512-AvTWu99doU3T8ifoMYOMLW2CXKvyKLukPh1auOPwFGHzueWYvBBN+OxF8wF7XwjTBMMeRleVdLh3aWCDEX/ZWg== +"@cspell/dict-django@^4.1.6": + version "4.1.6" + resolved "https://registry.yarnpkg.com/@cspell/dict-django/-/dict-django-4.1.6.tgz#a92408ba8971ca3df3c602b9e3750a14be69a8f6" + integrity sha512-SdbSFDGy9ulETqNz15oWv2+kpWLlk8DJYd573xhIkeRdcXOjskRuxjSZPKfW7O3NxN/KEf3gm3IevVOiNuFS+w== -"@cspell/dict-docker@^1.1.15": - version "1.1.16" - resolved "https://registry.yarnpkg.com/@cspell/dict-docker/-/dict-docker-1.1.16.tgz#09aa60469fd8db3fca7b2297ddc0848095763c33" - integrity sha512-UiVQ5RmCg6j0qGIxrBnai3pIB+aYKL3zaJGvXk1O/ertTKJif9RZikKXCEgqhaCYMweM4fuLqWSVmw3hU164Iw== +"@cspell/dict-docker@^1.1.17": + version "1.1.17" + resolved "https://registry.yarnpkg.com/@cspell/dict-docker/-/dict-docker-1.1.17.tgz#8674b3613dfa9c7d2922f6ec29ff845cb16e6650" + integrity sha512-OcnVTIpHIYYKhztNTyK8ShAnXTfnqs43hVH6p0py0wlcwRIXe5uj4f12n7zPf2CeBI7JAlPjEsV0Rlf4hbz/xQ== -"@cspell/dict-dotnet@^5.0.10": - version "5.0.10" - resolved "https://registry.yarnpkg.com/@cspell/dict-dotnet/-/dict-dotnet-5.0.10.tgz#7c1a4ba2174ead6e4c9cbe8f45ab80ecb1e37acc" - integrity sha512-ooar8BP/RBNP1gzYfJPStKEmpWy4uv/7JCq6FOnJLeD1yyfG3d/LFMVMwiJo+XWz025cxtkM3wuaikBWzCqkmg== +"@cspell/dict-dotnet@^5.0.13": + version "5.0.13" + resolved "https://registry.yarnpkg.com/@cspell/dict-dotnet/-/dict-dotnet-5.0.13.tgz#c75b4cdba79462398c4098739e699ea4144c85e5" + integrity sha512-xPp7jMnFpOri7tzmqmm/dXMolXz1t2bhNqxYkOyMqXhvs08oc7BFs+EsbDY0X7hqiISgeFZGNqn0dOCr+ncPYw== "@cspell/dict-elixir@^4.0.8": version "4.0.8" resolved "https://registry.yarnpkg.com/@cspell/dict-elixir/-/dict-elixir-4.0.8.tgz#c1b2a30d0fc654a001f718f196beb60c01e0e1f6" integrity sha512-CyfphrbMyl4Ms55Vzuj+mNmd693HjBFr9hvU+B2YbFEZprE5AG+EXLYTMRWrXbpds4AuZcvN3deM2XVB80BN/Q== -"@cspell/dict-en-common-misspellings@^2.1.3": - version "2.1.3" - resolved "https://registry.yarnpkg.com/@cspell/dict-en-common-misspellings/-/dict-en-common-misspellings-2.1.3.tgz#6ee2a034999f2b302c35274317dca76afe3f2ae1" - integrity sha512-v1I97Hr1OrK+mwHsVzbY4vsPxx6mA5quhxzanF6XuRofz00wH4HPz8Q3llzRHxka5Wl/59gyan04UkUrvP4gdA== +"@cspell/dict-en-common-misspellings@^2.1.12": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@cspell/dict-en-common-misspellings/-/dict-en-common-misspellings-2.2.0.tgz#a7ba55b9064f0219a78c9963064115ea98295258" + integrity sha512-5PmCHv+AhY0LVNo3bE1FdRKMmw1esKj83GPwLymnVPYNtlI2Jf6y27EjC0azg4zny5keWmku0GQiMf55Fi+qeA== -"@cspell/dict-en-gb-mit@^3.1.5": - version "3.1.6" - resolved "https://registry.yarnpkg.com/@cspell/dict-en-gb-mit/-/dict-en-gb-mit-3.1.6.tgz#ed7c43690dd2577ab43435d8fa8a1685c5a5bf15" - integrity sha512-3JJGxuPhDK5rMDYPzJYAdjjsBddEyV54rXfUQpOCl7c7weMhNDWfC2q4h3cKNDj7Isud1q2RM+DlSxQWf40OTw== +"@cspell/dict-en-gb-mit@^3.1.22": + version "3.1.25" + resolved "https://registry.yarnpkg.com/@cspell/dict-en-gb-mit/-/dict-en-gb-mit-3.1.25.tgz#f33bb024ffb4f52b0d4f263882e11bffcf338f8a" + integrity sha512-zGODptk24CMrXi49ieG2SUm94CKxEsVF0dYNF+1ZYH0MSsQDZ/PKDlrrbvtBqSupKdPSj0Z9sjOmMNfHHW9ZSg== -"@cspell/dict-en_us@^4.4.15": - version "4.4.16" - resolved "https://registry.yarnpkg.com/@cspell/dict-en_us/-/dict-en_us-4.4.16.tgz#124987e86c8fe3fc81f1386c0a4d37cb03450a74" - integrity sha512-/R47sUbUmba2dG/0LZyE6P6gX/DRF1sCcYNQNWyPk/KeidQRNZG+FH9U0KRvX42/2ZzMge6ebXH3WAJ52w0Vqw== +"@cspell/dict-en_us@^4.4.33": + version "4.4.36" + resolved "https://registry.yarnpkg.com/@cspell/dict-en_us/-/dict-en_us-4.4.36.tgz#fdfd0e722b6d9f234364c08c72149fb305c2131e" + integrity sha512-2yOhI/+7d1DbfvMljGW4jw8pLqDEsVmnvUXBOCFXtLU2BWgQkrqOJDCNseYjEiEbTp0OtdrWEWWPFSP1TNugQw== -"@cspell/dict-filetypes@^3.0.13": - version "3.0.13" - resolved "https://registry.yarnpkg.com/@cspell/dict-filetypes/-/dict-filetypes-3.0.13.tgz#119211401e7718c0af82614968352280e20da3af" - integrity sha512-g6rnytIpQlMNKGJT1JKzWkC+b3xCliDKpQ3ANFSq++MnR4GaLiifaC4JkVON11Oh/UTplYOR1nY3BR4X30bswA== +"@cspell/dict-filetypes@^3.0.18": + version "3.0.18" + resolved "https://registry.yarnpkg.com/@cspell/dict-filetypes/-/dict-filetypes-3.0.18.tgz#b798a5321d8a4a254a9998920db4b91491e51ebf" + integrity sha512-yU7RKD/x1IWmDLzWeiItMwgV+6bUcU/af23uS0+uGiFUbsY1qWV/D4rxlAAO6Z7no3J2z8aZOkYIOvUrJq0Rcw== "@cspell/dict-flutter@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@cspell/dict-flutter/-/dict-flutter-1.1.1.tgz#fab57cf189a8012e870d2e1f21526b18345038d7" integrity sha512-UlOzRcH2tNbFhZmHJN48Za/2/MEdRHl2BMkCWZBYs+30b91mWvBfzaN4IJQU7dUZtowKayVIF9FzvLZtZokc5A== -"@cspell/dict-fonts@^4.0.5": - version "4.0.5" - resolved "https://registry.yarnpkg.com/@cspell/dict-fonts/-/dict-fonts-4.0.5.tgz#21ff391df20722c7d370ce79c89665e4b8980200" - integrity sha512-BbpkX10DUX/xzHs6lb7yzDf/LPjwYIBJHJlUXSBXDtK/1HaeS+Wqol4Mlm2+NAgZ7ikIE5DQMViTgBUY3ezNoQ== +"@cspell/dict-fonts@^4.0.6": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@cspell/dict-fonts/-/dict-fonts-4.0.6.tgz#efdda213b4c876053aea51bafc7cd882c6379563" + integrity sha512-aR/0csY01dNb0A1tw/UmN9rKgHruUxsYsvXu6YlSBJFu60s26SKr/k1o4LavpHTQ+lznlYMqAvuxGkE4Flliqw== "@cspell/dict-fsharp@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@cspell/dict-fsharp/-/dict-fsharp-1.1.1.tgz#46414a8177b1c3373f1edb156df446088147cc22" integrity sha512-imhs0u87wEA4/cYjgzS0tAyaJpwG7vwtC8UyMFbwpmtw+/bgss+osNfyqhYRyS/ehVCWL17Ewx2UPkexjKyaBA== -"@cspell/dict-fullstack@^3.2.7": - version "3.2.7" - resolved "https://registry.yarnpkg.com/@cspell/dict-fullstack/-/dict-fullstack-3.2.7.tgz#b5cc10c8e93093b124811a3af8d7169e52133723" - integrity sha512-IxEk2YAwAJKYCUEgEeOg3QvTL4XLlyArJElFuMQevU1dPgHgzWElFevN5lsTFnvMFA1riYsVinqJJX0BanCFEg== +"@cspell/dict-fullstack@^3.2.9": + version "3.2.9" + resolved "https://registry.yarnpkg.com/@cspell/dict-fullstack/-/dict-fullstack-3.2.9.tgz#e8baf9382b6006921684c9408dcad2c1de5ae4bc" + integrity sha512-diZX+usW5aZ4/b2T0QM/H/Wl9aNMbdODa1Jq0ReBr/jazmNeWjd+PyqeVgzd1joEaHY+SAnjrf/i9CwKd2ZtWQ== "@cspell/dict-gaming-terms@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@cspell/dict-gaming-terms/-/dict-gaming-terms-1.1.2.tgz#459aa470b43eacbd3cbf7b32bd5bbb259cb78812" integrity sha512-9XnOvaoTBscq0xuD6KTEIkk9hhdfBkkvJAIsvw3JMcnp1214OCGW8+kako5RqQ2vTZR3Tnf3pc57o7VgkM0q1Q== -"@cspell/dict-git@^3.0.7": - version "3.0.7" - resolved "https://registry.yarnpkg.com/@cspell/dict-git/-/dict-git-3.0.7.tgz#382093019e8fa446f5cf2b347a9aef1cf7a30316" - integrity sha512-odOwVKgfxCQfiSb+nblQZc4ErXmnWEnv8XwkaI4sNJ7cNmojnvogYVeMqkXPjvfrgEcizEEA4URRD2Ms5PDk1w== +"@cspell/dict-git@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@cspell/dict-git/-/dict-git-3.1.0.tgz#7ac48114425c74e0a1c00f154138cf81b04f250b" + integrity sha512-KEt9zGkxqGy2q1nwH4CbyqTSv5nadpn8BAlDnzlRcnL0Xb3LX9xTgSGShKvzb0bw35lHoYyLWN2ZKAqbC4pgGQ== -"@cspell/dict-golang@^6.0.23": - version "6.0.23" - resolved "https://registry.yarnpkg.com/@cspell/dict-golang/-/dict-golang-6.0.23.tgz#e5f0bca4acb088a314a04229c691f89570e971f4" - integrity sha512-oXqUh/9dDwcmVlfUF5bn3fYFqbUzC46lXFQmi5emB0vYsyQXdNWsqi6/yH3uE7bdRE21nP7Yo0mR1jjFNyLamg== +"@cspell/dict-golang@^6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@cspell/dict-golang/-/dict-golang-6.0.26.tgz#8d0a6f09ade1c489a92b594475bba2b6020b6d28" + integrity sha512-YKA7Xm5KeOd14v5SQ4ll6afe9VSy3a2DWM7L9uBq4u3lXToRBQ1W5PRa+/Q9udd+DTURyVVnQ+7b9cnOlNxaRg== "@cspell/dict-google@^1.0.9": version "1.0.9" @@ -293,15 +306,15 @@ resolved "https://registry.yarnpkg.com/@cspell/dict-haskell/-/dict-haskell-4.0.6.tgz#881436f944a6901cff8fab1af776277ca96f1b8c" integrity sha512-ib8SA5qgftExpYNjWhpYIgvDsZ/0wvKKxSP+kuSkkak520iPvTJumEpIE+qPcmJQo4NzdKMN8nEfaeci4OcFAQ== -"@cspell/dict-html-symbol-entities@^4.0.4": - version "4.0.4" - resolved "https://registry.yarnpkg.com/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.4.tgz#e6e2819b0930df6d14de3e706cac838c9d5f5839" - integrity sha512-afea+0rGPDeOV9gdO06UW183Qg6wRhWVkgCFwiO3bDupAoyXRuvupbb5nUyqSTsLXIKL8u8uXQlJ9pkz07oVXw== +"@cspell/dict-html-symbol-entities@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.5.tgz#cbdd8c133c7d649d32e10f48b58bd4a9304b5cb6" + integrity sha512-429alTD4cE0FIwpMucvSN35Ld87HCyuM8mF731KU5Rm4Je2SG6hmVx7nkBsLyrmH3sQukTcr1GaiZsiEg8svPA== -"@cspell/dict-html@^4.0.12": - version "4.0.12" - resolved "https://registry.yarnpkg.com/@cspell/dict-html/-/dict-html-4.0.12.tgz#5932e2b9e3cb1c668aa5ef054c24b38b0baadf08" - integrity sha512-JFffQ1dDVEyJq6tCDWv0r/RqkdSnV43P2F/3jJ9rwLgdsOIXwQbXrz6QDlvQLVvNSnORH9KjDtenFTGDyzfCaA== +"@cspell/dict-html@^4.0.15": + version "4.0.15" + resolved "https://registry.yarnpkg.com/@cspell/dict-html/-/dict-html-4.0.15.tgz#4ccd850cff30cc65b2eb372d8b96ec9a55afd177" + integrity sha512-GJYnYKoD9fmo2OI0aySEGZOjThnx3upSUvV7mmqUu8oG+mGgzqm82P/f7OqsuvTaInZZwZbo+PwJQd/yHcyFIw== "@cspell/dict-java@^5.0.12": version "5.0.12" @@ -323,10 +336,10 @@ resolved "https://registry.yarnpkg.com/@cspell/dict-kotlin/-/dict-kotlin-1.1.1.tgz#830d7b3d33685c0998ef5b922b0d7779f6669706" integrity sha512-J3NzzfgmxRvEeOe3qUXnSJQCd38i/dpF9/t3quuWh6gXM+krsAXP75dY1CzDmS8mrJAlBdVBeAW5eAZTD8g86Q== -"@cspell/dict-latex@^4.0.4": - version "4.0.4" - resolved "https://registry.yarnpkg.com/@cspell/dict-latex/-/dict-latex-4.0.4.tgz#ce058efd274dac8936db0ac9c8134599a2bdaf9f" - integrity sha512-YdTQhnTINEEm/LZgTzr9Voz4mzdOXH7YX+bSFs3hnkUHCUUtX/mhKgf1CFvZ0YNM2afjhQcmLaR9bDQVyYBvpA== +"@cspell/dict-latex@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@cspell/dict-latex/-/dict-latex-5.1.0.tgz#c607cfb349ea73378ab5ae79592d389a3cc47c3e" + integrity sha512-qxT4guhysyBt0gzoliXYEBYinkAdEtR2M7goRaUH0a7ltCsoqqAeEV8aXYRIdZGcV77gYSobvu3jJL038tlPAw== "@cspell/dict-lorem-ipsum@^4.0.5": version "4.0.5" @@ -343,77 +356,77 @@ resolved "https://registry.yarnpkg.com/@cspell/dict-makefile/-/dict-makefile-1.0.5.tgz#fe6e7df2360ff694ef41c90a0d4b422e81f560ef" integrity sha512-4vrVt7bGiK8Rx98tfRbYo42Xo2IstJkAF4tLLDMNQLkQ86msDlYSKG1ZCk8Abg+EdNcFAjNhXIiNO+w4KflGAQ== -"@cspell/dict-markdown@^2.0.12": - version "2.0.12" - resolved "https://registry.yarnpkg.com/@cspell/dict-markdown/-/dict-markdown-2.0.12.tgz#9c2e3533d6a850c5986bd3074e94cd6ef099a24e" - integrity sha512-ufwoliPijAgWkD/ivAMC+A9QD895xKiJRF/fwwknQb7kt7NozTLKFAOBtXGPJAB4UjhGBpYEJVo2elQ0FCAH9A== +"@cspell/dict-markdown@^2.0.16": + version "2.0.17" + resolved "https://registry.yarnpkg.com/@cspell/dict-markdown/-/dict-markdown-2.0.17.tgz#6f9115195202e83d4a6763b50468207a2a410c8f" + integrity sha512-H8bAxih6U8NOnSPL7R8My+tqjaB4tmnJTjERuz4zYqmf+cH+5xshX3UVgKlwWFcyjsYfv/zEDuRdMctQv1q6HQ== -"@cspell/dict-monkeyc@^1.0.11": - version "1.0.11" - resolved "https://registry.yarnpkg.com/@cspell/dict-monkeyc/-/dict-monkeyc-1.0.11.tgz#166bb61c86a2ff95707078db209ee73408d15bae" - integrity sha512-7Q1Ncu0urALI6dPTrEbSTd//UK0qjRBeaxhnm8uY5fgYNFYAG+u4gtnTIo59S6Bw5P++4H3DiIDYoQdY/lha8w== +"@cspell/dict-monkeyc@^1.0.12": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@cspell/dict-monkeyc/-/dict-monkeyc-1.1.0.tgz#c7f2ca2dac90668d961df866115ec5229372d0fd" + integrity sha512-mc/hgvSy/emOIYtc8kuVEaLUlnaERunfAubfJ5plUXq6s0A69bcukJzUzSt9C0UTCwS28641ILRPv80m3L9QbA== -"@cspell/dict-node@^5.0.8": - version "5.0.8" - resolved "https://registry.yarnpkg.com/@cspell/dict-node/-/dict-node-5.0.8.tgz#5c732e6d9a71a8c857456abc26be2ee836cb720e" - integrity sha512-AirZcN2i84ynev3p2/1NCPEhnNsHKMz9zciTngGoqpdItUb2bDt1nJBjwlsrFI78GZRph/VaqTVFwYikmncpXg== +"@cspell/dict-node@^5.0.9": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@cspell/dict-node/-/dict-node-5.0.9.tgz#ca894e62b85deaf2f55e9d9c86fdbb260ba923eb" + integrity sha512-hO+ga+uYZ/WA4OtiMEyKt5rDUlUyu3nXMf8KVEeqq2msYvAPdldKBGH7lGONg6R/rPhv53Rb+0Y1SLdoK1+7wQ== -"@cspell/dict-npm@^5.2.12": - version "5.2.13" - resolved "https://registry.yarnpkg.com/@cspell/dict-npm/-/dict-npm-5.2.13.tgz#f7768b895122cc8c2d2b0b33bbefbeb514f4d44c" - integrity sha512-yE7DfpiQjDFW6TLr5/fsSj4BlUy1A8lsuz2LQQHv4lQAAkZ4RsePYFL9DkRRfEtxn8CZYetUnU74/jQbfsnyrA== +"@cspell/dict-npm@^5.2.38": + version "5.2.45" + resolved "https://registry.yarnpkg.com/@cspell/dict-npm/-/dict-npm-5.2.45.tgz#8782586680b601cf7d3ecdf5a1d82d138a43c3d8" + integrity sha512-BXUmMMspl+AhPIk/ZOjxlNu5k1yCRAzSMzdNPYM+2vaBL8ffev7ZFfOyBCDk8kgcpsHotz8/3fLNX+xwWaDR2w== -"@cspell/dict-php@^4.0.15": - version "4.0.15" - resolved "https://registry.yarnpkg.com/@cspell/dict-php/-/dict-php-4.0.15.tgz#06a1e184ded5a7889d9f8e6922889d04299dc3d6" - integrity sha512-iepGB2gtToMWSTvybesn4/lUp4LwXcEm0s8vasJLP76WWVkq1zYjmeS+WAIzNgsuURyZ/9mGqhS0CWMuo74ODw== +"@cspell/dict-php@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@cspell/dict-php/-/dict-php-4.1.1.tgz#39117cde87706f843a0476c56b807c16d71a9e4b" + integrity sha512-EXelI+4AftmdIGtA8HL8kr4WlUE11OqCSVlnIgZekmTkEGSZdYnkFdiJ5IANSALtlQ1mghKjz+OFqVs6yowgWA== "@cspell/dict-powershell@^5.0.15": version "5.0.15" resolved "https://registry.yarnpkg.com/@cspell/dict-powershell/-/dict-powershell-5.0.15.tgz#4ad8b6a741c96508f7b5acbcda2a15978be351c6" integrity sha512-l4S5PAcvCFcVDMJShrYD0X6Huv9dcsQPlsVsBGbH38wvuN7gS7+GxZFAjTNxDmTY1wrNi1cCatSg6Pu2BW4rgg== -"@cspell/dict-public-licenses@^2.0.14": - version "2.0.14" - resolved "https://registry.yarnpkg.com/@cspell/dict-public-licenses/-/dict-public-licenses-2.0.14.tgz#1231ae3d0440fcbf9e110c0706bfbe302bc8b052" - integrity sha512-8NhNzQWALF6+NlLeKZKilSHbeW9MWeiD+NcrjehMAcovKFbsn8smmQG/bVxw+Ymtd6WEgNpLgswAqNsbSQQ4og== +"@cspell/dict-public-licenses@^2.0.16": + version "2.0.16" + resolved "https://registry.yarnpkg.com/@cspell/dict-public-licenses/-/dict-public-licenses-2.0.16.tgz#8eb3c467c24526460543a24edf55a979a4f34f39" + integrity sha512-EQRrPvEOmwhwWezV+W7LjXbIBjiy6y/shrET6Qcpnk3XANTzfvWflf9PnJ5kId/oKWvihFy0za0AV1JHd03pSQ== -"@cspell/dict-python@^4.2.19": - version "4.2.19" - resolved "https://registry.yarnpkg.com/@cspell/dict-python/-/dict-python-4.2.19.tgz#51d4cd0981f24ff547e6444df67989a3d76e34ba" - integrity sha512-9S2gTlgILp1eb6OJcVZeC8/Od83N8EqBSg5WHVpx97eMMJhifOzePkE0kDYjyHMtAFznCQTUu0iQEJohNQ5B0A== +"@cspell/dict-python@^4.2.26": + version "4.3.0" + resolved "https://registry.yarnpkg.com/@cspell/dict-python/-/dict-python-4.3.0.tgz#92a63f76ef5cb47096a67d6fa4a2a39aeeb4eee9" + integrity sha512-afVcbsCOYtpdvu8I4ANiIXVcxqxzrno+oEuObNx9hwpvEmf/AbXQBk9CMjFR9QZllcfnOfpjCSXgGuTna5hKQA== dependencies: - "@cspell/dict-data-science" "^2.0.9" + "@cspell/dict-data-science" "^2.0.16" "@cspell/dict-r@^2.1.1": version "2.1.1" resolved "https://registry.yarnpkg.com/@cspell/dict-r/-/dict-r-2.1.1.tgz#ace8d66799cae4148411bb6483d9c8a8a3c8a50f" integrity sha512-71Ka+yKfG4ZHEMEmDxc6+blFkeTTvgKbKAbwiwQAuKl3zpqs1Y0vUtwW2N4b3LgmSPhV3ODVY0y4m5ofqDuKMw== -"@cspell/dict-ruby@^5.0.9": - version "5.0.9" - resolved "https://registry.yarnpkg.com/@cspell/dict-ruby/-/dict-ruby-5.0.9.tgz#844d6214a7c2132696eacc8f741d5950307d48e1" - integrity sha512-H2vMcERMcANvQshAdrVx0XoWaNX8zmmiQN11dZZTQAZaNJ0xatdJoSqY8C8uhEMW89bfgpN+NQgGuDXW2vmXEw== +"@cspell/dict-ruby@^5.1.1": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@cspell/dict-ruby/-/dict-ruby-5.1.1.tgz#73c5c48cb20402b1ba5589b08c904b11e2f12ccb" + integrity sha512-LHrp84oEV6q1ZxPPyj4z+FdKyq1XAKYPtmGptrd+uwHbrF/Ns5+fy6gtSi7pS+uc0zk3JdO9w/tPK+8N1/7WUA== -"@cspell/dict-rust@^4.0.12": - version "4.0.12" - resolved "https://registry.yarnpkg.com/@cspell/dict-rust/-/dict-rust-4.0.12.tgz#058cc67066f9c9bec1a503f221a131097493b797" - integrity sha512-z2QiH+q9UlNhobBJArvILRxV8Jz0pKIK7gqu4TgmEYyjiu1TvnGZ1tbYHeu9w3I/wOP6UMDoCBTty5AlYfW0mw== +"@cspell/dict-rust@^4.1.2": + version "4.1.2" + resolved "https://registry.yarnpkg.com/@cspell/dict-rust/-/dict-rust-4.1.2.tgz#6a151e72dc3be916c040111bba7358401ba57e15" + integrity sha512-O1FHrumYcO+HZti3dHfBPUdnDFkI+nbYK3pxYmiM1sr+G0ebOd6qchmswS0Wsc6ZdEVNiPYJY/gZQR6jfW3uOg== -"@cspell/dict-scala@^5.0.8": - version "5.0.8" - resolved "https://registry.yarnpkg.com/@cspell/dict-scala/-/dict-scala-5.0.8.tgz#6b274dc2fad5d2829337f7c9e800f1d4262a2ded" - integrity sha512-YdftVmumv8IZq9zu1gn2U7A4bfM2yj9Vaupydotyjuc+EEZZSqAafTpvW/jKLWji2TgybM1L2IhmV0s/Iv9BTw== +"@cspell/dict-scala@^5.0.9": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@cspell/dict-scala/-/dict-scala-5.0.9.tgz#181d6b9cad0596bec2f8df198a79576f97112b6e" + integrity sha512-AjVcVAELgllybr1zk93CJ5wSUNu/Zb5kIubymR/GAYkMyBdYFCZ3Zbwn4Zz8GJlFFAbazABGOu0JPVbeY59vGg== -"@cspell/dict-shell@1.1.1", "@cspell/dict-shell@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@cspell/dict-shell/-/dict-shell-1.1.1.tgz#2274798fefaf6bab354cfec4d1169cc8d0f2a2b1" - integrity sha512-T37oYxE7OV1x/1D4/13Y8JZGa1QgDCXV7AVt3HLXjn0Fe3TaNDvf5sU0fGnXKmBPqFFrHdpD3uutAQb1dlp15g== +"@cspell/dict-shell@1.2.0", "@cspell/dict-shell@^1.1.2": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@cspell/dict-shell/-/dict-shell-1.2.0.tgz#5702fe3f8fc8813448e98dcec9df9e34934b0f21" + integrity sha512-PVctvT22lJ49niMiakO8xieY7ELCAzjSqhejWR7bAMb5AZ9F4WDEs+XdGMnoVHWeXq7K5rcepLPmEJb+37zzIw== -"@cspell/dict-software-terms@^5.1.4": - version "5.1.5" - resolved "https://registry.yarnpkg.com/@cspell/dict-software-terms/-/dict-software-terms-5.1.5.tgz#b83440158c1a2c3ecc1bf463da93142279321de5" - integrity sha512-MX5beBP3pLmIM0mjqfrHbie3EEfyLWZ8ZqW56jcLuRlLoDcfC0FZsr66NCARgCgEwsWiidHFe87+7fFsnwqY6A== +"@cspell/dict-software-terms@^5.2.2": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@cspell/dict-software-terms/-/dict-software-terms-5.3.0.tgz#cee14993bc3470121c3dfa18c7fa3996ecd71167" + integrity sha512-HEMvFWeItTA5A/MFxrH5fpOz9DU7ormXaGp1PJPsd0jt+qqaYNpevGIgTfIhfp/hsabPyAGa51N/DeDT0ZMg2Q== "@cspell/dict-sql@^2.2.1": version "2.2.1" @@ -445,28 +458,38 @@ resolved "https://registry.yarnpkg.com/@cspell/dict-vue/-/dict-vue-3.0.5.tgz#e915b6a004d0352f5c27a2e4583c42dba62b6ce0" integrity sha512-Mqutb8jbM+kIcywuPQCCaK5qQHTdaByoEO2J9LKFy3sqAdiBogNkrplqUK0HyyRFgCfbJUgjz3N85iCMcWH0JA== -"@cspell/dynamic-import@9.2.0": - version "9.2.0" - resolved "https://registry.yarnpkg.com/@cspell/dynamic-import/-/dynamic-import-9.2.0.tgz#d46ae6779029985b581348df433661592530df41" - integrity sha512-2/k4LR8CQqbgIPQGELbCdt9xgg9+aQ7pMwOtllKvnFYBtwNiwqcZjlzAam2gtvD5DghKX2qrcSHG5A7YP5cX9A== +"@cspell/dict-zig@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@cspell/dict-zig/-/dict-zig-1.0.0.tgz#f75fef19f2fdad6f5bc4d02b95b8bec824e82ab9" + integrity sha512-XibBIxBlVosU06+M6uHWkFeT0/pW5WajDRYdXG2CgHnq85b0TI/Ks0FuBJykmsgi2CAD3Qtx8UHFEtl/DSFnAQ== + +"@cspell/dynamic-import@10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@cspell/dynamic-import/-/dynamic-import-10.0.1.tgz#816a7806cbb5285a3654e6aae9409c751918e6d1" + integrity sha512-mP1gdq00aIcH8HxNMqnH11X6BKxLcneDtFgl/ecjIKnaGKwi44m8AndP5Kr4ODaYdl8UUw9O3dJh7KaQXnLHZQ== dependencies: - "@cspell/url" "9.2.0" - import-meta-resolve "^4.1.0" + "@cspell/url" "10.0.1" + import-meta-resolve "^4.2.0" -"@cspell/filetypes@9.2.0": - version "9.2.0" - resolved "https://registry.yarnpkg.com/@cspell/filetypes/-/filetypes-9.2.0.tgz#bd111a1bdc3660c579153cf9630fe2d263cc33f0" - integrity sha512-6wmCa3ZyI647H7F4w6kb9PCJ703JKSgFTB8EERTdIoGySbgVp5+qMIIoZ//wELukdjgcufcFZ5pBrhRDRsemRA== +"@cspell/filetypes@10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@cspell/filetypes/-/filetypes-10.0.1.tgz#ff877980caebb41fc1c8b4d5a2d411b4ca2c22b4" + integrity sha512-Z5S35giU5IW49fBBq6BksUbE8PC4IYPfaKuwl5Nl9jkf/OkAKiBmCowKX45NzRUQInwK/GSqqIUifrNeI6LdLw== -"@cspell/strong-weak-map@9.2.0": - version "9.2.0" - resolved "https://registry.yarnpkg.com/@cspell/strong-weak-map/-/strong-weak-map-9.2.0.tgz#80c153a980c72a4a2a36ec67d0803a59235e787a" - integrity sha512-5mpIMiIOCu4cBqy1oCTXISgJuOCQ6R/e38AkvnYWfmMIx7fCdx8n+mF52wX9m61Ng28Sq8VL253xybsWcCxHug== +"@cspell/rpc@10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@cspell/rpc/-/rpc-10.0.1.tgz#6bb88f18253faf1f4599d89555c2ba238174221c" + integrity sha512-axSRKv3zEAmBm66iD/FV/MPmE4/Yf7c3PZiwTW894Yd3iEhtn3KPKeTrqQ2/tDrhB1Z2qTsap/Hue0MK4o5WXg== -"@cspell/url@9.2.0": - version "9.2.0" - resolved "https://registry.yarnpkg.com/@cspell/url/-/url-9.2.0.tgz#225070a8f553f80357e67ff11b0dd232645a81a0" - integrity sha512-plB0wwdAESqBl4xDAT2db2/K1FZHJXfYlJTiV6pkn0XffTGyg4UGLaSCm15NzUoPxdSmzqj5jQb7y+mB9kFK8g== +"@cspell/strong-weak-map@10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@cspell/strong-weak-map/-/strong-weak-map-10.0.1.tgz#55a206076ab9d8957db5ffbf188cc73d422a3a14" + integrity sha512-lenN1DVyPi8nJLSMSJJ670ddTjyiruLueuSZO1qLcxBqUhgxDt/mALu9N/1m6WdOVcg6m/5cLiZVg2KOo2UzRw== + +"@cspell/url@10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@cspell/url/-/url-10.0.1.tgz#b7eeefe6f4c84b8f2748d510127e85a3eacbb86b" + integrity sha512-abYYgI29wJhWIfWTYrYuzRYDcHQUQ1N5ylnhxYn1NJnIQMqUWGLbDmt12JABtZ+R6h6UNatQrS7rhP86etvJyQ== "@electrum-cash/debug-logs@^1.0.0": version "1.0.0" @@ -475,22 +498,22 @@ dependencies: debug "^4.3.7" -"@electrum-cash/network@^4.1.3": - version "4.1.3" - resolved "https://registry.yarnpkg.com/@electrum-cash/network/-/network-4.1.3.tgz#195a96e8bb34493c622223992da0649c753aafff" - integrity sha512-amMvdcEfHhquoUkhN7x/H04KPYfqd5LilOGcg6O1OdUks1Mcrcah8WfHICHW/qyZ3Rgoos9o7Wx8gKz8qcSNzg== +"@electrum-cash/network@^4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@electrum-cash/network/-/network-4.2.2.tgz#867b849938d196e887d56cf2cbecb8a1f2c9ea71" + integrity sha512-v2Wwt2o0VBBALPArx2dJEDvSqewKjiTW5KAd+jEXxxgdSxFygjJIrFcXryKOCv2CDbpE5+lXAogPAjx6FqW/nw== dependencies: "@electrum-cash/debug-logs" "^1.0.0" - "@electrum-cash/web-socket" "^1.0.0" + "@electrum-cash/web-socket" "^1.2.3" async-mutex "^0.5.0" debug "^4.3.2" eventemitter3 "^5.0.1" lossless-json "^4.0.1" -"@electrum-cash/web-socket@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@electrum-cash/web-socket/-/web-socket-1.0.0.tgz#0ab57e46d41e941ffb57deff86afea3ae1379d5d" - integrity sha512-+VQ6aPE7nUysyDn9SB7/uqKuVJmjrhvr0LRK2ANTeR1DfmXBnv5z29e/0zK5A7ZoAz0gdZZuLDzN46r8S5Cxig== +"@electrum-cash/web-socket@^1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@electrum-cash/web-socket/-/web-socket-1.2.3.tgz#473b6ddb4dba513e1c4e62b163da219205e7b31b" + integrity sha512-sFWujTt98mvsMvE6gWjG44evkF3PcZhG1JffkkEUAVan+c9X51wkiWr3hkjPeIW0WfNpMTrFkvpGqVYmRj1RCA== dependencies: "@electrum-cash/debug-logs" "^1.0.0" "@monsterbitar/isomorphic-ws" "^5.3.0" @@ -500,265 +523,135 @@ lossless-json "^4.0.1" ws "^8.13.0" -"@esbuild/aix-ppc64@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz#a1414903bb38027382f85f03dda6065056757727" - integrity sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA== - -"@esbuild/aix-ppc64@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz#bef96351f16520055c947aba28802eede3c9e9a9" - integrity sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA== - -"@esbuild/android-arm64@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz#c859994089e9767224269884061f89dae6fb51c6" - integrity sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w== - -"@esbuild/android-arm64@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz#d2e70be7d51a529425422091e0dcb90374c1546c" - integrity sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg== - -"@esbuild/android-arm@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.8.tgz#96a8f2ca91c6cd29ea90b1af79d83761c8ba0059" - integrity sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw== - -"@esbuild/android-arm@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.9.tgz#d2a753fe2a4c73b79437d0ba1480e2d760097419" - integrity sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ== - -"@esbuild/android-x64@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.8.tgz#a3a626c4fec4a024a9fa8c7679c39996e92916f0" - integrity sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA== - -"@esbuild/android-x64@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.9.tgz#5278836e3c7ae75761626962f902a0d55352e683" - integrity sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw== - -"@esbuild/darwin-arm64@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz#a5e1252ca2983d566af1c0ea39aded65736fc66d" - integrity sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw== - -"@esbuild/darwin-arm64@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz#f1513eaf9ec8fa15dcaf4c341b0f005d3e8b47ae" - integrity sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg== - -"@esbuild/darwin-x64@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz#5271b0df2bb12ce8df886704bfdd1c7cc01385d2" - integrity sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg== - -"@esbuild/darwin-x64@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz#e27dbc3b507b3a1cea3b9280a04b8b6b725f82be" - integrity sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ== - -"@esbuild/freebsd-arm64@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz#d0a0e7fdf19733b8bb1566b81df1aa0bb7e46ada" - integrity sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA== - -"@esbuild/freebsd-arm64@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz#364e3e5b7a1fd45d92be08c6cc5d890ca75908ca" - integrity sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q== - -"@esbuild/freebsd-x64@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz#2de8b2e0899d08f1cb1ef3128e159616e7e85343" - integrity sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw== - -"@esbuild/freebsd-x64@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz#7c869b45faeb3df668e19ace07335a0711ec56ab" - integrity sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg== - -"@esbuild/linux-arm64@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz#a4209efadc0c2975716458484a4e90c237c48ae9" - integrity sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w== - -"@esbuild/linux-arm64@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz#48d42861758c940b61abea43ba9a29b186d6cb8b" - integrity sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw== - -"@esbuild/linux-arm@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz#ccd9e291c24cd8d9142d819d463e2e7200d25b19" - integrity sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg== - -"@esbuild/linux-arm@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz#6ce4b9cabf148274101701d112b89dc67cc52f37" - integrity sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw== - -"@esbuild/linux-ia32@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz#006ad1536d0c2b28fb3a1cf0b53bcb85aaf92c4d" - integrity sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg== - -"@esbuild/linux-ia32@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz#207e54899b79cac9c26c323fc1caa32e3143f1c4" - integrity sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A== - -"@esbuild/linux-loong64@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz#127b3fbfb2c2e08b1397e985932f718f09a8f5c4" - integrity sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ== - -"@esbuild/linux-loong64@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz#0ba48a127159a8f6abb5827f21198b999ffd1fc0" - integrity sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ== - -"@esbuild/linux-mips64el@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz#837d1449517791e3fa7d82675a2d06d9f56cb340" - integrity sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw== - -"@esbuild/linux-mips64el@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz#a4d4cc693d185f66a6afde94f772b38ce5d64eb5" - integrity sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA== - -"@esbuild/linux-ppc64@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz#aa2e3bd93ab8df084212f1895ca4b03c42d9e0fe" - integrity sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ== - -"@esbuild/linux-ppc64@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz#0f5805c1c6d6435a1dafdc043cb07a19050357db" - integrity sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w== - -"@esbuild/linux-riscv64@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz#a340620e31093fef72767dd28ab04214b3442083" - integrity sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg== - -"@esbuild/linux-riscv64@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz#6776edece0f8fca79f3386398b5183ff2a827547" - integrity sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg== - -"@esbuild/linux-s390x@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz#ddfed266c8c13f5efb3105a0cd47f6dcd0e79e71" - integrity sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg== - -"@esbuild/linux-s390x@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz#3f6f29ef036938447c2218d309dc875225861830" - integrity sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA== - -"@esbuild/linux-x64@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz#9a4f78c75c051e8c060183ebb39a269ba936a2ac" - integrity sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ== - -"@esbuild/linux-x64@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz#831fe0b0e1a80a8b8391224ea2377d5520e1527f" - integrity sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg== - -"@esbuild/netbsd-arm64@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz#902c80e1d678047926387230bc037e63e00697d0" - integrity sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw== - -"@esbuild/netbsd-arm64@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz#06f99d7eebe035fbbe43de01c9d7e98d2a0aa548" - integrity sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q== - -"@esbuild/netbsd-x64@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz#2d9eb4692add2681ff05a14ce99de54fbed7079c" - integrity sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg== - -"@esbuild/netbsd-x64@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz#db99858e6bed6e73911f92a88e4edd3a8c429a52" - integrity sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g== - -"@esbuild/openbsd-arm64@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz#89c3b998c6de739db38ab7fb71a8a76b3fa84a45" - integrity sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ== - -"@esbuild/openbsd-arm64@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz#afb886c867e36f9d86bb21e878e1185f5d5a0935" - integrity sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ== - -"@esbuild/openbsd-x64@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz#2f01615cf472b0e48c077045cfd96b5c149365cc" - integrity sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ== - -"@esbuild/openbsd-x64@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz#30855c9f8381fac6a0ef5b5f31ac6e7108a66ecf" - integrity sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA== - -"@esbuild/openharmony-arm64@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz#a201f720cd2c3ebf9a6033fcc3feb069a54b509a" - integrity sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg== - -"@esbuild/openharmony-arm64@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz#2f2144af31e67adc2a8e3705c20c2bd97bd88314" - integrity sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg== - -"@esbuild/sunos-x64@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz#07046c977985a3334667f19e6ab3a01a80862afb" - integrity sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w== - -"@esbuild/sunos-x64@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz#69b99a9b5bd226c9eb9c6a73f990fddd497d732e" - integrity sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw== - -"@esbuild/win32-arm64@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz#4a5470caf0d16127c05d4833d4934213c69392d1" - integrity sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ== - -"@esbuild/win32-arm64@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz#d789330a712af916c88325f4ffe465f885719c6b" - integrity sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ== - -"@esbuild/win32-ia32@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz#3de3e8470b7b328d99dbc3e9ec1eace207e5bbc4" - integrity sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg== - -"@esbuild/win32-ia32@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz#52fc735406bd49688253e74e4e837ac2ba0789e3" - integrity sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww== - -"@esbuild/win32-x64@0.25.8": - version "0.25.8" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz#610d7ea539d2fcdbe39237b5cc175eb2c4451f9c" - integrity sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw== - -"@esbuild/win32-x64@0.25.9": - version "0.25.9" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz#585624dc829cfb6e7c0aa6c3ca7d7e6daa87e34f" - integrity sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ== +"@esbuild/aix-ppc64@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz#bf6e10303bcf2e7c686975fa52f937ec2728d8bc" + integrity sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ== + +"@esbuild/android-arm64@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz#0c6246bc8d2c4d172aac2db3fb1190d72bd65504" + integrity sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A== + +"@esbuild/android-arm@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.28.2.tgz#2d84ece6a4e2684d92be26ee13d42757d831c381" + integrity sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg== + +"@esbuild/android-x64@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.28.2.tgz#fc38d4d6358d8dc1cf53f09f7589fe436eb64801" + integrity sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q== + +"@esbuild/darwin-arm64@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz#f83afeeac1d7dac01c7a2fd012b3e451a0591fcc" + integrity sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw== + +"@esbuild/darwin-x64@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz#510147c055a795588dbbe14fd6b1b8ad0a2f30de" + integrity sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw== + +"@esbuild/freebsd-arm64@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz#093b9200ecf0b115ba4e5e248a7485c9c5f8bd5e" + integrity sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw== + +"@esbuild/freebsd-x64@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz#0be22b6df925d213e841ea87123af5df80b0faf7" + integrity sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg== + +"@esbuild/linux-arm64@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz#1bdbc651cda9ba9995c53ed9c71ceaa65094762d" + integrity sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug== + +"@esbuild/linux-arm@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz#beb12ad72b84f72d28488cc1b8ee9f7eb141d753" + integrity sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w== + +"@esbuild/linux-ia32@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz#b81f9d55529b45c206a46a138214b1aa6879696b" + integrity sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ== + +"@esbuild/linux-loong64@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz#598667241a04c99b76ed6ef940ac50038c419f98" + integrity sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ== + +"@esbuild/linux-mips64el@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz#1c51eb9cea903f53d97b5af3b1841db70f5596ca" + integrity sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA== + +"@esbuild/linux-ppc64@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz#63dd61f17ceb31a81227f413feac8a71bc2c51f2" + integrity sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ== + +"@esbuild/linux-riscv64@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz#3763b08fde5cf25ab1facb8e7752edfe45fbfc27" + integrity sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA== + +"@esbuild/linux-s390x@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz#1a137ff293a82906eb3176385bd7e8e0e5cfb7cb" + integrity sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg== + +"@esbuild/linux-x64@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz#268b36211c146ca54f8fe12c578a8d6ef8979485" + integrity sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ== + +"@esbuild/netbsd-arm64@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz#22571ad951d62bb6accc82d8d1fad5c8c1ac0ba1" + integrity sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw== + +"@esbuild/netbsd-x64@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz#42fcc57297eb0a0ca3f5fc475291f4c1a3f7c0de" + integrity sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw== + +"@esbuild/openbsd-arm64@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz#9eb32af104ac3dacf4edca01f596664aab0c73ef" + integrity sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ== + +"@esbuild/openbsd-x64@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz#febed2402d6088225e91f20fb4ce2522ad0a4efd" + integrity sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw== + +"@esbuild/openharmony-arm64@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz#85641c3d466428bfbccea5f21c26836663fef5ce" + integrity sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q== + +"@esbuild/sunos-x64@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz#a736f9d8962481045fc4c3e54f5479f22c870fb4" + integrity sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g== + +"@esbuild/win32-arm64@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz#ee5ab40fad186201b652a33f8a5eb149e9e42532" + integrity sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ== + +"@esbuild/win32-ia32@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz#c40d28a6d99a127da6711f2afd74b11cb63b06a7" + integrity sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA== + +"@esbuild/win32-x64@0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz#b21affb804cc167c133d95f45b3a1dc1323b9a87" + integrity sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g== "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" @@ -900,7 +793,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== -"@jridgewell/trace-mapping@^0.3.23", "@jridgewell/trace-mapping@^0.3.31": +"@jridgewell/trace-mapping@^0.3.31": version "0.3.31" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== @@ -1741,115 +1634,95 @@ dependencies: "@types/node" ">= 8" -"@rollup/rollup-android-arm-eabi@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.48.1.tgz#13cccb90969f7ca3d1354129c859a3b05e90beed" - integrity sha512-rGmb8qoG/zdmKoYELCBwu7vt+9HxZ7Koos3pD0+sH5fR3u3Wb/jGcpnqxcnWsPEKDUyzeLSqksN8LJtgXjqBYw== - -"@rollup/rollup-android-arm64@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.48.1.tgz#0d01925255bb27b56edd095ba1764c3f91f28048" - integrity sha512-4e9WtTxrk3gu1DFE+imNJr4WsL13nWbD/Y6wQcyku5qadlKHY3OQ3LJ/INrrjngv2BJIHnIzbqMk1GTAC2P8yQ== - -"@rollup/rollup-darwin-arm64@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.48.1.tgz#5b11bca1da78d68f26aa98754cecd1887b683689" - integrity sha512-+XjmyChHfc4TSs6WUQGmVf7Hkg8ferMAE2aNYYWjiLzAS/T62uOsdfnqv+GHRjq7rKRnYh4mwWb4Hz7h/alp8A== - -"@rollup/rollup-darwin-x64@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.48.1.tgz#00039989c4cd27ead349c313dc5562c3897c0524" - integrity sha512-upGEY7Ftw8M6BAJyGwnwMw91rSqXTcOKZnnveKrVWsMTF8/k5mleKSuh7D4v4IV1pLxKAk3Tbs0Lo9qYmii5mQ== - -"@rollup/rollup-freebsd-arm64@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.48.1.tgz#1e5aca23d8171313f408759c71342bbee7889f22" - integrity sha512-P9ViWakdoynYFUOZhqq97vBrhuvRLAbN/p2tAVJvhLb8SvN7rbBnJQcBu8e/rQts42pXGLVhfsAP0k9KXWa3nQ== - -"@rollup/rollup-freebsd-x64@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.48.1.tgz#04af010d99ccba84db10d4498cadaac8529ee738" - integrity sha512-VLKIwIpnBya5/saccM8JshpbxfyJt0Dsli0PjXozHwbSVaHTvWXJH1bbCwPXxnMzU4zVEfgD1HpW3VQHomi2AQ== - -"@rollup/rollup-linux-arm-gnueabihf@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.48.1.tgz#67747af83a2dd092144d643caf20b0d1eb817681" - integrity sha512-3zEuZsXfKaw8n/yF7t8N6NNdhyFw3s8xJTqjbTDXlipwrEHo4GtIKcMJr5Ed29leLpB9AugtAQpAHW0jvtKKaQ== - -"@rollup/rollup-linux-arm-musleabihf@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.48.1.tgz#b4bed820cfc5efec00a13190e3d53c0b5240f3b1" - integrity sha512-leo9tOIlKrcBmmEypzunV/2w946JeLbTdDlwEZ7OnnsUyelZ72NMnT4B2vsikSgwQifjnJUbdXzuW4ToN1wV+Q== - -"@rollup/rollup-linux-arm64-gnu@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.48.1.tgz#6b4a7af7e53e7d95c8c1975945d8f8dc8f6d74a9" - integrity sha512-Vy/WS4z4jEyvnJm+CnPfExIv5sSKqZrUr98h03hpAMbE2aI0aD2wvK6GiSe8Gx2wGp3eD81cYDpLLBqNb2ydwQ== - -"@rollup/rollup-linux-arm64-musl@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.48.1.tgz#dd08c8174cfb95d4fa90663dd6be8db27039ad51" - integrity sha512-x5Kzn7XTwIssU9UYqWDB9VpLpfHYuXw5c6bJr4Mzv9kIv242vmJHbI5PJJEnmBYitUIfoMCODDhR7KoZLot2VQ== - -"@rollup/rollup-linux-loongarch64-gnu@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.48.1.tgz#997248c983d2272d1b810e5817cc3c885ebde5ff" - integrity sha512-yzCaBbwkkWt/EcgJOKDUdUpMHjhiZT/eDktOPWvSRpqrVE04p0Nd6EGV4/g7MARXXeOqstflqsKuXVM3H9wOIQ== - -"@rollup/rollup-linux-ppc64-gnu@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.48.1.tgz#57d020583a314741d17b653419d1dbc3fe99bc52" - integrity sha512-UK0WzWUjMAJccHIeOpPhPcKBqax7QFg47hwZTp6kiMhQHeOYJeaMwzeRZe1q5IiTKsaLnHu9s6toSYVUlZ2QtQ== - -"@rollup/rollup-linux-riscv64-gnu@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.48.1.tgz#bbd381e5f99658de9baa0193b89e286767c6e029" - integrity sha512-3NADEIlt+aCdCbWVZ7D3tBjBX1lHpXxcvrLt/kdXTiBrOds8APTdtk2yRL2GgmnSVeX4YS1JIf0imFujg78vpw== - -"@rollup/rollup-linux-riscv64-musl@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.48.1.tgz#2866abbea1e702246900414f878203fea350ccee" - integrity sha512-euuwm/QTXAMOcyiFCcrx0/S2jGvFlKJ2Iro8rsmYL53dlblp3LkUQVFzEidHhvIPPvcIsxDhl2wkBE+I6YVGzA== - -"@rollup/rollup-linux-s390x-gnu@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.48.1.tgz#02f6a1b0f207bf9b6c8f76fca3bd394ad1a5e800" - integrity sha512-w8mULUjmPdWLJgmTYJx/W6Qhln1a+yqvgwmGXcQl2vFBkWsKGUBRbtLRuKJUln8Uaimf07zgJNxOhHOvjSQmBQ== - -"@rollup/rollup-linux-x64-gnu@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.48.1.tgz#867a70767a3e45c1a49b26310548e7861f980016" - integrity sha512-90taWXCWxTbClWuMZD0DKYohY1EovA+W5iytpE89oUPmT5O1HFdf8cuuVIylE6vCbrGdIGv85lVRzTcpTRZ+kA== - -"@rollup/rollup-linux-x64-musl@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.48.1.tgz#4b676c79d85c3ae58e706d44d4be3bc4eb6315cd" - integrity sha512-2Gu29SkFh1FfTRuN1GR1afMuND2GKzlORQUP3mNMJbqdndOg7gNsa81JnORctazHRokiDzQ5+MLE5XYmZW5VWg== - -"@rollup/rollup-win32-arm64-msvc@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.48.1.tgz#0106a573d0c7b82e95c6f57894b65f11bc0c7873" - integrity sha512-6kQFR1WuAO50bxkIlAVeIYsz3RUx+xymwhTo9j94dJ+kmHe9ly7muH23sdfWduD0BA8pD9/yhonUvAjxGh34jQ== - -"@rollup/rollup-win32-ia32-msvc@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.48.1.tgz#351eee360c21415c1efb01d402f53c2a1f5f1a53" - integrity sha512-RUyZZ/mga88lMI3RlXFs4WQ7n3VyU07sPXmMG7/C1NOi8qisUg57Y7LRarqoGoAiopmGmChUhSwfpvQ3H5iGSQ== - -"@rollup/rollup-win32-x64-msvc@4.48.1": - version "4.48.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.48.1.tgz#6821b48385af21ba55c7a5f9f64d19f38ea26014" - integrity sha512-8a/caCUN4vkTChxkaIJcMtwIVcBhi4X2PQRoT+yCK3qRYaZ7cURrmJFL5Ux9H9RaMIXj9RuihckdmkBX3zZsgg== +"@oxc-project/types@=0.143.0": + version "0.143.0" + resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.143.0.tgz#c3e4f3178b7b54e4dd194eac6d45258a60f0092b" + integrity sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA== + +"@rolldown/binding-android-arm64@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.3.tgz#001b8b0b01844701efda1bb6bed84b681c4a488b" + integrity sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw== + +"@rolldown/binding-darwin-arm64@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.3.tgz#5e87c602ed634a6fef092e2162e24fbfb881c4ec" + integrity sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA== + +"@rolldown/binding-darwin-x64@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.3.tgz#f32e0b286714bd03a421d693415d05d97d265b77" + integrity sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA== + +"@rolldown/binding-freebsd-x64@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.3.tgz#5f38ad5761b6b7b21b57a99566bb52634c60ab19" + integrity sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg== + +"@rolldown/binding-linux-arm-gnueabihf@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.3.tgz#ab4dcd07f1bd88e8d659ae0c3bb9d2f290adb897" + integrity sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw== + +"@rolldown/binding-linux-arm64-gnu@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.3.tgz#d279b7016039a725fb66d82784b9841f42df83da" + integrity sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw== + +"@rolldown/binding-linux-arm64-musl@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.3.tgz#d08bbc93d2742214548c5adf7df7788944e5a89a" + integrity sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q== + +"@rolldown/binding-linux-ppc64-gnu@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.3.tgz#6418e63745b3193f26ab3bb88744b3a4a1356d7c" + integrity sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w== + +"@rolldown/binding-linux-s390x-gnu@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.3.tgz#77ec30d0704cf4eb1cb4a63f501c9852c6728cf4" + integrity sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg== + +"@rolldown/binding-linux-x64-gnu@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.3.tgz#3b9b6e0dd3e86c597f42858748ca25f1dfd58ed8" + integrity sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w== + +"@rolldown/binding-linux-x64-musl@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.3.tgz#f78033c592c8bd2af48284a45f8e4baaa0befbf5" + integrity sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A== + +"@rolldown/binding-openharmony-arm64@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.3.tgz#36e951f5a6fca922a5205e283d0a82b9f98199ca" + integrity sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug== + +"@rolldown/binding-win32-arm64-msvc@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.3.tgz#c1e494ac47e13bd857fca0b3ad59c33580241f7e" + integrity sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw== + +"@rolldown/binding-win32-x64-msvc@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.3.tgz#b0effffcd6872f8a021373eb437916b1b52283a4" + integrity sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg== + +"@rolldown/pluginutils@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz#e3fcee093fbb5ce765e1ad088ff4de2889f6f9be" + integrity sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw== "@rtsao/scc@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== -"@standard-schema/spec@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.0.0.tgz#f193b73dc316c4170f2e82a881da0f550d551b9c" - integrity sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA== +"@standard-schema/spec@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.1.0.tgz#a79b55dbaf8604812f52d140b2c9ab41bc150bb8" + integrity sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w== "@types/chai@^5.2.2": version "5.2.2" @@ -1863,7 +1736,7 @@ resolved "https://registry.yarnpkg.com/@types/deep-eql/-/deep-eql-4.0.2.tgz#334311971d3a07121e7eb91b684a605e7eea9cbd" integrity sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw== -"@types/estree@1.0.8", "@types/estree@^1.0.0": +"@types/estree@^1.0.0": version "1.0.8" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== @@ -1903,27 +1776,22 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.0.tgz#7d4411bf5157339337d7cff864d9ff45f177b499" integrity sha512-mikldZQitV94akrc4sCcSjtJfsTKt4p+e/s0AGscVA6XArQ9kFclP+ZiYUMnq987rc6QlYxXv/EivqlfSLxpKA== -"@types/node@^22.17.0": - version "22.17.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.17.0.tgz#e8c9090e957bd4d9860efb323eb92d297347eac7" - integrity sha512-bbAKTCqX5aNVryi7qXVMi+OkB3w/OyblodicMbvE38blyAz7GxXf6XYhklokijuPwwVg9sDLKRxt0ZHXQwZVfQ== +"@types/node@^24", "@types/node@^24.13.3": + version "24.13.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.13.3.tgz#49f18bd3c647866dcda51a0756c145e14590ce16" + integrity sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q== dependencies: - undici-types "~6.21.0" + undici-types "~7.18.0" "@types/normalize-package-data@^2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== -"@types/semver@^7.5.8": - version "7.5.8" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" - integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== - -"@types/semver@^7.7.0": - version "7.7.0" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.7.0.tgz#64c441bdae033b378b6eef7d0c3d77c329b9378e" - integrity sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA== +"@types/semver@^7.8.0": + version "7.8.0" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.8.0.tgz#0bfe3ec51f5e9615bc317174cd5b88ea08b7fc2f" + integrity sha512-1mAINjtQCXXeLkJ9ehXkwOcBpqtLxiVtKhpUf83DdRNdQKV0iXZpaHYqRr7nj+wvxuJzoAmAwXI+sCNMv1CzLQ== "@types/ws@^8.5.5": version "8.5.14" @@ -2018,80 +1886,81 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== -"@vitest/coverage-v8@^4.0.15": - version "4.0.15" - resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-4.0.15.tgz#5daef6798ced6ed15f4f06f1caf789e1e0da8a11" - integrity sha512-FUJ+1RkpTFW7rQITdgTi93qOCWJobWhBirEPCeXh2SW2wsTlFxy51apDz5gzG+ZEYt/THvWeNmhdAoS9DTwpCw== +"@vitest/coverage-v8@^4.1.10": + version "4.1.10" + resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-4.1.10.tgz#037cd5e7ea8a2f448f4c2e10db1411c2b0c927bd" + integrity sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g== dependencies: "@bcoe/v8-coverage" "^1.0.2" - "@vitest/utils" "4.0.15" - ast-v8-to-istanbul "^0.3.8" + "@vitest/utils" "4.1.10" + ast-v8-to-istanbul "^1.0.0" istanbul-lib-coverage "^3.2.2" istanbul-lib-report "^3.0.1" - istanbul-lib-source-maps "^5.0.6" istanbul-reports "^3.2.0" - magicast "^0.5.1" + magicast "^0.5.2" obug "^2.1.1" - std-env "^3.10.0" - tinyrainbow "^3.0.3" + std-env "^4.0.0-rc.1" + tinyrainbow "^3.1.0" -"@vitest/expect@4.0.15": - version "4.0.15" - resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-4.0.15.tgz#8e7e1daf54b7bc9ef6db4d989563c1d55ce424f5" - integrity sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w== +"@vitest/expect@4.1.10": + version "4.1.10" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-4.1.10.tgz#799c06fc44bb0cf7e2784137b627c5cc173285d4" + integrity sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA== dependencies: - "@standard-schema/spec" "^1.0.0" + "@standard-schema/spec" "^1.1.0" "@types/chai" "^5.2.2" - "@vitest/spy" "4.0.15" - "@vitest/utils" "4.0.15" - chai "^6.2.1" - tinyrainbow "^3.0.3" + "@vitest/spy" "4.1.10" + "@vitest/utils" "4.1.10" + chai "^6.2.2" + tinyrainbow "^3.1.0" -"@vitest/mocker@4.0.15": - version "4.0.15" - resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-4.0.15.tgz#5aca5f9c4691efdd2397763efcbde9c680f79caf" - integrity sha512-CZ28GLfOEIFkvCFngN8Sfx5h+Se0zN+h4B7yOsPVCcgtiO7t5jt9xQh2E1UkFep+eb9fjyMfuC5gBypwb07fvQ== +"@vitest/mocker@4.1.10": + version "4.1.10" + resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-4.1.10.tgz#2413987ab4cd7fa1c2b614b404c407bf6ad1ead1" + integrity sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow== dependencies: - "@vitest/spy" "4.0.15" + "@vitest/spy" "4.1.10" estree-walker "^3.0.3" magic-string "^0.30.21" -"@vitest/pretty-format@4.0.15": - version "4.0.15" - resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-4.0.15.tgz#2cd8e1bcb4fc8e24124d889a23d1140aecca5744" - integrity sha512-SWdqR8vEv83WtZcrfLNqlqeQXlQLh2iilO1Wk1gv4eiHKjEzvgHb2OVc3mIPyhZE6F+CtfYjNlDJwP5MN6Km7A== +"@vitest/pretty-format@4.1.10": + version "4.1.10" + resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-4.1.10.tgz#75542e7273a08cc10fd4d8dad4e3eb1f16cd958c" + integrity sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q== dependencies: - tinyrainbow "^3.0.3" + tinyrainbow "^3.1.0" -"@vitest/runner@4.0.15": - version "4.0.15" - resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-4.0.15.tgz#fdd4e5d05a4c6b73be9746845d29c7329c37ae3b" - integrity sha512-+A+yMY8dGixUhHmNdPUxOh0la6uVzun86vAbuMT3hIDxMrAOmn5ILBHm8ajrqHE0t8R9T1dGnde1A5DTnmi3qw== +"@vitest/runner@4.1.10": + version "4.1.10" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-4.1.10.tgz#febf0a21a9168421422d1955370e606feab60355" + integrity sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg== dependencies: - "@vitest/utils" "4.0.15" + "@vitest/utils" "4.1.10" pathe "^2.0.3" -"@vitest/snapshot@4.0.15": - version "4.0.15" - resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-4.0.15.tgz#52f686d7f314bae53657c1404f8ce7b0b99d2cec" - integrity sha512-A7Ob8EdFZJIBjLjeO0DZF4lqR6U7Ydi5/5LIZ0xcI+23lYlsYJAfGn8PrIWTYdZQRNnSRlzhg0zyGu37mVdy5g== +"@vitest/snapshot@4.1.10": + version "4.1.10" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-4.1.10.tgz#7e3e9fec7d4d47232e493cfdcbd2170de4371c04" + integrity sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw== dependencies: - "@vitest/pretty-format" "4.0.15" + "@vitest/pretty-format" "4.1.10" + "@vitest/utils" "4.1.10" magic-string "^0.30.21" pathe "^2.0.3" -"@vitest/spy@4.0.15": - version "4.0.15" - resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-4.0.15.tgz#57987c857c3f1bcea5513b379e8dfc8f06b37b8f" - integrity sha512-+EIjOJmnY6mIfdXtE/bnozKEvTC4Uczg19yeZ2vtCz5Yyb0QQ31QWVQ8hswJ3Ysx/K2EqaNsVanjr//2+P3FHw== +"@vitest/spy@4.1.10": + version "4.1.10" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-4.1.10.tgz#5c0bfa97b56bba9e37403c976db776ff6ab56f65" + integrity sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw== -"@vitest/utils@4.0.15": - version "4.0.15" - resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-4.0.15.tgz#2e36d5c34656a1ce1a057d8595a835bff524f1bc" - integrity sha512-HXjPW2w5dxhTD0dLwtYHDnelK3j8sR8cWIaLxr22evTyY6q8pRCjZSmhRWVjBaOVXChQd6AwMzi9pucorXCPZA== +"@vitest/utils@4.1.10": + version "4.1.10" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-4.1.10.tgz#ffc71055f18bfccb1fd0586365ebc2824892e403" + integrity sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA== dependencies: - "@vitest/pretty-format" "4.0.15" - tinyrainbow "^3.0.3" + "@vitest/pretty-format" "4.1.10" + convert-source-map "^2.0.0" + tinyrainbow "^3.1.0" "@zkochan/cmd-shim@^3.1.0": version "3.1.0" @@ -2146,14 +2015,6 @@ agentkeepalive@^3.4.1: dependencies: humanize-ms "^1.2.1" -aggregate-error@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-4.0.1.tgz#25091fe1573b9e0be892aeda15c7c66a545f758e" - integrity sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w== - dependencies: - clean-stack "^4.0.0" - indent-string "^5.0.0" - ajv@^6.12.3: version "6.12.4" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234" @@ -2199,6 +2060,11 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" + integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== + ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -2380,11 +2246,6 @@ arrify@^1.0.1: resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= -arrify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-3.0.0.tgz#ccdefb8eaf2a1d2ab0da1ca2ce53118759fd46bc" - integrity sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw== - asap@^2.0.0: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" @@ -2407,14 +2268,14 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= -ast-v8-to-istanbul@^0.3.8: - version "0.3.8" - resolved "https://registry.yarnpkg.com/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.8.tgz#0a3faf070dc780dcebdf9d48af78dbd174a497a9" - integrity sha512-szgSZqUxI5T8mLKvS7WTjF9is+MVbOeLADU73IseOcrqhxr/VAvy6wfoVE39KnKzA7JRhjF5eUagNlHwvZPlKQ== +ast-v8-to-istanbul@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.5.tgz#708baeb6f5c879226d112a341ffa821c43881d2d" + integrity sha512-UPAgKJFSEGMWSDr3LX4tqnAb4f7KGT8O40Tyx8wbYmmZ/yn58lNCm8h3svs3eXgiGd5AXxz8NDOvXWvicq+rJA== dependencies: "@jridgewell/trace-mapping" "^0.3.31" estree-walker "^3.0.3" - js-tokens "^9.0.1" + js-tokens "^10.0.0" async-mutex@^0.5.0: version "0.5.0" @@ -2650,7 +2511,7 @@ callsites@^2.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= -callsites@^3.0.0, callsites@^3.1.0: +callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== @@ -2701,15 +2562,15 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chai@^6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/chai/-/chai-6.2.1.tgz#d1e64bc42433fbee6175ad5346799682060b5b6a" - integrity sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg== +chai@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/chai/-/chai-6.2.2.tgz#ae41b52c9aca87734505362717f3255facda360e" + integrity sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg== -chalk-template@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/chalk-template/-/chalk-template-1.1.0.tgz#ffc55db6dd745e9394b85327c8ac8466edb7a7b1" - integrity sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg== +chalk-template@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/chalk-template/-/chalk-template-1.1.2.tgz#88ff13e75a333d232304e13abc48c5b5be15f1ce" + integrity sha512-2bxTP2yUH7AJj/VAXfcA+4IcWGdQ87HwBANLt5XxGTeomo8yG0y95N1um9i5StvhT/Bl0/2cARA5v1PpPXUxUA== dependencies: chalk "^5.2.0" @@ -2730,11 +2591,16 @@ chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^5.2.0, chalk@^5.4.1: +chalk@^5.2.0: version "5.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.4.1.tgz#1b48bf0963ec158dce2aacf69c093ae2dd2092d8" integrity sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w== +chalk@^5.6.2: + version "5.6.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.6.2.tgz#b1238b6e23ea337af71c7f8a295db5af0c158aea" + integrity sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA== + chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -2760,21 +2626,6 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -clean-stack@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-4.2.0.tgz#c464e4cde4ac789f4e0735c5d75beb49d7b30b31" - integrity sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg== - dependencies: - escape-string-regexp "5.0.0" - -clear-module@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/clear-module/-/clear-module-4.1.2.tgz#5a58a5c9f8dccf363545ad7284cad3c887352a80" - integrity sha512-LWAxzHqdHsAZlPlEyJ2Poz6AIs384mPeqLVCru2p0BrP9G/kVGuhNyZYClLO6cXlnuJjzC8xtsJIuMjKqLXoAw== - dependencies: - parent-module "^2.0.0" - resolve-from "^5.0.0" - cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -2867,16 +2718,18 @@ commander@^14.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-14.0.0.tgz#f244fc74a92343514e56229f16ef5c5e22ced5e9" integrity sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA== -comment-json@^4.2.5: - version "4.2.5" - resolved "https://registry.yarnpkg.com/comment-json/-/comment-json-4.2.5.tgz#482e085f759c2704b60bc6f97f55b8c01bc41e70" - integrity sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw== +commander@^14.0.3: + version "14.0.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-14.0.3.tgz#425d79b48f9af82fcd9e4fc1ea8af6c5ec07bbc2" + integrity sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw== + +comment-json@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/comment-json/-/comment-json-5.0.0.tgz#3b0cba63da30b31f8b3ea8d75f4d79bfa8346896" + integrity sha512-uiqLcOiVDJtBP8WGkZHEP+FZIhTzP1dxvn59EfoYUi9gqupjrBWVQkO2atDrbnKPwLeotFYDsuNb26uBMqB+hw== dependencies: array-timsort "^1.0.3" - core-util-is "^1.0.3" esprima "^4.0.1" - has-own-prop "^2.0.0" - repeat-string "^1.6.1" compare-func@^2.0.0: version "2.0.0" @@ -3017,6 +2870,11 @@ conventional-recommended-bump@^5.0.0: meow "^4.0.0" q "^1.5.1" +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" @@ -3039,11 +2897,6 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -core-util-is@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== - cosmiconfig@^5.1.0: version "5.2.1" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" @@ -3054,37 +2907,6 @@ cosmiconfig@^5.1.0: js-yaml "^3.13.1" parse-json "^4.0.0" -cp-file@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/cp-file/-/cp-file-10.0.0.tgz#bbae9ecb9f505951b862880d2901e1f56de7a4dc" - integrity sha512-vy2Vi1r2epK5WqxOLnskeKeZkdZvTKfFZQCplE3XWsP+SUJyd5XAUFC9lFgTjjXJF2GMne/UML14iEmkAaDfFg== - dependencies: - graceful-fs "^4.2.10" - nested-error-stacks "^2.1.1" - p-event "^5.0.1" - -cpy-cli@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/cpy-cli/-/cpy-cli-5.0.0.tgz#facd60da2e98d9a830f93162f9769d2a86667a16" - integrity sha512-fb+DZYbL9KHc0BC4NYqGRrDIJZPXUmjjtqdw4XRRg8iV8dIfghUX/WiL+q4/B/KFTy3sK6jsbUhBaz0/Hxg7IQ== - dependencies: - cpy "^10.1.0" - meow "^12.0.1" - -cpy@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/cpy/-/cpy-10.1.0.tgz#85517387036b9be480f6424e54089261fc6f4bab" - integrity sha512-VC2Gs20JcTyeQob6UViBLnyP0bYHkBh6EiKzot9vi2DmeGlFT9Wd7VG3NBrkNx/jYvFBeyDOMMHdHQhbtKLgHQ== - dependencies: - arrify "^3.0.0" - cp-file "^10.0.0" - globby "^13.1.4" - junk "^4.0.1" - micromatch "^4.0.5" - nested-error-stacks "^2.1.1" - p-filter "^3.0.0" - p-map "^6.0.0" - cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -3105,121 +2927,120 @@ cross-spawn@^7.0.2: shebang-command "^2.0.0" which "^2.0.1" -cspell-config-lib@9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/cspell-config-lib/-/cspell-config-lib-9.2.0.tgz#dc85330fae8aab6f1f362c678a698b8816ddbdf0" - integrity sha512-Yc8+hT+uIWWCi6WMhOL6HDYbBCP2qig1tgKGThHVeOx6GviieV10TZ5kQ+P7ONgoqw2nmm7uXIC19dGYx3DblQ== - dependencies: - "@cspell/cspell-types" "9.2.0" - comment-json "^4.2.5" - smol-toml "^1.4.1" - yaml "^2.8.0" - -cspell-dictionary@9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/cspell-dictionary/-/cspell-dictionary-9.2.0.tgz#f4f13ee22e0207da6a4c8ec76eedb6672e16a345" - integrity sha512-lV4VtjsDtxu8LyCcb6DY7Br4e/Aw1xfR8QvjYhHaJ8t03xry9STey5Rkfp+lz+hlVevNcn3lfCaacGuXyD+lLg== - dependencies: - "@cspell/cspell-pipe" "9.2.0" - "@cspell/cspell-types" "9.2.0" - cspell-trie-lib "9.2.0" - fast-equals "^5.2.2" - -cspell-gitignore@9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/cspell-gitignore/-/cspell-gitignore-9.2.0.tgz#34ee91bc741f6efa0fa86a1d1565cc4311bc9ffa" - integrity sha512-gXDQZ7czTPwmEg1qtsUIjVEFm9IfgTO8rA02O8eYIveqjFixbSV3fIYOgoxZSZYxjt3O44m8+/zAFC1RE4CM/Q== - dependencies: - "@cspell/url" "9.2.0" - cspell-glob "9.2.0" - cspell-io "9.2.0" - -cspell-glob@9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/cspell-glob/-/cspell-glob-9.2.0.tgz#adc61596c96b3572c77dabe5da0d71320e714ab3" - integrity sha512-viycZDyegzW2AKPFqvX5RveqTrB0sKgexlCu2A8z8eumpYYor5sD1NP05VDOqkAF4hDuiGqkHn6iNo0L1wNgLw== - dependencies: - "@cspell/url" "9.2.0" - picomatch "^4.0.3" - -cspell-grammar@9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/cspell-grammar/-/cspell-grammar-9.2.0.tgz#c29aabf67e99506d15f7d52e9c2ad99466b7e8f2" - integrity sha512-qthAmWcNHpYAmufy7YWVg9xwrYANkVlI40bgC2uGd8EnKssm/qOPhqXXNS+kLf+q0NmJM5nMgRLhCC23xSp3JA== - dependencies: - "@cspell/cspell-pipe" "9.2.0" - "@cspell/cspell-types" "9.2.0" - -cspell-io@9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/cspell-io/-/cspell-io-9.2.0.tgz#ee18fcbd94a67feae0a8938c2d81d7b577d1d48e" - integrity sha512-oxKiqFLcz629FmOId8UpdDznpMvCgpuktg4nkD2G9pYpRh+fRLZpP4QtZPyvJqvpUIzFhIOznMeHjsiBYHOZUA== - dependencies: - "@cspell/cspell-service-bus" "9.2.0" - "@cspell/url" "9.2.0" - -cspell-lib@9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/cspell-lib/-/cspell-lib-9.2.0.tgz#f7d5dd20d2b1d2a30f44c35dc8f06d25daaff79c" - integrity sha512-RnhDIsETw6Ex0UaK3PFoJ2FwWMWfJPtdpNpv1qgmJwoGD4CzwtIqPOLtZ24zqdCP8ZnNTF/lwV/9rZVqifYjsw== - dependencies: - "@cspell/cspell-bundled-dicts" "9.2.0" - "@cspell/cspell-pipe" "9.2.0" - "@cspell/cspell-resolver" "9.2.0" - "@cspell/cspell-types" "9.2.0" - "@cspell/dynamic-import" "9.2.0" - "@cspell/filetypes" "9.2.0" - "@cspell/strong-weak-map" "9.2.0" - "@cspell/url" "9.2.0" - clear-module "^4.1.2" - comment-json "^4.2.5" - cspell-config-lib "9.2.0" - cspell-dictionary "9.2.0" - cspell-glob "9.2.0" - cspell-grammar "9.2.0" - cspell-io "9.2.0" - cspell-trie-lib "9.2.0" - env-paths "^3.0.0" - fast-equals "^5.2.2" - gensequence "^7.0.0" - import-fresh "^3.3.1" +cspell-config-lib@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/cspell-config-lib/-/cspell-config-lib-10.0.1.tgz#863860db3a0b9e93f0f63ece283b44333bbaa9ed" + integrity sha512-hMpo/0j6k7pbiqrLDOLJKD2IGP9XwhjKf2miiM6p84Xeo4nyuFZaxxDCQ68R851HSYFrrdltgpoipMbj1h2Tnw== + dependencies: + "@cspell/cspell-types" "10.0.1" + comment-json "^5.0.0" + smol-toml "^1.6.1" + yaml "^2.9.0" + +cspell-dictionary@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/cspell-dictionary/-/cspell-dictionary-10.0.1.tgz#a4947f20fc2bf07cdfad0370400c9e2e8fba4c17" + integrity sha512-3cZ659vgsZWkzGQJR/sNqGDVt/OnvTSieLKI76V++4t1bHJfochb9ZrrwsuMsb1VPGiyqClUP1/O6WrefF/FVg== + dependencies: + "@cspell/cspell-performance-monitor" "10.0.1" + "@cspell/cspell-pipe" "10.0.1" + "@cspell/cspell-types" "10.0.1" + cspell-trie-lib "10.0.1" + fast-equals "^6.0.0" + +cspell-gitignore@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/cspell-gitignore/-/cspell-gitignore-10.0.1.tgz#3ade523387114c18d434be174031aa5ac3917fc0" + integrity sha512-wN23U61Mx6qPJN3CesOmBU9vnbJ0jQm/ylK0iaVui3CcnO7Zzl5qLu5mPHUzGQGm8yso6qjyxqo16Ho7LpZGOQ== + dependencies: + "@cspell/url" "10.0.1" + cspell-glob "10.0.1" + cspell-io "10.0.1" + +cspell-glob@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/cspell-glob/-/cspell-glob-10.0.1.tgz#1bf73a85b408f2cc459e0c0491558713c4bcd2a0" + integrity sha512-7bII9J3aSSpZDwhx7w+zfQXbMxHZQ3be0ilUp5bHrsjz6o07v/NqOHMGcwKdPn1sw2dxDz9sv057xE5pqXnSdw== + dependencies: + "@cspell/url" "10.0.1" + picomatch "^4.0.4" + +cspell-grammar@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/cspell-grammar/-/cspell-grammar-10.0.1.tgz#d31b97bf7f9469490c09a99fe969dd0ca0cc54c2" + integrity sha512-xC9AFYmaI9wsO//a7S5tdDGKGJVD5UEEsTg+Up2fi7lPfXIryisYmV6tePNL1SEg0idYss4ja8LUZ3Mib09BjQ== + dependencies: + "@cspell/cspell-pipe" "10.0.1" + "@cspell/cspell-types" "10.0.1" + +cspell-io@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/cspell-io/-/cspell-io-10.0.1.tgz#3f362ea44d235f5e63f458c3ed0b578139316cca" + integrity sha512-8C2ka07faxflnaqEBO3pektS21XViE/SEHT7F5ZD1ou7FyMR5u3xawTBJSczClfsxLt/WYeztBYrpmGAjmjksw== + dependencies: + "@cspell/cspell-service-bus" "10.0.1" + "@cspell/url" "10.0.1" + +cspell-lib@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/cspell-lib/-/cspell-lib-10.0.1.tgz#794d9d5088051ec5e4088b4a3c10cd309cbe158c" + integrity sha512-RpsIPiLzc4/YMW8BMRKpyJ81x439qjYWcqgdKeXnMkbKM88J9PexzutfFf/4v97v96KzfNitEzMpbI0uj8OeUg== + dependencies: + "@cspell/cspell-bundled-dicts" "10.0.1" + "@cspell/cspell-performance-monitor" "10.0.1" + "@cspell/cspell-pipe" "10.0.1" + "@cspell/cspell-resolver" "10.0.1" + "@cspell/cspell-types" "10.0.1" + "@cspell/dynamic-import" "10.0.1" + "@cspell/filetypes" "10.0.1" + "@cspell/rpc" "10.0.1" + "@cspell/strong-weak-map" "10.0.1" + "@cspell/url" "10.0.1" + cspell-config-lib "10.0.1" + cspell-dictionary "10.0.1" + cspell-glob "10.0.1" + cspell-grammar "10.0.1" + cspell-io "10.0.1" + cspell-trie-lib "10.0.1" + env-paths "^4.0.0" + gensequence "^8.0.8" + import-fresh "^4.0.0" resolve-from "^5.0.0" vscode-languageserver-textdocument "^1.0.12" vscode-uri "^3.1.0" xdg-basedir "^5.1.0" -cspell-trie-lib@9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/cspell-trie-lib/-/cspell-trie-lib-9.2.0.tgz#968b42cc60681e8fad36e601edf4c11985c70950" - integrity sha512-6GHL1KvLQzcPBSNY6QWOabq8YwRJAnNKamA0O/tRKy+11Hy99ysD4xvfu3kKYPAcobp5ZykX4nudHxy8yrEvng== - dependencies: - "@cspell/cspell-pipe" "9.2.0" - "@cspell/cspell-types" "9.2.0" - gensequence "^7.0.0" - -cspell@^9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/cspell/-/cspell-9.2.0.tgz#a3197a7583f632c0d16ef3c24da5c0009a43ef49" - integrity sha512-AKzaFMem2jRcGpAY2spKP0z15jpZeX1WTDNHCDsB8/YvnhnOfWXc0S5AF+4sfU1cQgHWYGFOolMuTri0ZQdV+Q== - dependencies: - "@cspell/cspell-json-reporter" "9.2.0" - "@cspell/cspell-pipe" "9.2.0" - "@cspell/cspell-types" "9.2.0" - "@cspell/dynamic-import" "9.2.0" - "@cspell/url" "9.2.0" - chalk "^5.4.1" - chalk-template "^1.1.0" - commander "^14.0.0" - cspell-config-lib "9.2.0" - cspell-dictionary "9.2.0" - cspell-gitignore "9.2.0" - cspell-glob "9.2.0" - cspell-io "9.2.0" - cspell-lib "9.2.0" +cspell-trie-lib@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/cspell-trie-lib/-/cspell-trie-lib-10.0.1.tgz#d93adf6c104f584d1b7af2f0a76b014407a22cf2" + integrity sha512-BFvhalSkRQFjKrZ//FKK7fRGrZFpifnxB5AwCkzsIsBZqicsfafcQ1xP21qpb0QqyV/IomjNgviG+tRJs+0rMw== + +cspell@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/cspell/-/cspell-10.0.1.tgz#c6b58ff7bee40f7151f5e3258bbac8a31e963d64" + integrity sha512-Gg6w/flT3fKfl3la62hfTnhtNnDQ+9mU7kUhVqw/axl/Ms4oENw0oJMkWFIoj4f6nL/SDPz7KcPXd2XbkKFNmQ== + dependencies: + "@cspell/cspell-json-reporter" "10.0.1" + "@cspell/cspell-performance-monitor" "10.0.1" + "@cspell/cspell-pipe" "10.0.1" + "@cspell/cspell-types" "10.0.1" + "@cspell/cspell-worker" "10.0.1" + "@cspell/dynamic-import" "10.0.1" + "@cspell/url" "10.0.1" + ansi-regex "^6.2.2" + chalk "^5.6.2" + chalk-template "^1.1.2" + commander "^14.0.3" + cspell-config-lib "10.0.1" + cspell-dictionary "10.0.1" + cspell-gitignore "10.0.1" + cspell-glob "10.0.1" + cspell-io "10.0.1" + cspell-lib "10.0.1" fast-json-stable-stringify "^2.1.0" - flatted "^3.3.3" - semver "^7.7.2" - tinyglobby "^0.2.14" + flatted "^3.4.2" + semver "^7.8.1" + tinyglobby "^0.2.16" currently-unhandled@^0.4.1: version "0.4.1" @@ -3307,13 +3128,6 @@ debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.1.1: - version "4.4.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" - integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== - dependencies: - ms "^2.1.3" - debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.6" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" @@ -3435,6 +3249,11 @@ detect-indent@^5.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= +detect-libc@^2.0.3: + version "2.1.2" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" + integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== + dezalgo@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" @@ -3541,10 +3360,12 @@ env-paths@^2.2.0: resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43" integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA== -env-paths@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-3.0.0.tgz#2f1e89c2f6dbd3408e1b1711dd82d62e317f58da" - integrity sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A== +env-paths@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-4.0.0.tgz#d0bb1f84a81d2542581bf7b7e8085d0683b39097" + integrity sha512-pxP8eL2SwwaTRi/KHYwLYXinDs7gL3jxFcBYmEdYfZmZXbaVDvdppd0XBU8qVz03rDfKZMXg1omHCbsJjZrMsw== + dependencies: + is-safe-filename "^0.1.0" envinfo@^7.3.1: version "7.7.3" @@ -3647,10 +3468,10 @@ es-errors@^1.3.0: resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== -es-module-lexer@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a" - integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== +es-module-lexer@^2.0.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-2.3.1.tgz#5bf2df06999dbbe5f006a5f46a11fb9f5b7b391b" + integrity sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA== es-object-atoms@^1.0.0: version "1.1.1" @@ -3697,74 +3518,37 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" -esbuild@^0.25.0: - version "0.25.9" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.9.tgz#15ab8e39ae6cdc64c24ff8a2c0aef5b3fd9fa976" - integrity sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g== - optionalDependencies: - "@esbuild/aix-ppc64" "0.25.9" - "@esbuild/android-arm" "0.25.9" - "@esbuild/android-arm64" "0.25.9" - "@esbuild/android-x64" "0.25.9" - "@esbuild/darwin-arm64" "0.25.9" - "@esbuild/darwin-x64" "0.25.9" - "@esbuild/freebsd-arm64" "0.25.9" - "@esbuild/freebsd-x64" "0.25.9" - "@esbuild/linux-arm" "0.25.9" - "@esbuild/linux-arm64" "0.25.9" - "@esbuild/linux-ia32" "0.25.9" - "@esbuild/linux-loong64" "0.25.9" - "@esbuild/linux-mips64el" "0.25.9" - "@esbuild/linux-ppc64" "0.25.9" - "@esbuild/linux-riscv64" "0.25.9" - "@esbuild/linux-s390x" "0.25.9" - "@esbuild/linux-x64" "0.25.9" - "@esbuild/netbsd-arm64" "0.25.9" - "@esbuild/netbsd-x64" "0.25.9" - "@esbuild/openbsd-arm64" "0.25.9" - "@esbuild/openbsd-x64" "0.25.9" - "@esbuild/openharmony-arm64" "0.25.9" - "@esbuild/sunos-x64" "0.25.9" - "@esbuild/win32-arm64" "0.25.9" - "@esbuild/win32-ia32" "0.25.9" - "@esbuild/win32-x64" "0.25.9" - -esbuild@~0.25.0: - version "0.25.8" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.8.tgz#482d42198b427c9c2f3a81b63d7663aecb1dda07" - integrity sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q== +esbuild@~0.28.0: + version "0.28.2" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.28.2.tgz#0f43bd1bad955b72d24e2261e3abe5957ccf0816" + integrity sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA== optionalDependencies: - "@esbuild/aix-ppc64" "0.25.8" - "@esbuild/android-arm" "0.25.8" - "@esbuild/android-arm64" "0.25.8" - "@esbuild/android-x64" "0.25.8" - "@esbuild/darwin-arm64" "0.25.8" - "@esbuild/darwin-x64" "0.25.8" - "@esbuild/freebsd-arm64" "0.25.8" - "@esbuild/freebsd-x64" "0.25.8" - "@esbuild/linux-arm" "0.25.8" - "@esbuild/linux-arm64" "0.25.8" - "@esbuild/linux-ia32" "0.25.8" - "@esbuild/linux-loong64" "0.25.8" - "@esbuild/linux-mips64el" "0.25.8" - "@esbuild/linux-ppc64" "0.25.8" - "@esbuild/linux-riscv64" "0.25.8" - "@esbuild/linux-s390x" "0.25.8" - "@esbuild/linux-x64" "0.25.8" - "@esbuild/netbsd-arm64" "0.25.8" - "@esbuild/netbsd-x64" "0.25.8" - "@esbuild/openbsd-arm64" "0.25.8" - "@esbuild/openbsd-x64" "0.25.8" - "@esbuild/openharmony-arm64" "0.25.8" - "@esbuild/sunos-x64" "0.25.8" - "@esbuild/win32-arm64" "0.25.8" - "@esbuild/win32-ia32" "0.25.8" - "@esbuild/win32-x64" "0.25.8" - -escape-string-regexp@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" - integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== + "@esbuild/aix-ppc64" "0.28.2" + "@esbuild/android-arm" "0.28.2" + "@esbuild/android-arm64" "0.28.2" + "@esbuild/android-x64" "0.28.2" + "@esbuild/darwin-arm64" "0.28.2" + "@esbuild/darwin-x64" "0.28.2" + "@esbuild/freebsd-arm64" "0.28.2" + "@esbuild/freebsd-x64" "0.28.2" + "@esbuild/linux-arm" "0.28.2" + "@esbuild/linux-arm64" "0.28.2" + "@esbuild/linux-ia32" "0.28.2" + "@esbuild/linux-loong64" "0.28.2" + "@esbuild/linux-mips64el" "0.28.2" + "@esbuild/linux-ppc64" "0.28.2" + "@esbuild/linux-riscv64" "0.28.2" + "@esbuild/linux-s390x" "0.28.2" + "@esbuild/linux-x64" "0.28.2" + "@esbuild/netbsd-arm64" "0.28.2" + "@esbuild/netbsd-x64" "0.28.2" + "@esbuild/openbsd-arm64" "0.28.2" + "@esbuild/openbsd-x64" "0.28.2" + "@esbuild/openharmony-arm64" "0.28.2" + "@esbuild/sunos-x64" "0.28.2" + "@esbuild/win32-arm64" "0.28.2" + "@esbuild/win32-ia32" "0.28.2" + "@esbuild/win32-x64" "0.28.2" escape-string-regexp@^1.0.5: version "1.0.5" @@ -4016,10 +3800,10 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expect-type@^1.2.2: - version "1.3.0" - resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.3.0.tgz#0d58ed361877a31bbc4dd6cf71bbfef7faf6bd68" - integrity sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA== +expect-type@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.4.0.tgz#24edf7f0cc69a44d008567ba4594ab96f3c3a3d6" + integrity sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA== extend-shallow@^2.0.1: version "2.0.1" @@ -4079,10 +3863,10 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-equals@^5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-5.2.2.tgz#885d7bfb079fac0ce0e8450374bce29e9b742484" - integrity sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw== +fast-equals@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-6.0.2.tgz#36b0a0289a927f48a3c99b773970a077b2deb57f" + integrity sha512-sAjhj9ZhOxYCGiNMnZLaucOqf5ZeFnHNoKoAZiD9thhJ0N8RP85qJK759/97C/3L7NzzmGVB5uiX9AUpySZmUQ== fast-glob@^2.2.6: version "2.2.7" @@ -4107,17 +3891,6 @@ fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-glob@^3.3.0: - version "3.3.3" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" - integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.8" - fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -4135,20 +3908,15 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" -fdir@^6.4.4: - version "6.4.6" - resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.6.tgz#2b268c0232697063111bbf3f64810a2a741ba281" - integrity sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w== - fdir@^6.5.0: version "6.5.0" resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== -fflate@^0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" - integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== +fflate@^0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.3.tgz#bc27d8eb30343d4d512abb03480202ce65d825fc" + integrity sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA== figgy-pudding@^3.4.1, figgy-pudding@^3.5.1: version "3.5.2" @@ -4238,10 +4006,10 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== -flatted@^3.3.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" - integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== +flatted@^3.4.2: + version "3.4.4" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.4.tgz#aeeca2a506303f0cee61c59e6c9f2a88d2f29fc6" + integrity sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q== flush-write-stream@^1.0.0: version "1.1.1" @@ -4323,7 +4091,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@~2.3.2, fsevents@~2.3.3: +fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== @@ -4369,10 +4137,10 @@ genfun@^5.0.0: resolved "https://registry.yarnpkg.com/genfun/-/genfun-5.0.0.tgz#9dd9710a06900a5c4a5bf57aca5da4e52fe76537" integrity sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA== -gensequence@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/gensequence/-/gensequence-7.0.0.tgz#bb6aedec8ff665e3a6c42f92823121e3a6ea7718" - integrity sha512-47Frx13aZh01afHJTB3zTtKIlFI6vWY+MYCN9Qpew6i52rfKjnhCF/l1YlC8UmEMvvntZZ6z4PiCcmyuedR2aQ== +gensequence@^8.0.8: + version "8.0.8" + resolved "https://registry.yarnpkg.com/gensequence/-/gensequence-8.0.8.tgz#381a46bef4b1c26f6aff2b291ce9cd417d363fb1" + integrity sha512-omMVniXEXpdx/vKxGnPRoO2394Otlze28TyxECbFVyoSpZ9H3EO7lemjcB12OpQJzRW4e5tt/dL1rOxry6aMHg== get-caller-file@^2.0.1: version "2.0.5" @@ -4440,13 +4208,6 @@ get-symbol-description@^1.1.0: es-errors "^1.3.0" get-intrinsic "^1.2.6" -get-tsconfig@^4.7.5: - version "4.10.0" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.10.0.tgz#403a682b373a823612475a4c2928c7326fc0f6bb" - integrity sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A== - dependencies: - resolve-pkg-maps "^1.0.0" - get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -4566,12 +4327,12 @@ glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -global-directory@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/global-directory/-/global-directory-4.0.1.tgz#4d7ac7cfd2cb73f304c53b8810891748df5e361e" - integrity sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q== +global-directory@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/global-directory/-/global-directory-5.0.0.tgz#0f66a94212acd0f81ee838d0a991e88d1c2836cf" + integrity sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w== dependencies: - ini "4.1.1" + ini "6.0.0" globals@^13.19.0: version "13.24.0" @@ -4600,17 +4361,6 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -globby@^13.1.4: - version "13.2.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592" - integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w== - dependencies: - dir-glob "^3.0.1" - fast-glob "^3.3.0" - ignore "^5.2.4" - merge2 "^1.4.1" - slash "^4.0.0" - globby@^9.2.0: version "9.2.0" resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" @@ -4635,11 +4385,6 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== -graceful-fs@^4.2.10: - version "4.2.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" - integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== - graphemer@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" @@ -4690,11 +4435,6 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-own-prop@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-own-prop/-/has-own-prop-2.0.0.tgz#f0f95d58f65804f5d218db32563bb85b8e0417af" - integrity sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ== - has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" @@ -4857,7 +4597,7 @@ ignore@^4.0.3: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.2.0, ignore@^5.2.4, ignore@^5.3.1: +ignore@^5.2.0, ignore@^5.3.1: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== @@ -4878,13 +4618,10 @@ import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" -import-fresh@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" - integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" +import-fresh@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-4.0.0.tgz#07057d6f3b6d9bf19b8f287c8d73b43da5f9f289" + integrity sha512-Fpi660c7VPDM3fPKYovStd9IP1CPOikf6v/dGxJJMmHPcwYQIMJ4W7kO1avBYEpMqkCh+Dx3Ln6H7VYqgztLjw== import-local@^2.0.0: version "2.0.0" @@ -4894,10 +4631,10 @@ import-local@^2.0.0: pkg-dir "^3.0.0" resolve-cwd "^2.0.0" -import-meta-resolve@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz#f9db8bead9fafa61adb811db77a2bf22c5399706" - integrity sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw== +import-meta-resolve@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz#08cb85b5bd37ecc8eb1e0f670dc2767002d43734" + integrity sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg== imurmurhash@^0.1.4: version "0.1.4" @@ -4921,11 +4658,6 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -indent-string@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-5.0.0.tgz#4fd2980fccaf8622d14c64d694f4cf33c81951a5" - integrity sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg== - infer-owner@^1.0.3, infer-owner@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" @@ -4944,10 +4676,10 @@ inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ini/-/ini-4.1.1.tgz#d95b3d843b1e906e56d6747d5447904ff50ce7a1" - integrity sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g== +ini@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-6.0.0.tgz#efc7642b276f6a37d22fdf56ef50889d7146bf30" + integrity sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ== ini@^1.3.2, ini@^1.3.4: version "1.3.5" @@ -5281,6 +5013,11 @@ is-regex@^1.2.1: has-tostringtag "^1.0.2" hasown "^2.0.2" +is-safe-filename@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-safe-filename/-/is-safe-filename-0.1.1.tgz#fb22eead097c614c47aa674de5d79a1648a53e66" + integrity sha512-4SrR7AdnY11LHfDKTZY1u6Ga3RuxZdl3YKWWShO5iyuG5h8QS4GD2tOb04peBJ5I7pXbR+CGBNEhTcwK+FzN3g== + is-set@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" @@ -5417,15 +5154,6 @@ istanbul-lib-report@^3.0.0, istanbul-lib-report@^3.0.1: make-dir "^4.0.0" supports-color "^7.1.0" -istanbul-lib-source-maps@^5.0.6: - version "5.0.6" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz#acaef948df7747c8eb5fbf1265cb980f6353a441" - integrity sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A== - dependencies: - "@jridgewell/trace-mapping" "^0.3.23" - debug "^4.1.1" - istanbul-lib-coverage "^3.0.0" - istanbul-reports@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.2.0.tgz#cb4535162b5784aa623cee21a7252cf2c807ac93" @@ -5434,16 +5162,16 @@ istanbul-reports@^3.2.0: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +js-tokens@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-10.0.0.tgz#dffe7599b4a8bb7fe30aff8d0235234dffb79831" + integrity sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q== + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-tokens@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-9.0.1.tgz#2ec43964658435296f6761b34e10671c2d9527f4" - integrity sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ== - js-yaml@^3.13.1: version "3.14.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" @@ -5528,11 +5256,6 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -junk@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/junk/-/junk-4.0.1.tgz#7ee31f876388c05177fe36529ee714b07b50fbed" - integrity sha512-Qush0uP+G8ZScpGMZvHUiRfI0YBWuB3gVBYlI0v0vvOJt5FLicco+IkP0a50LqTTQhmts/m6tP5SWE+USyIvcQ== - keyv@^4.5.3: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" @@ -5596,6 +5319,80 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +lightningcss-android-arm64@1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz#9a6841f88ae50fc83502903892b41af41bc2b907" + integrity sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg== + +lightningcss-darwin-arm64@1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz#c0f2c31c0bfd19fa4dd3f18e957a1f1a152097d6" + integrity sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg== + +lightningcss-darwin-x64@1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz#cb0705965acb538c6683949ce6925fb3cdf7c361" + integrity sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ== + +lightningcss-freebsd-x64@1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz#763538828b26bab2680dadafcc84ee78b0eb502b" + integrity sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg== + +lightningcss-linux-arm-gnueabihf@1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz#6862e3176a331aedbdec1ed352b4d7d0dd0784de" + integrity sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ== + +lightningcss-linux-arm64-gnu@1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz#c6a3a2ed15141daf6bdc2628930f8e39bdf473aa" + integrity sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg== + +lightningcss-linux-arm64-musl@1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz#7fa1334971fc82845f9827df6ef8a0b20914bac6" + integrity sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ== + +lightningcss-linux-x64-gnu@1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz#8b927862ea8c2bbc6831a46509244b50d9936e55" + integrity sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg== + +lightningcss-linux-x64-musl@1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz#0c525bb077dfd94404c059cfe42dad797e96aeaf" + integrity sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw== + +lightningcss-win32-arm64-msvc@1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz#850ee1103dac989cfab50e3ac22d1a69e394e63d" + integrity sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA== + +lightningcss-win32-x64-msvc@1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz#e343ae152eed3609dc6e11949d1a3bf39a1c946f" + integrity sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA== + +lightningcss@^1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.33.0.tgz#c08867d71a79385c6e190214fd72fef3e5f95f0b" + integrity sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA== + dependencies: + detect-libc "^2.0.3" + optionalDependencies: + lightningcss-android-arm64 "1.33.0" + lightningcss-darwin-arm64 "1.33.0" + lightningcss-darwin-x64 "1.33.0" + lightningcss-freebsd-x64 "1.33.0" + lightningcss-linux-arm-gnueabihf "1.33.0" + lightningcss-linux-arm64-gnu "1.33.0" + lightningcss-linux-arm64-musl "1.33.0" + lightningcss-linux-x64-gnu "1.33.0" + lightningcss-linux-x64-musl "1.33.0" + lightningcss-win32-arm64-msvc "1.33.0" + lightningcss-win32-x64-msvc "1.33.0" + lines-and-columns@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" @@ -5755,13 +5552,13 @@ magic-string@^0.30.21: dependencies: "@jridgewell/sourcemap-codec" "^1.5.5" -magicast@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/magicast/-/magicast-0.5.1.tgz#518959aea78851cd35d4bb0da92f780db3f606d3" - integrity sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw== +magicast@^0.5.2: + version "0.5.4" + resolved "https://registry.yarnpkg.com/magicast/-/magicast-0.5.4.tgz#bbe38dfd6037670057f33abf22b8aa79d5e99d0a" + integrity sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w== dependencies: - "@babel/parser" "^7.28.5" - "@babel/types" "^7.28.5" + "@babel/parser" "^7.29.7" + "@babel/types" "^7.29.7" source-map-js "^1.2.1" make-dir@^1.0.0: @@ -5835,11 +5632,6 @@ math-intrinsics@^1.1.0: resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== -meow@^12.0.1: - version "12.1.1" - resolved "https://registry.yarnpkg.com/meow/-/meow-12.1.1.tgz#e558dddbab12477b69b2e9a2728c327f191bace6" - integrity sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw== - meow@^3.3.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" @@ -5912,7 +5704,7 @@ micromatch@^3.1.10: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.4, micromatch@^4.0.5, micromatch@^4.0.8: +micromatch@^4.0.4: version "4.0.8" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== @@ -6102,10 +5894,10 @@ mz@^2.5.0: object-assign "^4.0.1" thenify-all "^1.0.0" -nanoid@^3.3.11: - version "3.3.11" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" - integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== +nanoid@^3.3.17: + version "3.3.18" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.18.tgz#f66a2de1199ffde0fcf21c8a5f13106b1c081913" + integrity sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w== nanomatch@^1.2.9: version "1.2.13" @@ -6134,11 +5926,6 @@ neo-async@^2.6.0: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -nested-error-stacks@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz#26c8a3cee6cc05fbcf1e333cd2fc3e003326c0b5" - integrity sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw== - nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -6471,20 +6258,6 @@ own-keys@^1.0.1: object-keys "^1.1.1" safe-push-apply "^1.0.0" -p-event@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/p-event/-/p-event-5.0.1.tgz#614624ec02ae7f4f13d09a721c90586184af5b0c" - integrity sha512-dd589iCQ7m1L0bmC5NLlVYfy3TbBEsMUfWx9PyAgPeIcFZ/E2yaTZ4Rz4MiBmmJShviiftHVXOqfnfzJ6kyMrQ== - dependencies: - p-timeout "^5.0.2" - -p-filter@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-filter/-/p-filter-3.0.0.tgz#ce50e03b24b23930e11679ab8694bd09a2d7ed35" - integrity sha512-QtoWLjXAW++uTX67HZQz1dbTpqBfiidsB6VtQUC9iR85S120+s0T5sO6s+B5MLzFcZkrEd/DGMmCjR+f2Qpxwg== - dependencies: - p-map "^5.1.0" - p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -6551,18 +6324,6 @@ p-map@^2.1.0: resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== -p-map@^5.1.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-5.5.0.tgz#054ca8ca778dfa4cf3f8db6638ccb5b937266715" - integrity sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg== - dependencies: - aggregate-error "^4.0.0" - -p-map@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-6.0.0.tgz#4d9c40d3171632f86c47601b709f4b4acd70fed4" - integrity sha512-T8BatKGY+k5rU+Q/GTYgrEf2r4xRMevAN5mtXc2aPc4rS1j3s+vWTaO2Wag94neXuCAUAs8cxBL9EeB5EA6diw== - p-pipe@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-1.2.0.tgz#4b1a11399a11520a67790ee5a0c1d5881d6befe9" @@ -6595,11 +6356,6 @@ p-retry@^8.0.0: dependencies: is-network-error "^1.3.0" -p-timeout@^5.0.2: - version "5.1.0" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-5.1.0.tgz#b3c691cf4415138ce2d9cfe071dba11f0fee085b" - integrity sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew== - p-timeout@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-7.0.1.tgz#95680a6aa693c530f14ac337b8bd32d4ec6ae4f0" @@ -6638,13 +6394,6 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parent-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-2.0.0.tgz#fa71f88ff1a50c27e15d8ff74e0e3a9523bf8708" - integrity sha512-uo0Z9JJeWzv8BG+tRcapBKNJ0dro9cLyczGzulS6EfeyAdeC9sbojtW6XwvYxJkEne9En+J2XEl4zyglVeIwFg== - dependencies: - callsites "^3.1.0" - parse-github-repo-url@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz#9e7d8bb252a6cb6ba42595060b7bf6df3dbc1f50" @@ -6786,16 +6535,16 @@ picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -picomatch@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" - integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== - picomatch@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== +picomatch@^4.0.4, picomatch@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.5.tgz#51ea57a17d86f605f81039595fbc40ed06a55fab" + integrity sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A== + pify@^2.0.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -6840,12 +6589,12 @@ possible-typed-array-names@^1.0.0: resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== -postcss@^8.5.6: - version "8.5.6" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c" - integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== +postcss@^8.5.25: + version "8.5.26" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.26.tgz#6e75135780c7e10df3433bf2266c552d35c8c620" + integrity sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ== dependencies: - nanoid "^3.3.11" + nanoid "^3.3.17" picocolors "^1.1.1" source-map-js "^1.2.1" @@ -7212,11 +6961,6 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve-pkg-maps@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" - integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== - resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -7275,34 +7019,28 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" -rollup@^4.43.0: - version "4.48.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.48.1.tgz#acd64b7e3f8734728c5daedd5db42f4a8ea57858" - integrity sha512-jVG20NvbhTYDkGAty2/Yh7HK6/q3DGSRH4o8ALKGArmMuaauM9kLfoMZ+WliPwA5+JHr2lTn3g557FxBV87ifg== +rolldown@~1.2.1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/rolldown/-/rolldown-1.2.3.tgz#103bdcbbd575d51265277b8b510f080827b6eb6f" + integrity sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A== dependencies: - "@types/estree" "1.0.8" + "@oxc-project/types" "=0.143.0" + "@rolldown/pluginutils" "^1.0.0" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.48.1" - "@rollup/rollup-android-arm64" "4.48.1" - "@rollup/rollup-darwin-arm64" "4.48.1" - "@rollup/rollup-darwin-x64" "4.48.1" - "@rollup/rollup-freebsd-arm64" "4.48.1" - "@rollup/rollup-freebsd-x64" "4.48.1" - "@rollup/rollup-linux-arm-gnueabihf" "4.48.1" - "@rollup/rollup-linux-arm-musleabihf" "4.48.1" - "@rollup/rollup-linux-arm64-gnu" "4.48.1" - "@rollup/rollup-linux-arm64-musl" "4.48.1" - "@rollup/rollup-linux-loongarch64-gnu" "4.48.1" - "@rollup/rollup-linux-ppc64-gnu" "4.48.1" - "@rollup/rollup-linux-riscv64-gnu" "4.48.1" - "@rollup/rollup-linux-riscv64-musl" "4.48.1" - "@rollup/rollup-linux-s390x-gnu" "4.48.1" - "@rollup/rollup-linux-x64-gnu" "4.48.1" - "@rollup/rollup-linux-x64-musl" "4.48.1" - "@rollup/rollup-win32-arm64-msvc" "4.48.1" - "@rollup/rollup-win32-ia32-msvc" "4.48.1" - "@rollup/rollup-win32-x64-msvc" "4.48.1" - fsevents "~2.3.2" + "@rolldown/binding-android-arm64" "1.2.3" + "@rolldown/binding-darwin-arm64" "1.2.3" + "@rolldown/binding-darwin-x64" "1.2.3" + "@rolldown/binding-freebsd-x64" "1.2.3" + "@rolldown/binding-linux-arm-gnueabihf" "1.2.3" + "@rolldown/binding-linux-arm64-gnu" "1.2.3" + "@rolldown/binding-linux-arm64-musl" "1.2.3" + "@rolldown/binding-linux-ppc64-gnu" "1.2.3" + "@rolldown/binding-linux-s390x-gnu" "1.2.3" + "@rolldown/binding-linux-x64-gnu" "1.2.3" + "@rolldown/binding-linux-x64-musl" "1.2.3" + "@rolldown/binding-openharmony-arm64" "1.2.3" + "@rolldown/binding-win32-arm64-msvc" "1.2.3" + "@rolldown/binding-win32-x64-msvc" "1.2.3" run-async@^2.2.0: version "2.4.1" @@ -7405,10 +7143,10 @@ semver@^7.6.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== -semver@^7.7.2: - version "7.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" - integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== +semver@^7.8.1, semver@^7.8.5: + version "7.8.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.5.tgz#39b646037dd50c14fb451e7e4cac58ed8b863f69" + integrity sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA== set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" @@ -7547,11 +7285,6 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slash@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" - integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== - slide@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" @@ -7562,10 +7295,10 @@ smart-buffer@^4.1.0: resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" integrity sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw== -smol-toml@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/smol-toml/-/smol-toml-1.4.1.tgz#f67dff9e1d4ba344242aaf9864062543536b1f72" - integrity sha512-CxdwHXyYTONGHThDbq5XdwbFsuY4wlClRGejfE2NtwUtiHYsP1QtNsHb/hnj31jKYSchztJsaA8pSQoVzkfCFg== +smol-toml@^1.6.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/smol-toml/-/smol-toml-1.7.1.tgz#ba3e28d2e4348874a824fd8e1d73fec618cb763b" + integrity sha512-PPlsspAZ4jbMBu5DMFhfUGDQLu/vrL4SyBROVS37x8ynnVmFIs1VPBz1Co8Xks3TvpIaZXmU85y4DrQ+UyVFoQ== snapdragon-node@^2.0.1: version "2.1.1" @@ -7738,10 +7471,10 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -std-env@^3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.10.0.tgz#d810b27e3a073047b2b5e40034881f5ea6f9c83b" - integrity sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg== +std-env@^4.0.0-rc.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-4.2.0.tgz#8ebe0ec60485668ab47227b312f4254cdf80c9d3" + integrity sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw== stream-each@^1.1.0: version "1.2.3" @@ -8026,14 +7759,6 @@ tinyexec@^1.0.2: resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-1.0.2.tgz#bdd2737fe2ba40bd6f918ae26642f264b99ca251" integrity sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg== -tinyglobby@^0.2.14: - version "0.2.14" - resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.14.tgz#5280b0cf3f972b050e74ae88406c0a6a58f4079d" - integrity sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ== - dependencies: - fdir "^6.4.4" - picomatch "^4.0.2" - tinyglobby@^0.2.15: version "0.2.15" resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" @@ -8042,10 +7767,18 @@ tinyglobby@^0.2.15: fdir "^6.5.0" picomatch "^4.0.3" -tinyrainbow@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-3.0.3.tgz#984a5b1c1b25854a9b6bccbe77964d0593d1ea42" - integrity sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q== +tinyglobby@^0.2.16, tinyglobby@^0.2.17: + version "0.2.17" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.17.tgz#562a9a6c9eb2b3b123d39719f9af5bb44fcd7631" + integrity sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g== + dependencies: + fdir "^6.5.0" + picomatch "^4.0.4" + +tinyrainbow@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-3.1.1.tgz#c0168387d3d8d70b6b3c2c0936de5fee738cea20" + integrity sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw== tmp@^0.0.33: version "0.0.33" @@ -8146,13 +7879,12 @@ tslib@^2.4.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== -tsx@^4.20.3: - version "4.20.3" - resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.20.3.tgz#f913e4911d59ad177c1bcee19d1035ef8dd6e2fb" - integrity sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ== +tsx@^4.23.12: + version "4.23.12" + resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.23.12.tgz#3a4919591cd9b9e00011b75e596c8ab8db23c09c" + integrity sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q== dependencies: - esbuild "~0.25.0" - get-tsconfig "^4.7.5" + esbuild "~0.28.0" optionalDependencies: fsevents "~2.3.3" @@ -8285,10 +8017,10 @@ undici-types@~6.20.0: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== -undici-types@~6.21.0: - version "6.21.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" - integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== +undici-types@~7.18.0: + version "7.18.2" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.18.2.tgz#29357a89e7b7ca4aef3bf0fd3fd0cd73884229e9" + integrity sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w== union-value@^1.0.0: version "1.0.1" @@ -8402,44 +8134,43 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -"vite@^6.0.0 || ^7.0.0": - version "7.2.7" - resolved "https://registry.yarnpkg.com/vite/-/vite-7.2.7.tgz#0789a4c3206081699f34a9ecca2dda594a07478e" - integrity sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ== +"vite@^6.0.0 || ^7.0.0 || ^8.0.0": + version "8.2.1" + resolved "https://registry.yarnpkg.com/vite/-/vite-8.2.1.tgz#6fc8d8bb843bd52353091fac978e194d4de5b31d" + integrity sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw== dependencies: - esbuild "^0.25.0" - fdir "^6.5.0" - picomatch "^4.0.3" - postcss "^8.5.6" - rollup "^4.43.0" - tinyglobby "^0.2.15" + lightningcss "^1.33.0" + picomatch "^4.0.5" + postcss "^8.5.25" + rolldown "~1.2.1" + tinyglobby "^0.2.17" optionalDependencies: fsevents "~2.3.3" -vitest@^4.0.15: - version "4.0.15" - resolved "https://registry.yarnpkg.com/vitest/-/vitest-4.0.15.tgz#bb65e8289d49c89bc3c1dba8e1bf9c13f039c6b0" - integrity sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA== - dependencies: - "@vitest/expect" "4.0.15" - "@vitest/mocker" "4.0.15" - "@vitest/pretty-format" "4.0.15" - "@vitest/runner" "4.0.15" - "@vitest/snapshot" "4.0.15" - "@vitest/spy" "4.0.15" - "@vitest/utils" "4.0.15" - es-module-lexer "^1.7.0" - expect-type "^1.2.2" +vitest@^4.1.10: + version "4.1.10" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-4.1.10.tgz#7e9285efe264b1167050b7a3a7ff34788e1b7afc" + integrity sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw== + dependencies: + "@vitest/expect" "4.1.10" + "@vitest/mocker" "4.1.10" + "@vitest/pretty-format" "4.1.10" + "@vitest/runner" "4.1.10" + "@vitest/snapshot" "4.1.10" + "@vitest/spy" "4.1.10" + "@vitest/utils" "4.1.10" + es-module-lexer "^2.0.0" + expect-type "^1.3.0" magic-string "^0.30.21" obug "^2.1.1" pathe "^2.0.3" picomatch "^4.0.3" - std-env "^3.10.0" + std-env "^4.0.0-rc.1" tinybench "^2.9.0" tinyexec "^1.0.2" tinyglobby "^0.2.15" - tinyrainbow "^3.0.3" - vite "^6.0.0 || ^7.0.0" + tinyrainbow "^3.1.0" + vite "^6.0.0 || ^7.0.0 || ^8.0.0" why-is-node-running "^2.3.0" vscode-languageserver-textdocument@^1.0.12: @@ -8631,10 +8362,10 @@ write-pkg@^3.1.0: sort-keys "^2.0.0" write-json-file "^2.2.0" -ws@^8.13.0: - version "8.18.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.1.tgz#ea131d3784e1dfdff91adb0a4a116b127515e3cb" - integrity sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w== +ws@^8.13.0, ws@^8.21.3: + version "8.21.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.21.3.tgz#660b4faddb6a3e575c86e078126919961f4de4fc" + integrity sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw== xdg-basedir@^5.1.0: version "5.1.0" @@ -8656,10 +8387,10 @@ yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yaml@^2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.0.tgz#15f8c9866211bdc2d3781a0890e44d4fa1a5fff6" - integrity sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ== +yaml@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.9.0.tgz#78274afd93598a1dfdd6130df6a566defcbf9aa4" + integrity sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA== yargs-parser@^15.0.1: version "15.0.1"