scrypt-ts
Advanced tools
Comparing version 0.1.0-alpha to 0.1.1-alpha
import { PubKey, Sig, PubKeyHash, SigHashType, OpCodeType, SigHashPreimage, PrivKey } from "./types"; | ||
/** | ||
* bigint can be converted to string with pack | ||
* @category Bytes Operations | ||
*/ | ||
export declare function pack(n: bigint): string; | ||
/** | ||
* string can be converted to bigint using function unpack. | ||
* @category Bytes Operations | ||
*/ | ||
export declare function unpack(a: string): bigint; | ||
export declare function num2bin(a: bigint, c: bigint): string; | ||
/** | ||
* Converts a number num into a string array of certain size size, including the sign bit. It fails if the number cannot be accommodated. | ||
* @category Bytes Operations | ||
* @param num - a number being converts | ||
* @param size - the size of the result string array | ||
* @returns {string} A string . | ||
*/ | ||
export declare function num2bin(num: bigint, size: bigint): string; | ||
/** | ||
* Returns the length of the string bytes. Not the length of the string. | ||
* @category Bytes Operations | ||
* @param a - a string | ||
* @returns {string} The length of the string bytes. | ||
*/ | ||
export declare function len(a: string): number; | ||
export declare function reverseBytes(a: string, c: number): string; | ||
export declare function checkSig(a: Sig, b: PubKey): boolean; | ||
export declare function checkMultiSig(a: Sig[], b: PubKey[]): boolean; | ||
export declare function exit(a: boolean): void; | ||
/** | ||
* Returns reversed bytes of b, which is of size bytes. Note size must be a compile time constant. It is often useful when converting a number between little-endian and big-endian. | ||
* @category Bytes Operations | ||
* @param b - a string to be reversed | ||
* @param size - the size of the string bytes. | ||
* @returns {string} The length of the string bytes. | ||
*/ | ||
export declare function reverseBytes(b: string, size: number): string; | ||
/** | ||
* verifies an ECDSA signature. It takes two inputs from the stack, a public key (on top of the stack) and an ECDSA signature in its DER_CANONISED format concatenated with sighash flags. It outputs true or false on the stack based on whether the signature check passes or fails. | ||
* @category Signature Verification | ||
* @see https://wiki.bitcoinsv.io/index.php/Opcodes_used_in_Bitcoin_Script | ||
*/ | ||
export declare function checkSig(signature: Sig, publickey: PubKey): boolean; | ||
/** | ||
* Compares the first signature against each public key until it finds an ECDSA match. Starting with the subsequent public key, it compares the second signature against each remaining public key until it finds an ECDSA match. The process is repeated until all signatures have been checked or not enough public keys remain to produce a successful result. All signatures need to match a public key. Because public keys are not checked again if they fail any signature comparison, signatures must be placed in the scriptSig using the same order as their corresponding public keys were placed in the scriptPubKey or redeemScript. If all signatures are valid, 1 is returned, 0 otherwise. Due to a bug, one extra unused value is removed from the stack. | ||
* @category Signature Verification | ||
* @see https://wiki.bitcoinsv.io/index.php/Opcodes_used_in_Bitcoin_Script | ||
*/ | ||
export declare function checkMultiSig(signatures: Sig[], publickeys: PubKey[]): boolean; | ||
/** | ||
* `exit(bool status)`; statement terminates contract execution. | ||
* @category exit() | ||
* @param status - If status is true, contract succeeds; otherwise, it fails. | ||
* | ||
*/ | ||
export declare function exit(status: boolean): void; | ||
/** | ||
* The input `a` is made positive. | ||
* @category Math | ||
*/ | ||
export declare function abs(a: bigint): bigint; | ||
/** | ||
* Returns the smaller of `a` and `b`. | ||
* @category Math | ||
*/ | ||
export declare function min(a: bigint, b: bigint): bigint; | ||
/** | ||
* Returns the larger of `a` and `b`. | ||
* @category Math | ||
*/ | ||
export declare function max(a: bigint, b: bigint): bigint; | ||
/** | ||
* Returns true if `x` is within the specified range (left-inclusive), false otherwise. | ||
* @category Math | ||
*/ | ||
export declare function within(x: bigint, min: bigint, max: bigint): boolean; | ||
/** | ||
* A RIPEMD160 hash, which is always 160 bits or 20 bytes long. | ||
* See: | ||
* https://en.wikipedia.org/wiki/RIPEMD | ||
* @category Hashing | ||
* @param {string} string Data, a.k.a. pre-image, which can be any size. | ||
* @returns {string} The hash in the form of a string. | ||
*/ | ||
export declare function ripemd160(a: string): string; | ||
/** | ||
* A SHA or SHA1 hash, which is always 160 bits or 20 bytes long. | ||
* | ||
* See: | ||
* https://en.wikipedia.org/wiki/SHA-1 | ||
* @category Hashing | ||
* @param {string} Data, a.k.a. pre-image, which can be any size. | ||
* @returns {string} The hash in the form of a string. | ||
*/ | ||
export declare function sha1(a: string): string; | ||
/** | ||
* A SHA256 hash, which is always 256 bits or 32 bytes long. | ||
* | ||
* See: | ||
* https://www.movable-type.co.uk/scripts/sha256.html | ||
* @category Hashing | ||
* @param {string} Data, a.k.a. pre-image, which can be any size. | ||
* @returns {string} The hash in the form of a string. | ||
*/ | ||
export declare function sha256(a: string): string; | ||
/** | ||
* A RIPEMD160 hash of a SHA256 hash, which is always 160 bits or 20 bytes long. | ||
* This value is commonly used inside Bitcoin, particularly for Bitcoin | ||
* addresses. | ||
* | ||
* See: | ||
* https://en.wikipedia.org/wiki/RIPEMD | ||
* @category Hashing | ||
* @param {string} Data, a.k.a. pre-image, which can be any size. | ||
* @returns {string} The hash in the form of a string. | ||
*/ | ||
export declare function hash160(a: string): string; | ||
/** | ||
* A double SHA256 hash, which is always 256 bits or 32 bytes bytes long. This | ||
* hash function is commonly used inside Bitcoin, particularly for the hash of a | ||
* block and the hash of a transaction. | ||
* | ||
* See: | ||
* https://www.movable-type.co.uk/scripts/sha256.html | ||
* @category Hashing | ||
* @param {string} data, a.k.a. pre-image, which can be any size. | ||
* @returns {string} The hash in the form of a string. | ||
*/ | ||
export declare function hash256(a: string): string; | ||
/** | ||
* @category Hashing | ||
* @ignore | ||
*/ | ||
export declare function flattenSha256(a: any): string; | ||
/** | ||
* @category assert | ||
*/ | ||
export declare function assert(cond: boolean): void; | ||
export declare function loop(times: bigint): (fn: (i: number) => void) => void; | ||
/** | ||
* Bitcoin script does not provide looping constructs natively for security reasons. sCrypt achieves looping by repeating the loop body maxLoopCount times. For example, the loop | ||
* @example | ||
* ```ts | ||
* loop (5n) (() => { | ||
* x = x * 2; | ||
* }) | ||
* ``` | ||
* is equivalently unrolled to | ||
* @example | ||
* ```ts | ||
* x = x * 2; | ||
* x = x * 2; | ||
* x = x * 2; | ||
* x = x * 2; | ||
* x = x * 2; | ||
* x = x * 2; | ||
* x = x * 2; | ||
* x = x * 2; | ||
* x = x * 2; | ||
* x = x * 2; | ||
* ``` | ||
* If `maxLoopCount` is set too small, the contract may not work correctly. If `maxLoopCount` is set too large, the resulting script is bloated unnecessarily and costs more to execute. There are a number of ways to choose the right `maxLoopCount` judiciously. One way is to simulate the contract off chain and find the number of loops. Another way is to exploit the characteristics of the looping itself. For example, if a loop iterates over each bit of a sha256 hash, `maxLoopCount` is 256. | ||
* ## Induction variable | ||
* Induction variable can be defined when loop index is needed. | ||
* @example | ||
* ```ts | ||
* loop (5n) ((i: number) => { | ||
* // i is the outer loop index | ||
* loop (5n) ((k: number) => { | ||
* // j is the inner loop index | ||
* a += BigInt(i + k); | ||
* }) | ||
* }) | ||
* ``` | ||
* @category loop | ||
*/ | ||
export declare function loop(maxLoopCount: bigint): (fn: (i: number) => void) => void; | ||
/** | ||
* @ignore | ||
*/ | ||
export declare function asm<ReturnType>(code: any): ReturnType; | ||
/** | ||
* @category Standard Contracts | ||
*/ | ||
export declare class OpCode { | ||
@@ -145,2 +303,5 @@ static readonly OP_0: OpCodeType; | ||
} | ||
/** | ||
* @category Standard Contracts | ||
*/ | ||
export declare class Utils { | ||
@@ -157,2 +318,5 @@ static readonly OutputValueLen: bigint; | ||
} | ||
/** | ||
* @category Standard Contracts | ||
*/ | ||
export declare class SigHash { | ||
@@ -162,5 +326,5 @@ static readonly ALL_FORKID: SigHashType; | ||
static readonly SINGLE_FORKID: SigHashType; | ||
static readonly ALL_ANYONECANPAY_FORKID: SigHashType; | ||
static readonly NONE_ANYONECANPAY_ORKID: SigHashType; | ||
static readonly SINGLE_ANYONECANPAY_FORKID: SigHashType; | ||
static readonly ANYONECANPAY_ALL_FORKID: SigHashType; | ||
static readonly ANYONECANPAY_NONE_FORKID: SigHashType; | ||
static readonly ANYONECANPAY_SINGLE_FORKID: SigHashType; | ||
static nVersion(preimage: SigHashPreimage): string; | ||
@@ -180,2 +344,5 @@ static hashPrevouts(preimage: SigHashPreimage): string; | ||
} | ||
/** | ||
* @category Standard Contracts | ||
*/ | ||
export declare class VarIntReader { | ||
@@ -192,2 +359,5 @@ static readonly StateLen: bigint; | ||
} | ||
/** | ||
* @category Standard Contracts | ||
*/ | ||
export declare class VarIntWriter { | ||
@@ -201,2 +371,3 @@ static writestring(buf: string): string; | ||
* library to access current tx | ||
* @category Standard Contracts | ||
*/ | ||
@@ -224,7 +395,5 @@ export declare class Tx { | ||
} | ||
export declare class StdShift { | ||
static pow2(n: bigint): bigint; | ||
static left(x: bigint, n: bigint): bigint; | ||
static right(x: bigint, n: bigint): bigint; | ||
} | ||
/** | ||
* @category Standard Contracts | ||
*/ | ||
export declare class Constants { | ||
@@ -231,0 +400,0 @@ static readonly InputSeqLen: bigint; |
"use strict"; | ||
// build-in function | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Constants = exports.StdShift = exports.Tx = exports.VarIntWriter = exports.VarIntReader = exports.SigHash = exports.Utils = exports.OpCode = exports.asm = exports.loop = exports.assert = exports.flattenSha256 = exports.hash256 = exports.hash160 = exports.sha256 = exports.sha1 = exports.ripemd160 = exports.within = exports.max = exports.min = exports.abs = exports.exit = exports.checkMultiSig = exports.checkSig = exports.reverseBytes = exports.len = exports.num2bin = exports.unpack = exports.pack = void 0; | ||
exports.Constants = exports.Tx = exports.VarIntWriter = exports.VarIntReader = exports.SigHash = exports.Utils = exports.OpCode = exports.asm = exports.loop = exports.assert = exports.flattenSha256 = exports.hash256 = exports.hash160 = exports.sha256 = exports.sha1 = exports.ripemd160 = exports.within = exports.max = exports.min = exports.abs = exports.exit = exports.checkMultiSig = exports.checkSig = exports.reverseBytes = exports.len = exports.num2bin = exports.unpack = exports.pack = void 0; | ||
const scryptlib_1 = require("scryptlib"); | ||
const utils_1 = require("scryptlib/dist/utils"); | ||
const types_1 = require("./types"); | ||
// https://scryptdoc.readthedocs.io/en/latest/functions.html#string-operations | ||
/** | ||
* bigint can be converted to string with pack | ||
* @category Bytes Operations | ||
*/ | ||
function pack(n) { | ||
@@ -14,2 +17,6 @@ const num = new scryptlib_1.bsv.crypto.BN(n); | ||
exports.pack = pack; | ||
/** | ||
* string can be converted to bigint using function unpack. | ||
* @category Bytes Operations | ||
*/ | ||
function unpack(a) { | ||
@@ -20,29 +27,66 @@ return BigInt((0, scryptlib_1.bin2num)(a)); | ||
; | ||
function num2bin(a, c) { return (0, scryptlib_1.num2bin)(a, Number(c)); } | ||
/** | ||
* Converts a number num into a string array of certain size size, including the sign bit. It fails if the number cannot be accommodated. | ||
* @category Bytes Operations | ||
* @param num - a number being converts | ||
* @param size - the size of the result string array | ||
* @returns {string} A string . | ||
*/ | ||
function num2bin(num, size) { | ||
return (0, scryptlib_1.num2bin)(num, Number(size)); | ||
} | ||
exports.num2bin = num2bin; | ||
; | ||
/** | ||
* Returns the length of the string bytes. Not the length of the string. | ||
* @category Bytes Operations | ||
* @param a - a string | ||
* @returns {string} The length of the string bytes. | ||
*/ | ||
function len(a) { return a.length / 2; } | ||
exports.len = len; | ||
; | ||
function reverseBytes(a, c) { | ||
let l = len(a); | ||
if (l != c) { | ||
throw new Error(`reversestring error, expected c = ${l}`); | ||
/** | ||
* Returns reversed bytes of b, which is of size bytes. Note size must be a compile time constant. It is often useful when converting a number between little-endian and big-endian. | ||
* @category Bytes Operations | ||
* @param b - a string to be reversed | ||
* @param size - the size of the string bytes. | ||
* @returns {string} The length of the string bytes. | ||
*/ | ||
function reverseBytes(b, size) { | ||
let l = len(b); | ||
if (l != size) { | ||
throw new Error(`reverseBytes error, expected c = ${l}`); | ||
} | ||
return a.match(/[a-fA-F0-9]{2}/g).reverse().join(''); | ||
return b.match(/[a-fA-F0-9]{2}/g).reverse().join(''); | ||
} | ||
exports.reverseBytes = reverseBytes; | ||
; | ||
// https://scryptdoc.readthedocs.io/en/latest/functions.html#signature-verification | ||
function checkSig(a, b) { return true; } | ||
/** | ||
* verifies an ECDSA signature. It takes two inputs from the stack, a public key (on top of the stack) and an ECDSA signature in its DER_CANONISED format concatenated with sighash flags. It outputs true or false on the stack based on whether the signature check passes or fails. | ||
* @category Signature Verification | ||
* @see https://wiki.bitcoinsv.io/index.php/Opcodes_used_in_Bitcoin_Script | ||
*/ | ||
function checkSig(signature, publickey) { return true; } | ||
exports.checkSig = checkSig; | ||
; | ||
function checkMultiSig(a, b) { return true; } | ||
/** | ||
* Compares the first signature against each public key until it finds an ECDSA match. Starting with the subsequent public key, it compares the second signature against each remaining public key until it finds an ECDSA match. The process is repeated until all signatures have been checked or not enough public keys remain to produce a successful result. All signatures need to match a public key. Because public keys are not checked again if they fail any signature comparison, signatures must be placed in the scriptSig using the same order as their corresponding public keys were placed in the scriptPubKey or redeemScript. If all signatures are valid, 1 is returned, 0 otherwise. Due to a bug, one extra unused value is removed from the stack. | ||
* @category Signature Verification | ||
* @see https://wiki.bitcoinsv.io/index.php/Opcodes_used_in_Bitcoin_Script | ||
*/ | ||
function checkMultiSig(signatures, publickeys) { return true; } | ||
exports.checkMultiSig = checkMultiSig; | ||
; | ||
// https://scryptdoc.readthedocs.io/en/latest/syntax.html#exit | ||
function exit(a) { } | ||
/** | ||
* `exit(bool status)`; statement terminates contract execution. | ||
* @category exit() | ||
* @param status - If status is true, contract succeeds; otherwise, it fails. | ||
* | ||
*/ | ||
function exit(status) { } | ||
exports.exit = exit; | ||
; | ||
// https://scryptdoc.readthedocs.io/en/latest/functions.html#math | ||
/** | ||
* The input `a` is made positive. | ||
* @category Math | ||
*/ | ||
function abs(a) { | ||
@@ -55,2 +99,6 @@ if (a < 0) { | ||
exports.abs = abs; | ||
/** | ||
* Returns the smaller of `a` and `b`. | ||
* @category Math | ||
*/ | ||
function min(a, b) { | ||
@@ -60,2 +108,6 @@ return (a - b < 0 ? a : b); | ||
exports.min = min; | ||
/** | ||
* Returns the larger of `a` and `b`. | ||
* @category Math | ||
*/ | ||
function max(a, b) { | ||
@@ -65,2 +117,6 @@ return (a - b > 0 ? a : b); | ||
exports.max = max; | ||
/** | ||
* Returns true if `x` is within the specified range (left-inclusive), false otherwise. | ||
* @category Math | ||
*/ | ||
function within(x, min, max) { | ||
@@ -70,3 +126,10 @@ return (min - x <= 0) && (x - max < 0); | ||
exports.within = within; | ||
// https://scryptdoc.readthedocs.io/en/latest/functions.html#hashing | ||
/** | ||
* A RIPEMD160 hash, which is always 160 bits or 20 bytes long. | ||
* See: | ||
* https://en.wikipedia.org/wiki/RIPEMD | ||
* @category Hashing | ||
* @param {string} string Data, a.k.a. pre-image, which can be any size. | ||
* @returns {string} The hash in the form of a string. | ||
*/ | ||
function ripemd160(a) { | ||
@@ -76,2 +139,11 @@ return scryptlib_1.bsv.crypto.Hash.ripemd160(Buffer.from(a, 'hex')).toString('hex'); | ||
exports.ripemd160 = ripemd160; | ||
/** | ||
* A SHA or SHA1 hash, which is always 160 bits or 20 bytes long. | ||
* | ||
* See: | ||
* https://en.wikipedia.org/wiki/SHA-1 | ||
* @category Hashing | ||
* @param {string} Data, a.k.a. pre-image, which can be any size. | ||
* @returns {string} The hash in the form of a string. | ||
*/ | ||
function sha1(a) { | ||
@@ -82,2 +154,11 @@ return scryptlib_1.bsv.crypto.Hash.sha1(Buffer.from(a, 'hex')).toString('hex'); | ||
; | ||
/** | ||
* A SHA256 hash, which is always 256 bits or 32 bytes long. | ||
* | ||
* See: | ||
* https://www.movable-type.co.uk/scripts/sha256.html | ||
* @category Hashing | ||
* @param {string} Data, a.k.a. pre-image, which can be any size. | ||
* @returns {string} The hash in the form of a string. | ||
*/ | ||
function sha256(a) { | ||
@@ -87,2 +168,13 @@ return scryptlib_1.bsv.crypto.Hash.sha256(Buffer.from(a, 'hex')).toString('hex'); | ||
exports.sha256 = sha256; | ||
/** | ||
* A RIPEMD160 hash of a SHA256 hash, which is always 160 bits or 20 bytes long. | ||
* This value is commonly used inside Bitcoin, particularly for Bitcoin | ||
* addresses. | ||
* | ||
* See: | ||
* https://en.wikipedia.org/wiki/RIPEMD | ||
* @category Hashing | ||
* @param {string} Data, a.k.a. pre-image, which can be any size. | ||
* @returns {string} The hash in the form of a string. | ||
*/ | ||
function hash160(a) { | ||
@@ -92,12 +184,29 @@ return scryptlib_1.bsv.crypto.Hash.sha256ripemd160(Buffer.from(a, 'hex')).toString('hex'); | ||
exports.hash160 = hash160; | ||
/** | ||
* A double SHA256 hash, which is always 256 bits or 32 bytes bytes long. This | ||
* hash function is commonly used inside Bitcoin, particularly for the hash of a | ||
* block and the hash of a transaction. | ||
* | ||
* See: | ||
* https://www.movable-type.co.uk/scripts/sha256.html | ||
* @category Hashing | ||
* @param {string} data, a.k.a. pre-image, which can be any size. | ||
* @returns {string} The hash in the form of a string. | ||
*/ | ||
function hash256(a) { return sha256(sha256(a)); } | ||
exports.hash256 = hash256; | ||
; | ||
/** | ||
* @category Hashing | ||
* @ignore | ||
*/ | ||
function flattenSha256(a) { throw new Error('unimplemented'); } | ||
exports.flattenSha256 = flattenSha256; | ||
; | ||
// others | ||
/** | ||
* @category assert | ||
*/ | ||
function assert(cond) { | ||
if (!cond) { | ||
throw new Error('Execution failed'); | ||
//throw new Error('Execution failed'); | ||
} | ||
@@ -107,6 +216,42 @@ } | ||
; | ||
// https://scryptdoc.readthedocs.io/en/latest/loop.html | ||
function loop(times) { | ||
/** | ||
* Bitcoin script does not provide looping constructs natively for security reasons. sCrypt achieves looping by repeating the loop body maxLoopCount times. For example, the loop | ||
* @example | ||
* ```ts | ||
* loop (5n) (() => { | ||
* x = x * 2; | ||
* }) | ||
* ``` | ||
* is equivalently unrolled to | ||
* @example | ||
* ```ts | ||
* x = x * 2; | ||
* x = x * 2; | ||
* x = x * 2; | ||
* x = x * 2; | ||
* x = x * 2; | ||
* x = x * 2; | ||
* x = x * 2; | ||
* x = x * 2; | ||
* x = x * 2; | ||
* x = x * 2; | ||
* ``` | ||
* If `maxLoopCount` is set too small, the contract may not work correctly. If `maxLoopCount` is set too large, the resulting script is bloated unnecessarily and costs more to execute. There are a number of ways to choose the right `maxLoopCount` judiciously. One way is to simulate the contract off chain and find the number of loops. Another way is to exploit the characteristics of the looping itself. For example, if a loop iterates over each bit of a sha256 hash, `maxLoopCount` is 256. | ||
* ## Induction variable | ||
* Induction variable can be defined when loop index is needed. | ||
* @example | ||
* ```ts | ||
* loop (5n) ((i: number) => { | ||
* // i is the outer loop index | ||
* loop (5n) ((k: number) => { | ||
* // j is the inner loop index | ||
* a += BigInt(i + k); | ||
* }) | ||
* }) | ||
* ``` | ||
* @category loop | ||
*/ | ||
function loop(maxLoopCount) { | ||
let loopFn = (fn) => { | ||
for (let i = 0; i < times; i++) { | ||
for (let i = 0; i < maxLoopCount; i++) { | ||
fn(i); | ||
@@ -119,3 +264,5 @@ } | ||
; | ||
// https://scryptdoc.readthedocs.io/en/latest/asm.html | ||
/** | ||
* @ignore | ||
*/ | ||
function asm(code) { | ||
@@ -126,5 +273,11 @@ throw new Error('unimplemented'); | ||
; | ||
/** | ||
* @category Standard Contracts | ||
*/ | ||
class OpCode { | ||
} | ||
exports.OpCode = OpCode; | ||
/** | ||
* @category Standard Contracts | ||
*/ | ||
class Utils { | ||
@@ -158,2 +311,5 @@ // convert signed integer `n` to unsigned integer of `l` string, in little endian | ||
Utils.PubKeyHashLen = 1n; | ||
/** | ||
* @category Standard Contracts | ||
*/ | ||
class SigHash { | ||
@@ -183,2 +339,11 @@ /* | ||
exports.SigHash = SigHash; | ||
SigHash.ALL_FORKID = new types_1.SigHashType(scryptlib_1.SigHash.ALL_FORKID); | ||
SigHash.NONE_FORKID = new types_1.SigHashType(scryptlib_1.SigHash.NONE_FORKID); | ||
SigHash.SINGLE_FORKID = new types_1.SigHashType(scryptlib_1.SigHash.SINGLE_FORKID); | ||
SigHash.ANYONECANPAY_ALL_FORKID = new types_1.SigHashType(scryptlib_1.SigHash.ANYONECANPAY_ALL_FORKID); | ||
SigHash.ANYONECANPAY_NONE_FORKID = new types_1.SigHashType(scryptlib_1.SigHash.ANYONECANPAY_NONE_FORKID); | ||
SigHash.ANYONECANPAY_SINGLE_FORKID = new types_1.SigHashType(scryptlib_1.SigHash.ANYONECANPAY_SINGLE_FORKID); | ||
/** | ||
* @category Standard Contracts | ||
*/ | ||
class VarIntReader { | ||
@@ -194,2 +359,5 @@ constructor(buf) { | ||
exports.VarIntReader = VarIntReader; | ||
/** | ||
* @category Standard Contracts | ||
*/ | ||
class VarIntWriter { | ||
@@ -207,2 +375,3 @@ // return VarInt encoding | ||
* library to access current tx | ||
* @category Standard Contracts | ||
*/ | ||
@@ -233,11 +402,5 @@ class Tx { | ||
exports.Tx = Tx; | ||
class StdShift { | ||
// return 2^n | ||
static pow2(n) { throw new Error('unimplemented'); } | ||
// binary left shift number x by n places | ||
static left(x, n) { throw new Error('unimplemented'); } | ||
// binary right shift number x by n places | ||
static right(x, n) { throw new Error('unimplemented'); } | ||
} | ||
exports.StdShift = StdShift; | ||
/** | ||
* @category Standard Contracts | ||
*/ | ||
class Constants { | ||
@@ -244,0 +407,0 @@ } |
@@ -1,5 +0,31 @@ | ||
export { PubKey, Sig, SigHashPreimage, Ripemd160, PubKeyHash, Sha256, Sha1, SigHashType, OpCodeType, PrivKey, Bytes, Int, Bool } from "scryptlib"; | ||
import { PubKey as PubKey_, Sig as Sig_, SigHashPreimage as SigHashPreimage_, Ripemd160 as Ripemd160_, PubKeyHash as PubKeyHash_, Sha256 as Sha256_, Sha1 as Sha1_, SigHashType as SigHashType_, OpCodeType as OpCodeType_, PrivKey as PrivKey_ } from "scryptlib"; | ||
/** | ||
* @ignore | ||
*/ | ||
declare type HexChar = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F'; | ||
/** | ||
* @ignore | ||
*/ | ||
declare type HexByte = `${HexChar}${HexChar}`; | ||
/** | ||
* @ignore | ||
*/ | ||
declare type MatchesPattern<Pattern extends string, Current extends string> = Current extends `` ? string : (Current extends `${Pattern}${infer Rest}` ? MatchesPattern<Pattern, Rest> : "bytes"); | ||
/** | ||
* Represents a hex literal string. | ||
* @param {string} hexStr - should be in format of hex bytes, i.e. `/^([0-9a-fA-F]{2})*$/` | ||
*/ | ||
export declare function b<T extends string & IsHexBytes, IsHexBytes = MatchesPattern<HexByte, T>>(hexStr: T): string; | ||
/** | ||
* The auto keyword specifies that the type of the variable, of basic type, declared will be automatically deducted from its initializer. | ||
* @category Types | ||
*/ | ||
export declare type auto = any; | ||
export declare function b(a: TemplateStringsArray): string; | ||
/** | ||
* @ignore | ||
*/ | ||
declare type Grow<T, A extends Array<T>> = ((x: T, ...xs: A) => void) extends ((...a: infer X) => void) ? X : never; | ||
/** | ||
* @ignore | ||
*/ | ||
declare type GrowToSize<T, A extends Array<T>, N extends number> = { | ||
@@ -9,3 +35,76 @@ 0: A; | ||
}[A['length'] extends N ? 0 : 1]; | ||
/** | ||
* An array is a fixed-size list of values of the same basic type. | ||
* When you declare an array you have to declare it like this: | ||
* @example | ||
* ```ts | ||
* let aaa: FixedArray<bigint, 3> = [1n, 3n, 3n]; | ||
* | ||
* let abb: FixedArray<FixedArray<bigint, 2>, 3> = [[1n, 3n], [1n, 3n], [1n, 3n]]; | ||
* | ||
* let bbb: FixedArray<FixedArray<FixedArray<bigint, 1>, 2>, 3> = [[[1n], [1n]], [[1n], [1n]], [[1n], [1n]]]; | ||
* ``` | ||
* @category Array | ||
*/ | ||
export declare type FixedArray<T, N extends number> = GrowToSize<T, [], N>; | ||
/** | ||
* a public key type. | ||
* @category Types | ||
*/ | ||
export declare class PubKey extends PubKey_ { | ||
} | ||
/** | ||
* a signature type in [DER](https://docs.moneybutton.com/docs/bsv-signature.html) format, including [signature hash type](https://github.com/libbitcoin/libbitcoin-system/wiki/Sighash-and-TX-Signing), which is `SIGHASH_ALL | SIGHASH_FORKID (0x41)` in the below example. | ||
* @category Types | ||
*/ | ||
export declare class Sig extends Sig_ { | ||
} | ||
/** | ||
* a sighash preimage type. | ||
* @category Types | ||
*/ | ||
export declare class SigHashPreimage extends SigHashPreimage_ { | ||
} | ||
/** | ||
* a RIPEMD-160 hash type. | ||
* @category Types | ||
*/ | ||
export declare class Ripemd160 extends Ripemd160_ { | ||
} | ||
/** | ||
* an alias for Ripemd160` type. Usually represent a bitcoin address. | ||
* @category Types | ||
*/ | ||
export declare class PubKeyHash extends PubKeyHash_ { | ||
} | ||
/** | ||
* a SHA-256 hash type. | ||
* @category Types | ||
*/ | ||
export declare class Sha256 extends Sha256_ { | ||
} | ||
/** | ||
* a SHA-1 hash type. | ||
* @category Types | ||
*/ | ||
export declare class Sha1 extends Sha1_ { | ||
} | ||
/** | ||
* a sighash type. | ||
* @category Types | ||
*/ | ||
export declare class SigHashType extends SigHashType_ { | ||
} | ||
/** | ||
* a OpCode type. | ||
* @category Types | ||
*/ | ||
export declare class OpCodeType extends OpCodeType_ { | ||
} | ||
/** | ||
* a private key type. | ||
* @category Types | ||
*/ | ||
export declare class PrivKey extends PrivKey_ { | ||
} | ||
export {}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.b = exports.Bool = exports.Int = exports.Bytes = exports.PrivKey = exports.OpCodeType = exports.SigHashType = exports.Sha1 = exports.Sha256 = exports.PubKeyHash = exports.Ripemd160 = exports.SigHashPreimage = exports.Sig = exports.PubKey = void 0; | ||
var scryptlib_1 = require("scryptlib"); | ||
Object.defineProperty(exports, "PubKey", { enumerable: true, get: function () { return scryptlib_1.PubKey; } }); | ||
Object.defineProperty(exports, "Sig", { enumerable: true, get: function () { return scryptlib_1.Sig; } }); | ||
Object.defineProperty(exports, "SigHashPreimage", { enumerable: true, get: function () { return scryptlib_1.SigHashPreimage; } }); | ||
Object.defineProperty(exports, "Ripemd160", { enumerable: true, get: function () { return scryptlib_1.Ripemd160; } }); | ||
Object.defineProperty(exports, "PubKeyHash", { enumerable: true, get: function () { return scryptlib_1.PubKeyHash; } }); | ||
Object.defineProperty(exports, "Sha256", { enumerable: true, get: function () { return scryptlib_1.Sha256; } }); | ||
Object.defineProperty(exports, "Sha1", { enumerable: true, get: function () { return scryptlib_1.Sha1; } }); | ||
Object.defineProperty(exports, "SigHashType", { enumerable: true, get: function () { return scryptlib_1.SigHashType; } }); | ||
Object.defineProperty(exports, "OpCodeType", { enumerable: true, get: function () { return scryptlib_1.OpCodeType; } }); | ||
Object.defineProperty(exports, "PrivKey", { enumerable: true, get: function () { return scryptlib_1.PrivKey; } }); | ||
Object.defineProperty(exports, "Bytes", { enumerable: true, get: function () { return scryptlib_1.Bytes; } }); | ||
Object.defineProperty(exports, "Int", { enumerable: true, get: function () { return scryptlib_1.Int; } }); | ||
Object.defineProperty(exports, "Bool", { enumerable: true, get: function () { return scryptlib_1.Bool; } }); | ||
function b(a) { return a.join(''); } | ||
exports.PrivKey = exports.OpCodeType = exports.SigHashType = exports.Sha1 = exports.Sha256 = exports.PubKeyHash = exports.Ripemd160 = exports.SigHashPreimage = exports.Sig = exports.PubKey = exports.b = void 0; | ||
const scryptlib_1 = require("scryptlib"); | ||
/** | ||
* Represents a hex literal string. | ||
* @param {string} hexStr - should be in format of hex bytes, i.e. `/^([0-9a-fA-F]{2})*$/` | ||
*/ | ||
function b(hexStr) { | ||
return hexStr; | ||
} | ||
exports.b = b; | ||
; | ||
/** | ||
* a public key type. | ||
* @category Types | ||
*/ | ||
class PubKey extends scryptlib_1.PubKey { | ||
} | ||
exports.PubKey = PubKey; | ||
/** | ||
* a signature type in [DER](https://docs.moneybutton.com/docs/bsv-signature.html) format, including [signature hash type](https://github.com/libbitcoin/libbitcoin-system/wiki/Sighash-and-TX-Signing), which is `SIGHASH_ALL | SIGHASH_FORKID (0x41)` in the below example. | ||
* @category Types | ||
*/ | ||
class Sig extends scryptlib_1.Sig { | ||
} | ||
exports.Sig = Sig; | ||
/** | ||
* a sighash preimage type. | ||
* @category Types | ||
*/ | ||
class SigHashPreimage extends scryptlib_1.SigHashPreimage { | ||
} | ||
exports.SigHashPreimage = SigHashPreimage; | ||
/** | ||
* a RIPEMD-160 hash type. | ||
* @category Types | ||
*/ | ||
class Ripemd160 extends scryptlib_1.Ripemd160 { | ||
} | ||
exports.Ripemd160 = Ripemd160; | ||
/** | ||
* an alias for Ripemd160` type. Usually represent a bitcoin address. | ||
* @category Types | ||
*/ | ||
class PubKeyHash extends scryptlib_1.PubKeyHash { | ||
} | ||
exports.PubKeyHash = PubKeyHash; | ||
/** | ||
* a SHA-256 hash type. | ||
* @category Types | ||
*/ | ||
class Sha256 extends scryptlib_1.Sha256 { | ||
} | ||
exports.Sha256 = Sha256; | ||
/** | ||
* a SHA-1 hash type. | ||
* @category Types | ||
*/ | ||
class Sha1 extends scryptlib_1.Sha1 { | ||
} | ||
exports.Sha1 = Sha1; | ||
/** | ||
* a sighash type. | ||
* @category Types | ||
*/ | ||
class SigHashType extends scryptlib_1.SigHashType { | ||
} | ||
exports.SigHashType = SigHashType; | ||
/** | ||
* a OpCode type. | ||
* @category Types | ||
*/ | ||
class OpCodeType extends scryptlib_1.OpCodeType { | ||
} | ||
exports.OpCodeType = OpCodeType; | ||
/** | ||
* a private key type. | ||
* @category Types | ||
*/ | ||
class PrivKey extends scryptlib_1.PrivKey { | ||
} | ||
exports.PrivKey = PrivKey; | ||
//# sourceMappingURL=types.js.map |
@@ -6,2 +6,5 @@ /// <reference types="bsv" /> | ||
import { SigHashPreimage, SigHashType } from "./builtins/types"; | ||
/** | ||
* The input point where the smart contract is spent by the transaction | ||
*/ | ||
export declare type TxInputRef = { | ||
@@ -11,2 +14,5 @@ tx: bsv.Transaction; | ||
}; | ||
/** | ||
* The transaction output point bound by the smart contract | ||
*/ | ||
export declare type TxOutputRef = { | ||
@@ -16,2 +22,12 @@ tx: bsv.Transaction; | ||
}; | ||
/** | ||
* The main contract class. To write a contract, extend this class as such: | ||
* @example | ||
* ```ts | ||
* class YourSmartContract extends SmartContract { | ||
* // your smart contract code here | ||
* } | ||
* ``` | ||
* @category SmartContract | ||
*/ | ||
export declare class SmartContract { | ||
@@ -22,3 +38,3 @@ lockingTo?: TxOutputRef; | ||
private delegateInstance; | ||
static compile(): Promise<import("scryptlib/dist/compilerWrapper").CompileError[]>; | ||
static compile(): Promise<void>; | ||
private static _getScryptFile; | ||
@@ -28,3 +44,3 @@ constructor(...args: any[]); | ||
getUnlockingScript(callPub: (self: typeof this) => void): Script; | ||
get lockingScript(): Script; | ||
get lockingScript(): bsv.Script; | ||
clone(): this; | ||
@@ -31,0 +47,0 @@ markAsGenesis(): void; |
@@ -48,6 +48,16 @@ "use strict"; | ||
const lodash_clonedeep_1 = __importDefault(require("lodash.clonedeep")); | ||
/** | ||
* The main contract class. To write a contract, extend this class as such: | ||
* @example | ||
* ```ts | ||
* class YourSmartContract extends SmartContract { | ||
* // your smart contract code here | ||
* } | ||
* ``` | ||
* @category SmartContract | ||
*/ | ||
class SmartContract { | ||
constructor(...args) { | ||
if (!SmartContract.DelegateClazz) { | ||
throw new Error(`'${typeof this}.compile' should be called before initilizing any instance!`); | ||
throw new Error(`'${this.constructor.name}.compile' should be called before initilizing any instance!`); | ||
} | ||
@@ -66,7 +76,8 @@ const args_ = args.map(arg => { | ||
const result = await (0, scryptlib_1.compileContractAsync)(filePath, { | ||
sourceMap: true, | ||
out: (0, path_1.join)(__dirname, '..', 'out') | ||
// sourceMap: true, | ||
desc: true, | ||
out: (0, path_1.join)((0, path_1.dirname)(filePath)) | ||
}); | ||
if (result.errors.length > 0) { | ||
return result.errors; | ||
throw new Error(`Compiled failed for class \`${this.name}\`, check the output details at project building time!`); | ||
} | ||
@@ -95,3 +106,3 @@ SmartContract.DelegateClazz = (0, scryptlib_1.buildContractClass)(result); | ||
let scryptFile = Object.getPrototypeOf(this).constructor._getScryptFile(); | ||
const sourceMapFile = scryptFile.replace(/\.scrypt$/, '.map.json'); | ||
const sourceMapFile = scryptFile.replace(/\.scrypt$/, '.scrypt.map'); | ||
if (!fs.existsSync(sourceMapFile)) { | ||
@@ -147,3 +158,3 @@ throw new Error(`can not find the bundled sourcemap file for \`${typeof this}\` at ${sourceMapFile}`); | ||
getStateScript() { | ||
return (0, types_1.b) `00`; | ||
return ""; | ||
} | ||
@@ -150,0 +161,0 @@ updateState(preimage, balance) { |
@@ -1,3 +0,11 @@ | ||
export declare function contract(target: any): any; | ||
/** | ||
* Indicates whether the method is a contract method, and ordinary methods do not affect the execution of the contract | ||
* @category decorator | ||
*/ | ||
export declare function method(target: any, methodName: string, descriptor: PropertyDescriptor): PropertyDescriptor; | ||
/** | ||
* Indicates whether the property is an property of a contract, and ordinary class properties cannot be accessed in contract methods | ||
* @category decorator | ||
* @param state - Whether the property is a property of a stateful contract | ||
*/ | ||
export declare function prop(state?: boolean): (target: any, propertyName: string) => void; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.prop = exports.method = exports.contract = void 0; | ||
exports.prop = exports.method = void 0; | ||
const dist_1 = require("scryptlib/dist"); | ||
function contract(target) { | ||
console.log(`@contract decorator on ${typeof target}`); | ||
return target; | ||
} | ||
exports.contract = contract; | ||
/** | ||
* Indicates whether the method is a contract method, and ordinary methods do not affect the execution of the contract | ||
* @category decorator | ||
*/ | ||
function method(target, methodName, descriptor) { | ||
@@ -43,2 +42,7 @@ const originalMethod = descriptor.value; | ||
exports.method = method; | ||
/** | ||
* Indicates whether the property is an property of a contract, and ordinary class properties cannot be accessed in contract methods | ||
* @category decorator | ||
* @param state - Whether the property is a property of a stateful contract | ||
*/ | ||
function prop(state = false) { | ||
@@ -49,3 +53,2 @@ return function (target, propertyName) { | ||
let stateProps = (Reflect.getMetadata(statePropsMetaKey, target) || []).concat(propertyName); | ||
console.log('define', propertyName, stateProps, target); | ||
Reflect.defineMetadata(statePropsMetaKey, stateProps, target); | ||
@@ -52,0 +55,0 @@ } |
@@ -82,3 +82,3 @@ "use strict"; | ||
if (this.symbolPaths.has(symbol)) { | ||
console.log(`symbol \`${symbol}\` already has a path binding ${this.symbolPaths.get(symbol)} in ${this.filePath}`); | ||
console.log(`symbol \`${symbol}\` already has a path binding \`${this.symbolPaths.get(symbol)}\` in ${this.filePath}`); | ||
// throw new Error(`symbol \`${symbol}\` already has a path binding ${this.symbolPaths.get(symbol)} in ${this.filePath}`) | ||
@@ -85,0 +85,0 @@ } |
@@ -1,5 +0,5 @@ | ||
import { contract, prop, method } from './decorators'; | ||
import { prop, method } from './decorators'; | ||
import { SmartContract, TxOutputRef } from './contract'; | ||
export * from './builtins/types'; | ||
export * from './builtins/functions'; | ||
export { contract, prop, method, SmartContract, TxOutputRef }; | ||
export { prop, method, SmartContract, TxOutputRef }; |
@@ -17,5 +17,4 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.SmartContract = exports.method = exports.prop = exports.contract = void 0; | ||
exports.SmartContract = exports.method = exports.prop = void 0; | ||
const decorators_1 = require("./decorators"); | ||
Object.defineProperty(exports, "contract", { enumerable: true, get: function () { return decorators_1.contract; } }); | ||
Object.defineProperty(exports, "prop", { enumerable: true, get: function () { return decorators_1.prop; } }); | ||
@@ -22,0 +21,0 @@ Object.defineProperty(exports, "method", { enumerable: true, get: function () { return decorators_1.method; } }); |
import * as ts from 'typescript'; | ||
export default function (program: ts.Program, pluginOptions: any): (ctx: ts.TransformationContext) => (sourceFile: ts.SourceFile) => ts.SourceFile; | ||
/*** | ||
* @ignore | ||
*/ | ||
export default function transformProgram(program: ts.Program, host: ts.CompilerHost | undefined, pluginOptions: ts.PluginConfig, { ts: tsInstance }: ts.ProgramTransformerExtras): ts.Program; |
@@ -30,6 +30,9 @@ "use strict"; | ||
const transpiler_1 = require("./transpiler"); | ||
function default_1(program, pluginOptions) { | ||
if (pluginOptions.debug) { | ||
console.log('transformer loaded with options:', pluginOptions, '\n'); | ||
} | ||
/*** | ||
* @ignore | ||
*/ | ||
function transformProgram(program, host, pluginOptions, { ts: tsInstance }) { | ||
const compilerOptions = program.getCompilerOptions(); | ||
const compilerHost = getPatchedHost(host, tsInstance, compilerOptions); | ||
const rootFileNames = program.getRootFileNames().map(path.normalize); | ||
let tsconfigDir = process.env['TS_NODE_PROJECT'] ? path.dirname(process.env['TS_NODE_PROJECT']) : '.'; | ||
@@ -43,2 +46,4 @@ tsconfigDir = path.isAbsolute(tsconfigDir) ? tsconfigDir : path.join(program.getCurrentDirectory(), tsconfigDir); | ||
if (pluginOptions.debug) { | ||
console.log('activate scrypt-ts transformer plugin'); | ||
console.log('transformer loaded with options:', pluginOptions, '\n'); | ||
console.log('*'.repeat(20), 'path context', '*'.repeat(20)); | ||
@@ -49,47 +54,85 @@ console.log(`tsRootDir: ${tsRootDir}\ntsconfigDir: ${tsconfigDir}\njsOutDir: ${jsOutDir}\nscryptOutDir: ${scryptOutDir}`); | ||
let indexer = new idexer_1.Indexer({ tsconfigDir, scryptOutDir }); | ||
return (ctx) => { | ||
return (sourceFile) => { | ||
// ts source file to root dir relative path which will be keeped in scrypt output structure. | ||
const root2srcRelativePath = path.relative(tsRootDir, sourceFile.fileName); | ||
// the relative path from the scrypt output root directory to the file | ||
const scryptFile = root2srcRelativePath.replace(/\.ts$/, '.scrypt'); | ||
let transpiler = new transpiler_1.Transpiler(sourceFile, program.getTypeChecker(), scryptOutDir, scryptFile, indexer); | ||
if (!transpiler.isTransformable()) { | ||
return sourceFile; | ||
} | ||
transpiler.transform(); | ||
let metaDataDefs = []; | ||
function visitor(node) { | ||
if (transpiler.contractDefs.includes(node)) { | ||
const className = node.name?.escapedText.toString(); | ||
if (className) { | ||
// `Reflect.defineMetadata("scrypt:file", $relative_path_to_scrypt, $className)` | ||
const defineScryptPath = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Reflect'), "defineMetadata"), [], [ | ||
ts.factory.createStringLiteral("scrypt:file"), | ||
ts.factory.createStringLiteral(scryptFile), | ||
ts.factory.createIdentifier(className) | ||
]); | ||
const defineSrcPath = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Reflect'), "defineMetadata"), [], [ | ||
ts.factory.createStringLiteral("__filename"), | ||
ts.factory.createIdentifier("__filename"), | ||
ts.factory.createIdentifier(className) | ||
]); | ||
// const printer = ts.createPrinter(); | ||
// const tsCode = printer.printNode(ts.EmitHint.Expression, defineMetaCall, sourceFile) | ||
// console.log('inserted code: ', tsCode) | ||
metaDataDefs.push(defineScryptPath, defineSrcPath); | ||
} | ||
return node; | ||
let checker = program.getTypeChecker(); | ||
const transformedSource = tsInstance.transform(program.getSourceFiles(), [ | ||
transformFile.bind(tsInstance, checker, tsRootDir, scryptOutDir, indexer) | ||
], compilerOptions).transformed; | ||
/* Render modified files and create new SourceFiles for them to use in host's cache */ | ||
const { printFile } = tsInstance.createPrinter(); | ||
for (const sourceFile of transformedSource) { | ||
const { fileName, languageVersion } = sourceFile; | ||
const updatedSourceFile = tsInstance.createSourceFile(fileName, printFile(sourceFile), languageVersion); | ||
compilerHost.fileCache.set(fileName, updatedSourceFile); | ||
} | ||
/* Re-create Program instance */ | ||
return tsInstance.createProgram(rootFileNames, compilerOptions, compilerHost, program); | ||
} | ||
exports.default = transformProgram; | ||
function getPatchedHost(maybeHost, tsInstance, compilerOptions) { | ||
const fileCache = new Map(); | ||
const compilerHost = maybeHost ?? tsInstance.createCompilerHost(compilerOptions, true); | ||
const cacheableVersion = (originalFunc) => { | ||
return function (fileName) { | ||
fileName = path.normalize(fileName); | ||
if (fileCache.has(fileName)) | ||
return fileCache.get(fileName); | ||
const sourceFile = originalFunc.apply(void 0, Array.from(arguments)); | ||
fileCache.set(fileName, sourceFile); | ||
return sourceFile; | ||
}; | ||
}; | ||
return Object.assign(compilerHost, { | ||
getSourceFile: cacheableVersion(compilerHost.getSourceFile), | ||
getSourceFileByPath: cacheableVersion(compilerHost.getSourceFileByPath), | ||
fileCache | ||
}); | ||
} | ||
function transformFile(checker, tsRootDir, scryptOutDir, indexer, ctx) { | ||
const tsInstance = this; | ||
return (sourceFile) => { | ||
// skip declaration files of *.d.ts | ||
if (sourceFile.fileName.endsWith('.d.ts')) { | ||
return sourceFile; | ||
} | ||
// console.log('scanning...', sourceFile.fileName); | ||
// ts source file to root dir relative path which will be keeped in scrypt output structure. | ||
const root2srcRelativePath = path.relative(tsRootDir, sourceFile.fileName); | ||
// the relative path from the scrypt output root directory to the file | ||
const scryptFile = root2srcRelativePath.replace(/\.ts$/, '.scrypt'); | ||
let transpiler = new transpiler_1.Transpiler(sourceFile, checker, scryptOutDir, scryptFile, indexer); | ||
if (!transpiler.isTransformable()) { | ||
return sourceFile; | ||
} | ||
transpiler.transform(); | ||
let metaDataDefs = []; | ||
function visitor(node) { | ||
if (transpiler.contractDefs.includes(node)) { | ||
const className = node.name?.escapedText.toString(); | ||
if (className) { | ||
// `Reflect.defineMetadata("scrypt:file", $relative_path_to_scrypt, $className)` | ||
const defineScryptPath = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Reflect'), "defineMetadata"), [], [ | ||
ts.factory.createStringLiteral("scrypt:file"), | ||
ts.factory.createStringLiteral(scryptFile), | ||
ts.factory.createIdentifier(className) | ||
]); | ||
const defineSrcPath = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Reflect'), "defineMetadata"), [], [ | ||
ts.factory.createStringLiteral("__filename"), | ||
ts.factory.createIdentifier("__filename"), | ||
ts.factory.createIdentifier(className) | ||
]); | ||
// const printer = ts.createPrinter(); | ||
// const tsCode = printer.printNode(ts.EmitHint.Expression, defineMetaCall, sourceFile) | ||
// console.log('inserted code: ', tsCode) | ||
metaDataDefs.push(defineScryptPath, defineSrcPath); | ||
} | ||
return ts.visitEachChild(node, visitor, ctx); | ||
return node; | ||
} | ||
ts.visitEachChild(sourceFile, visitor, ctx); | ||
return ts.factory.updateSourceFile(sourceFile, | ||
// appends meta data definitions to the end of source file. | ||
sourceFile.statements | ||
.concat(ts.factory.createNodeArray(metaDataDefs.map(e => ts.factory.createExpressionStatement(e)))), sourceFile.isDeclarationFile, sourceFile.referencedFiles, sourceFile.typeReferenceDirectives, sourceFile.hasNoDefaultLib, sourceFile.libReferenceDirectives); | ||
}; | ||
return tsInstance.visitEachChild(node, visitor, ctx); | ||
} | ||
tsInstance.visitEachChild(sourceFile, visitor, ctx); | ||
return ts.factory.updateSourceFile(sourceFile, | ||
// appends meta data definitions to the end of source file. | ||
sourceFile.statements | ||
.concat(ts.factory.createNodeArray(metaDataDefs.map(e => ts.factory.createExpressionStatement(e)))), sourceFile.isDeclarationFile, sourceFile.referencedFiles, sourceFile.typeReferenceDirectives, sourceFile.hasNoDefaultLib, sourceFile.libReferenceDirectives); | ||
}; | ||
} | ||
exports.default = default_1; | ||
//# sourceMappingURL=transformer.js.map |
@@ -46,6 +46,7 @@ import * as ts from 'typescript'; | ||
transform(): EmittedSection; | ||
checkScrypt(): void; | ||
isTransformable(): boolean; | ||
private checkTransformedScrypt; | ||
private generateSourceMapFile; | ||
private updateIndex; | ||
private searchScryptContracts; | ||
private searchSmartContracts; | ||
private getCoordinates; | ||
@@ -64,3 +65,3 @@ private getLocation; | ||
private transformArrayType; | ||
private transformType; | ||
private transformTypeNode; | ||
private transformModifiers; | ||
@@ -67,0 +68,0 @@ private transformImports; |
@@ -162,3 +162,3 @@ "use strict"; | ||
this._indexer = indexer; | ||
this.searchScryptContracts(); | ||
this.searchSmartContracts(); | ||
} | ||
@@ -171,2 +171,13 @@ transform() { | ||
let result = EmittedSection.join(imports, structs, ...contracts); | ||
ts.sys.writeFile(this._outFilePath, result.getCode()); | ||
this.checkTransformedScrypt(result); | ||
this.generateSourceMapFile(result); | ||
this.updateIndex(); | ||
return result; | ||
} | ||
isTransformable() { | ||
return this.contractDefs.length > 0; | ||
} | ||
// check transformed scrypt code to collect compile time errors | ||
checkTransformedScrypt(result) { | ||
const settings = Object.assign({}, DEFAULT_AST_COMPILE_OPTS, { | ||
@@ -203,18 +214,8 @@ cmdPrefix: (0, scryptlib_1.findCompiler)() | ||
} | ||
ts.sys.writeFile(this._outFilePath, result.getCode()); | ||
const sourceMapFile = this._outFilePath.replace(/\.scrypt$/, '.map.json'); | ||
ts.sys.writeFile(sourceMapFile, JSON.stringify(result.getSourceMap())); | ||
console.log('='.repeat(50)); | ||
console.log('transpiling errors', JSON.stringify(result.errors, null, 1)); | ||
this.updateIndex(); | ||
console.log('='.repeat(50)); | ||
return result; | ||
} | ||
checkScrypt() { | ||
// compile transpiled scrypt code | ||
// output errors | ||
generateSourceMapFile(result) { | ||
const sourceMapFile = this._outFilePath.replace(/\.scrypt$/, '.scrypt.map'); | ||
ts.sys.writeFile(sourceMapFile, JSON.stringify(result.getSourceMap())); | ||
} | ||
isTransformable() { | ||
return this.contractDefs.length > 0; | ||
} | ||
updateIndex() { | ||
@@ -224,6 +225,14 @@ let globalSymbols = Array.from(this._localTypeSymbols.keys()); | ||
} | ||
searchScryptContracts() { | ||
const contractDefs = (0, tsquery_1.tsquery)(this._srcFile, "ClassDeclaration:has(Decorator > Identifier[name='contract'])"); | ||
contractDefs.forEach(cDef => (0, console_1.assert)(ts.isClassDeclaration(cDef))); | ||
this.contractDefs = contractDefs; | ||
searchSmartContracts() { | ||
const contractDefs = (0, tsquery_1.tsquery)(this._srcFile, "ClassDeclaration:has(HeritageClause)"); | ||
let smartContracts = contractDefs.filter((cDef) => { | ||
// only count declarations as `class X extends SmartContrat` | ||
let extendsSmartContract = false; | ||
cDef.heritageClauses.forEach(clause => { | ||
extendsSmartContract ||= (0, tsquery_1.tsquery)(clause, "Identifier[name='SmartContract']").length > 0; | ||
}); | ||
return extendsSmartContract; | ||
}); | ||
smartContracts.forEach(cDef => (0, console_1.assert)(ts.isClassDeclaration(cDef))); | ||
this.contractDefs = smartContracts; | ||
} | ||
@@ -283,3 +292,3 @@ getCoordinates(pos) { | ||
transformPropertySignature(node, toSection) { | ||
return this.transformType(node.type, toSection, this.getCoordinates(node.type.getStart())) | ||
return this.transformTypeNode(node.type, toSection, this.getCoordinates(node.type.getStart())) | ||
.append(` ${node.name.getText()}`, this.getCoordinates(node.name.getStart())) | ||
@@ -301,7 +310,8 @@ .append(';'); | ||
} | ||
toSection.appendWith(this, toSec => { | ||
toSection | ||
.appendWith(this, toSec => { | ||
return this.transformModifiers(node, toSec); | ||
}) | ||
.appendWith(this, toSec => { | ||
return this.transformType(node.type, toSec, this.getCoordinates(node.type?.getStart())); | ||
return this.transformTypeNode(node.type, toSec, this.getCoordinates(node.type?.getStart())); | ||
}) | ||
@@ -379,3 +389,3 @@ .append(` ${node.name.getText()}`, this.getCoordinates(node.name.getStart())); | ||
.appendWith(this, toSec => { | ||
return this.transformType(node.type, toSec, this.getCoordinates(node.type.getStart())); | ||
return this.transformTypeNode(node.type, toSec, this.getCoordinates(node.type.getStart())); | ||
}) | ||
@@ -394,3 +404,3 @@ .append(' '); | ||
.appendWith(this, toSec => { | ||
return this.transformType(node.type, toSec, this.getCoordinates(node.type.getStart())); | ||
return this.transformTypeNode(node.type, toSec, this.getCoordinates(node.type.getStart())); | ||
}) | ||
@@ -445,3 +455,3 @@ .append(' ') | ||
.appendWith(this, toSec => { | ||
return this.transformType(d.type, toSec, this.getCoordinates(d.type.getStart())); | ||
return this.transformTypeNode(d.type, toSec, this.getCoordinates(d.type.getStart())); | ||
}) | ||
@@ -567,2 +577,6 @@ .append(` ${d.name.getText()}`, this.getCoordinates(d.name.getStart())) | ||
let isSerialize = e.expression.kind === ts.SyntaxKind.PropertyAccessExpression && e.expression.name.getText() === 'serialize'; | ||
let isNumberConvert = e.expression.getText() === "BigInt" || e.expression.getText() === "Number"; | ||
let isBytesLiteral = e.expression.kind === ts.SyntaxKind.Identifier && e.expression.getText() === 'b'; | ||
const isStillCallExpr = !isLoop && !isSerialize && !isToString && !isBytesLiteral && !isNumberConvert; | ||
// transform the callee's identifier | ||
toSection.appendWith(this, toSec => { | ||
@@ -577,3 +591,3 @@ if (e.expression.getText() === "assert") { | ||
} | ||
else if (e.expression.getText() === "BigInt" || e.expression.getText() === "Number") { | ||
else if (isNumberConvert) { | ||
return toSec; | ||
@@ -587,11 +601,17 @@ } | ||
}); | ||
// transform the open brace | ||
if (isSlice) { | ||
toSection.append("["); | ||
} | ||
else if (!isLoop && !isToString && !isSerialize) { | ||
else if (isBytesLiteral) { | ||
toSection.append("'"); | ||
} | ||
else if (isStillCallExpr) { | ||
toSection.append("("); | ||
} | ||
// transform arguments list | ||
e.arguments.forEach((arg, index) => { | ||
if (isSlice) { | ||
toSection.append("(") | ||
toSection | ||
.append("(") | ||
.appendWith(this, toSec => { | ||
@@ -602,2 +622,7 @@ return this.transformExpression(arg, toSec); | ||
} | ||
else if (isBytesLiteral) { | ||
let argTxt = arg.getText(); | ||
// remove around single or double quotes | ||
toSection.append(argTxt.slice(1, argTxt.length - 1), this.getCoordinates(arg.pos)); | ||
} | ||
else if (!isToString && !isSerialize) { | ||
@@ -617,6 +642,10 @@ toSection.appendWith(this, toSec => { | ||
}); | ||
// transform the close brace | ||
if (isSlice) { | ||
toSection.append("]"); | ||
} | ||
else if (!isLoop && !isSerialize && !isToString) { | ||
else if (isBytesLiteral) { | ||
toSection.append("'"); | ||
} | ||
else if (isStillCallExpr) { | ||
toSection.append(")"); | ||
@@ -633,3 +662,4 @@ } | ||
const e = node; | ||
toSection.appendWith(this, toSec => { | ||
toSection | ||
.appendWith(this, toSec => { | ||
return this.transformExpression(e.left, toSec); | ||
@@ -667,3 +697,4 @@ }) | ||
else { | ||
toSection.appendWith(this, toSec => { | ||
toSection | ||
.appendWith(this, toSec => { | ||
return this.transformExpression(e.expression, toSec); | ||
@@ -681,3 +712,4 @@ }) | ||
} | ||
toSection.appendWith(this, toSec => { | ||
toSection | ||
.appendWith(this, toSec => { | ||
return this.transformExpression(e.expression, toSec); | ||
@@ -749,3 +781,4 @@ }) | ||
const e = node; | ||
toSection.appendWith(this, toSec => { | ||
toSection | ||
.appendWith(this, toSec => { | ||
return this.transformExpression(e.expression, toSec); | ||
@@ -762,3 +795,4 @@ }) | ||
const e = node; | ||
toSection.appendWith(this, toSec => { | ||
toSection | ||
.appendWith(this, toSec => { | ||
return this.transformExpression(e.condition, toSec); | ||
@@ -785,3 +819,4 @@ }) | ||
const e = node; | ||
toSection.append("(") | ||
toSection | ||
.append("(") | ||
.appendWith(this, toSec => { | ||
@@ -878,3 +913,3 @@ return this.transformExpression(e.expression, toSec); | ||
} | ||
transformType(tnode, toSection, coordinates) { | ||
transformTypeNode(tnode, toSection, coordinates) { | ||
const scryptType = (0, utils_1.getBuildInType)(tnode.getText()); | ||
@@ -931,2 +966,38 @@ if (scryptType) { | ||
} | ||
// private transformType(type: ts.Type, toSection: EmittedSection, coordinates?: ts.LineAndCharacter) : EmittedSection { | ||
// switch (type.flags) { | ||
// case ts.TypeFlags.String: { | ||
// toSection.append('bytes', coordinates); | ||
// break; | ||
// } | ||
// case ts.TypeFlags.Number: { | ||
// toSection.append('int', coordinates); | ||
// break; | ||
// } | ||
// case ts.TypeFlags.BigInt: { | ||
// toSection.append('int', coordinates); | ||
// } | ||
// case ts.TypeFlags.Object: { | ||
// if (typeName === "FixedArray") { | ||
// const [t, dimensionals] = this.transformArrayType(tnode); | ||
// toSection.append(`${t}${dimensionals.reverse().map(d => `[${d}]`).join('') | ||
// }`, coordinates); | ||
// } else { | ||
// toSection.append(typeName, coordinates); | ||
// } | ||
// break; | ||
// } | ||
// case ts.TypeFlags.Any: { | ||
// toSection.append('auto', coordinates); | ||
// break; | ||
// } | ||
// default: { | ||
// throw new TranspileError( | ||
// `untransformable type ${type.flags}`, | ||
// { fileName: this._srcFile.fileName, coordinates } | ||
// ); | ||
// } | ||
// } | ||
// return toSection; | ||
// } | ||
transformModifiers(node, toSection) { | ||
@@ -933,0 +1004,0 @@ let modifiers = ts.getModifiers(node); |
{ | ||
"name": "scrypt-ts", | ||
"version": "0.1.0-alpha", | ||
"version": "0.1.1-alpha", | ||
"description": "A toolset for building sCrypt smart contract applications on Bitcoin SV network written in typescript.", | ||
@@ -9,5 +9,7 @@ "main": "dist/index.js", | ||
"build": "tsc", | ||
"gendocs": "typedoc --plugin typedoc-plugin-markdown --out docs src/index.ts", | ||
"pretest": "rimraf test/scrypt.index.json", | ||
"test": "cross-env TS_NODE_PROJECT=test/tsconfig.json NODE_ENV=test mocha -r ts-node/register 'test/**/*.test.ts' --timeout 1200000" | ||
"gendocs": "typedoc --plugin typedoc-plugin-markdown --out scrypt-ts-docs src/index.ts", | ||
"clean-test-out": "rimraf test/scrypt.index.json && rimraf test/out && rimraf test/scrypts", | ||
"pretest": "npm run clean-test-out && cross-env TS_NODE_PROJECT=test/tsconfig.json NODE_ENV=test tsc -p test", | ||
"test": "mocha 'test/out/**/*.test.js' --timeout 1200000", | ||
"single-test": "npm run clean-test-out && cross-env TS_NODE_PROJECT=test/tsconfig.json NODE_ENV=test mocha -r ts-node/register" | ||
}, | ||
@@ -30,2 +32,3 @@ "author": "sCrypt.Inc", | ||
"cross-env": "^7.0.3", | ||
"dotenv": "^16.0.3", | ||
"mocha": "^10.1.0", | ||
@@ -32,0 +35,0 @@ "rimraf": "^3.0.2", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
253040
41
2854
14