New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

scrypt-ts

Package Overview
Dependencies
Maintainers
1
Versions
179
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

scrypt-ts - npm Package Compare versions

Comparing version 0.1.3-alpha to 0.1.3-alpha.1

scrypt.index.json

52

dist/builtins/functions.d.ts

@@ -137,40 +137,2 @@ import { PubKey, Sig, PubKeyHash, SigHashType, OpCodeType, SigHashPreimage, PrivKey } from "./types";

/**
* 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

@@ -287,5 +249,3 @@ */

static readonly OP_NOP1: OpCodeType;
static readonly OP_CHECKLOCKTIMEVERIFY: OpCodeType;
static readonly OP_NOP2: OpCodeType;
static readonly OP_CHECKSEQUENCEVERIFY: OpCodeType;
static readonly OP_NOP3: OpCodeType;

@@ -299,4 +259,2 @@ static readonly OP_NOP4: OpCodeType;

static readonly OP_NOP10: OpCodeType;
static readonly OP_SMALLINTEGER: OpCodeType;
static readonly OP_PUBKEYS: OpCodeType;
static readonly OP_PUBKEYHASH: OpCodeType;

@@ -313,3 +271,3 @@ static readonly OP_PUBKEY: OpCodeType;

static toLEUnsigned(n: bigint, l: bigint): string;
static fromLEUnsigned(b: string): bigint;
static fromLEUnsigned(bytes: string): bigint;
static readVarint(buf: string): string;

@@ -342,3 +300,3 @@ static writeVarint(buf: string): string;

static nLocktimeRaw(preimage: SigHashPreimage): string;
static nLocktime(preimage: SigHashPreimage): number;
static nLocktime(preimage: SigHashPreimage): bigint;
static sigHashType(preimage: SigHashPreimage): SigHashType;

@@ -353,5 +311,7 @@ }

static readonly Version: bigint;
buf: string;
pos: bigint;
constructor(buf: string);
eof(): boolean;
readstring(): string;
readBytes(): string;
readBool(): boolean;

@@ -365,3 +325,3 @@ readInt(): bigint;

export declare class VarIntWriter {
static writestring(buf: string): string;
static writeBytes(buf: string): string;
static writeBool(x: boolean): string;

@@ -368,0 +328,0 @@ static writeInt(x: bigint): string;

"use strict";
// build-in function
Object.defineProperty(exports, "__esModule", { value: true });
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;
exports.Constants = exports.Tx = exports.VarIntWriter = exports.VarIntReader = exports.SigHash = exports.Utils = exports.OpCode = exports.asm = 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");

@@ -207,49 +206,2 @@ /**

/**
* 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 < maxLoopCount; i++) {
fn(i);
}
};
return loopFn;
}
exports.loop = loop;
;
/**
* @ignore

@@ -268,2 +220,129 @@ */

exports.OpCode = OpCode;
// push value
OpCode.OP_0 = new types_1.OpCodeType('00');
OpCode.OP_FALSE = new types_1.OpCodeType('00');
OpCode.OP_PUSHDATA1 = new types_1.OpCodeType('4c');
OpCode.OP_PUSHDATA2 = new types_1.OpCodeType('4d');
OpCode.OP_PUSHDATA4 = new types_1.OpCodeType('4e');
OpCode.OP_1NEGATE = new types_1.OpCodeType('4f');
OpCode.OP_RESERVED = new types_1.OpCodeType('50');
OpCode.OP_1 = new types_1.OpCodeType('51');
OpCode.OP_TRUE = new types_1.OpCodeType('51');
OpCode.OP_2 = new types_1.OpCodeType('52');
OpCode.OP_3 = new types_1.OpCodeType('53');
OpCode.OP_4 = new types_1.OpCodeType('54');
OpCode.OP_5 = new types_1.OpCodeType('55');
OpCode.OP_6 = new types_1.OpCodeType('56');
OpCode.OP_7 = new types_1.OpCodeType('57');
OpCode.OP_8 = new types_1.OpCodeType('58');
OpCode.OP_9 = new types_1.OpCodeType('59');
OpCode.OP_10 = new types_1.OpCodeType('5a');
OpCode.OP_11 = new types_1.OpCodeType('5b');
OpCode.OP_12 = new types_1.OpCodeType('5c');
OpCode.OP_13 = new types_1.OpCodeType('5d');
OpCode.OP_14 = new types_1.OpCodeType('5e');
OpCode.OP_15 = new types_1.OpCodeType('5f');
OpCode.OP_16 = new types_1.OpCodeType('60');
// control
OpCode.OP_NOP = new types_1.OpCodeType('61');
OpCode.OP_VER = new types_1.OpCodeType('62');
OpCode.OP_IF = new types_1.OpCodeType('63');
OpCode.OP_NOTIF = new types_1.OpCodeType('64');
OpCode.OP_VERIF = new types_1.OpCodeType('65');
OpCode.OP_VERNOTIF = new types_1.OpCodeType('66');
OpCode.OP_ELSE = new types_1.OpCodeType('67');
OpCode.OP_ENDIF = new types_1.OpCodeType('68');
OpCode.OP_VERIFY = new types_1.OpCodeType('69');
OpCode.OP_RETURN = new types_1.OpCodeType('6a');
// stack ops
OpCode.OP_TOALTSTACK = new types_1.OpCodeType('6b');
OpCode.OP_FROMALTSTACK = new types_1.OpCodeType('6c');
OpCode.OP_2DROP = new types_1.OpCodeType('6d');
OpCode.OP_2DUP = new types_1.OpCodeType('6e');
OpCode.OP_3DUP = new types_1.OpCodeType('6f');
OpCode.OP_2OVER = new types_1.OpCodeType('70');
OpCode.OP_2ROT = new types_1.OpCodeType('71');
OpCode.OP_2SWAP = new types_1.OpCodeType('72');
OpCode.OP_IFDUP = new types_1.OpCodeType('73');
OpCode.OP_DEPTH = new types_1.OpCodeType('74');
OpCode.OP_DROP = new types_1.OpCodeType('75');
OpCode.OP_DUP = new types_1.OpCodeType('76');
OpCode.OP_NIP = new types_1.OpCodeType('77');
OpCode.OP_OVER = new types_1.OpCodeType('78');
OpCode.OP_PICK = new types_1.OpCodeType('79');
OpCode.OP_ROLL = new types_1.OpCodeType('7a');
OpCode.OP_ROT = new types_1.OpCodeType('7b');
OpCode.OP_SWAP = new types_1.OpCodeType('7c');
OpCode.OP_TUCK = new types_1.OpCodeType('7d');
// splice ops
OpCode.OP_CAT = new types_1.OpCodeType('7e');
OpCode.OP_SPLIT = new types_1.OpCodeType('7f'); // after monolith upgrade (May 2018)
OpCode.OP_NUM2BIN = new types_1.OpCodeType('80'); // after monolith upgrade (May 2018)
OpCode.OP_BIN2NUM = new types_1.OpCodeType('81'); // after monolith upgrade (May 2018)
OpCode.OP_SIZE = new types_1.OpCodeType('82');
// bit logic
OpCode.OP_INVERT = new types_1.OpCodeType('83');
OpCode.OP_AND = new types_1.OpCodeType('84');
OpCode.OP_OR = new types_1.OpCodeType('85');
OpCode.OP_XOR = new types_1.OpCodeType('86');
OpCode.OP_EQUAL = new types_1.OpCodeType('87');
OpCode.OP_EQUALVERIFY = new types_1.OpCodeType('88');
OpCode.OP_RESERVED1 = new types_1.OpCodeType('89');
OpCode.OP_RESERVED2 = new types_1.OpCodeType('8a');
// numeric
OpCode.OP_1ADD = new types_1.OpCodeType('8b');
OpCode.OP_1SUB = new types_1.OpCodeType('8c');
OpCode.OP_2MUL = new types_1.OpCodeType('8d');
OpCode.OP_2DIV = new types_1.OpCodeType('8e');
OpCode.OP_NEGATE = new types_1.OpCodeType('8f');
OpCode.OP_ABS = new types_1.OpCodeType('90');
OpCode.OP_NOT = new types_1.OpCodeType('91');
OpCode.OP_0NOTEQUAL = new types_1.OpCodeType('92');
OpCode.OP_ADD = new types_1.OpCodeType('93');
OpCode.OP_SUB = new types_1.OpCodeType('94');
OpCode.OP_MUL = new types_1.OpCodeType('95');
OpCode.OP_DIV = new types_1.OpCodeType('96');
OpCode.OP_MOD = new types_1.OpCodeType('97');
OpCode.OP_LSHIFT = new types_1.OpCodeType('98');
OpCode.OP_RSHIFT = new types_1.OpCodeType('99');
OpCode.OP_BOOLAND = new types_1.OpCodeType('9a');
OpCode.OP_BOOLOR = new types_1.OpCodeType('9b');
OpCode.OP_NUMEQUAL = new types_1.OpCodeType('9c');
OpCode.OP_NUMEQUALVERIFY = new types_1.OpCodeType('9d');
OpCode.OP_NUMNOTEQUAL = new types_1.OpCodeType('9e');
OpCode.OP_LESSTHAN = new types_1.OpCodeType('9f');
OpCode.OP_GREATERTHAN = new types_1.OpCodeType('a0');
OpCode.OP_LESSTHANOREQUAL = new types_1.OpCodeType('a1');
OpCode.OP_GREATERTHANOREQUAL = new types_1.OpCodeType('a2');
OpCode.OP_MIN = new types_1.OpCodeType('a3');
OpCode.OP_MAX = new types_1.OpCodeType('a4');
OpCode.OP_WITHIN = new types_1.OpCodeType('a5');
// crypto
OpCode.OP_RIPEMD160 = new types_1.OpCodeType('a6');
OpCode.OP_SHA1 = new types_1.OpCodeType('a7');
OpCode.OP_SHA256 = new types_1.OpCodeType('a8');
OpCode.OP_HASH160 = new types_1.OpCodeType('a9');
OpCode.OP_HASH256 = new types_1.OpCodeType('aa');
OpCode.OP_CODESEPARATOR = new types_1.OpCodeType('ab');
OpCode.OP_CHECKSIG = new types_1.OpCodeType('ac');
OpCode.OP_CHECKSIGVERIFY = new types_1.OpCodeType('ad');
OpCode.OP_CHECKMULTISIG = new types_1.OpCodeType('ae');
OpCode.OP_CHECKMULTISIGVERIFY = new types_1.OpCodeType('af');
// expansion
OpCode.OP_NOP1 = new types_1.OpCodeType('b0');
OpCode.OP_NOP2 = new types_1.OpCodeType('b1'); // previously OP_CHECKLOCKTIMEVERIFY
OpCode.OP_NOP3 = new types_1.OpCodeType('b2'); // OpCode.OP_CHECKSEQUENCEVERIFY;
OpCode.OP_NOP4 = new types_1.OpCodeType('b3');
OpCode.OP_NOP5 = new types_1.OpCodeType('b4');
OpCode.OP_NOP6 = new types_1.OpCodeType('b5');
OpCode.OP_NOP7 = new types_1.OpCodeType('b6');
OpCode.OP_NOP8 = new types_1.OpCodeType('b7');
OpCode.OP_NOP9 = new types_1.OpCodeType('b8');
OpCode.OP_NOP10 = new types_1.OpCodeType('b9');
// The first static const OpCodeType OP_code value after all defined opcodes
//FIRST_UNDEFINED_OP_VALUE
// template matching params
OpCode.OP_PUBKEYHASH = new types_1.OpCodeType('fd');
OpCode.OP_PUBKEY = new types_1.OpCodeType('fe');
OpCode.OP_INVALIDOPCODE = new types_1.OpCodeType('ff');
/**

@@ -274,5 +353,11 @@ * @category Standard Contracts

// convert signed integer `n` to unsigned integer of `l` string, in little endian
static toLEUnsigned(n, l) { throw new Error('unimplemented'); }
static toLEUnsigned(n, l) {
let m = num2bin(n, l + 1n);
// remove sign byte
return m.slice(0, len(m) - 2);
}
// convert string to unsigned integer, in sign-magnitude little endian
static fromLEUnsigned(b) { throw new Error('unimplemented'); }
static fromLEUnsigned(bytes) {
return unpack(bytes + (0, types_1.b)('00'));
}
/*

@@ -283,20 +368,60 @@ * VarInt (variable integer) is used to encode fields of variable length in a bitcoin transaction

// read a VarInt field from the beginning of 'b'
static readVarint(buf) { throw new Error('unimplemented'); }
static readVarint(buf) {
let l = 0n;
let ret = (0, types_1.b)('');
let header = buf.slice(0, 2);
if (header == (0, types_1.b)('fd')) {
l = Utils.fromLEUnsigned(buf.slice(2, 6));
ret = buf.slice(6, 6 + Number(l * 2n));
}
else if (header == (0, types_1.b)('fe')) {
l = Utils.fromLEUnsigned(buf.slice(2, 10));
ret = buf.slice(10, 10 + Number(l * 2n));
}
else if (header == (0, types_1.b)('ff')) {
l = Utils.fromLEUnsigned(buf.slice(2, 18));
ret = buf.slice(18, 18 + Number(l * 2n));
}
else {
l = Utils.fromLEUnsigned(buf.slice(0, 2));
ret = buf.slice(2, 2 + Number(l * 2n));
}
return ret;
}
// convert 'b' to a VarInt field, including the preceding length
static writeVarint(buf) { throw new Error('unimplemented'); }
static writeVarint(buf) {
let n = len(buf);
let header = (0, types_1.b)('');
if (n < 0xfd) {
header = Utils.toLEUnsigned(BigInt(n), 1n);
}
else if (n < 0x10000) {
header = (0, types_1.b)('fd') + Utils.toLEUnsigned(BigInt(n), 2n);
}
else if (n < 0x100000000) {
header = (0, types_1.b)('fe') + Utils.toLEUnsigned(BigInt(n), 4n);
}
else if (n < 0x10000000000000000) {
header = (0, types_1.b)('ff') + Utils.toLEUnsigned(BigInt(n), 8n);
}
return header + buf;
}
// build a tx output from its script and satoshi amount
static buildOutput(outputScript, outputSatoshis) { return ""; }
static buildOutput(outputScript, outputSatoshis) {
return num2bin(outputSatoshis, Constants.OutputValueLen) + Utils.writeVarint(outputScript);
}
// build P2PKH script from PubKeyHash
static buildPublicKeyHashScript(pubKeyHash) {
//throw new Error('unimplemented');
return "";
return OpCode.OP_DUP.toString() + OpCode.OP_HASH160.toString() + pack(Constants.PubKeyHashLen /* "OP_PUSHDATA0" */) + pubKeyHash.toString() + OpCode.OP_EQUALVERIFY.toString() + OpCode.OP_CHECKSIG.toString();
}
// build false OPRETURN script from data payload
static buildOpreturnScript(data) { throw new Error('unimplemented'); }
static buildOpreturnScript(data) {
return OpCode.OP_FALSE.toString() + OpCode.OP_RETURN.toString() + Utils.writeVarint(data);
}
}
exports.Utils = Utils;
// number of string to denote output value
Utils.OutputValueLen = 1n;
Utils.OutputValueLen = 8n;
// number of string to denote a public key hash
Utils.PubKeyHashLen = 1n;
Utils.PubKeyHashLen = 20n;
/**

@@ -311,18 +436,50 @@ * @category Standard Contracts

*/
static nVersion(preimage) { throw new Error('unimplemented'); }
static hashPrevouts(preimage) { throw new Error('unimplemented'); }
static hashSequence(preimage) { throw new Error('unimplemented'); }
static outpoint(preimage) { throw new Error('unimplemented'); }
static nVersion(preimage) {
return preimage.toString().slice(0, 8);
}
static hashPrevouts(preimage) {
return preimage.toString().slice(8, 72);
}
static hashSequence(preimage) {
return preimage.toString().slice(36 * 2, 68 * 2);
}
static outpoint(preimage) {
return preimage.toString().slice(68 * 2, 104 * 2);
}
// scriptCode is just scriptPubKey if there is no CODESEPARATOR in the latter
static scriptCode(preimage) { throw new Error('unimplemented'); }
static valueRaw(preimage) { throw new Error('unimplemented'); }
static value(preimage) { return BigInt(preimage.amount); }
static nSequenceRaw(preimage) { throw new Error('unimplemented'); }
static nSequence(preimage) { throw new Error('unimplemented'); }
static scriptCode(preimage) {
return Utils.readVarint(preimage.toString().slice(104 * 2));
}
static valueRaw(preimage) {
let l = len(preimage.toString());
return preimage.toString().slice(l * 2 - 104, l * 2 - 88);
}
static value(preimage) {
return Utils.fromLEUnsigned(SigHash.valueRaw(preimage));
}
static nSequenceRaw(preimage) {
let l = len(preimage.toString());
return preimage.toString().slice(l * 2 - 88, l * 2 - 80);
}
static nSequence(preimage) {
return Utils.fromLEUnsigned(SigHash.nSequenceRaw(preimage));
}
;
static hashOutputs(preimage) { return preimage.hashOutputs; }
static nLocktimeRaw(preimage) { throw new Error('unimplemented'); }
static nLocktime(preimage) { return preimage.nLocktime; }
static hashOutputs(preimage) {
let l = len(preimage.toString());
return preimage.toString().slice(l * 2 - 80, l * 2 - 16);
}
static nLocktimeRaw(preimage) {
let l = len(preimage.toString());
return preimage.toString().slice(l * 2 - 80, l * 2 - 16);
}
static nLocktime(preimage) {
return Utils.fromLEUnsigned(SigHash.nLocktimeRaw(preimage));
}
;
static sigHashType(preimage) { return new types_1.SigHashType(utils_1.DEFAULT_SIGHASH_TYPE); }
static sigHashType(preimage) {
let l = len(preimage.toString());
let sigHashType = Utils.fromLEUnsigned(preimage.toString().slice(l * 2 - 8, l * 2 - 6));
return new types_1.SigHashType(Number(sigHashType));
}
}

@@ -341,10 +498,67 @@ exports.SigHash = SigHash;

constructor(buf) {
this.buf = buf;
this.pos = 0n;
}
eof() { throw new Error('unimplemented'); }
readstring() { throw new Error('unimplemented'); }
readBool() { throw new Error('unimplemented'); }
readInt() { throw new Error('unimplemented'); }
static getStateStart(scriptCode) { throw new Error('unimplemented'); }
eof() {
return this.pos >= len(this.buf);
}
readBytes() {
let l = 0n;
let buf = this.buf;
let ret = (0, types_1.b)('');
let header = unpack(buf.slice(Number(this.pos * 2n), Number((this.pos + 1n) * 2n)));
this.pos++;
if (header < 0x4cn) {
l = header;
ret = buf.slice(Number(this.pos * 2n), Number((this.pos + l) * 2n));
}
else if (header == 0x4cn) {
l = Utils.fromLEUnsigned(buf.slice(Number(this.pos * 2n), Number((this.pos + 1n) * 2n)));
this.pos += 1n;
ret = buf.slice(Number(this.pos * 2n), Number((this.pos + l) * 2n));
}
else if (header == 0x4dn) {
l = Utils.fromLEUnsigned(buf.slice(Number(this.pos * 2n), Number((this.pos + 2n) * 2n)));
this.pos += 2n;
ret = buf.slice(Number(this.pos * 2n), Number((this.pos + l) * 2n));
}
else if (header == 0x4en) {
l = Utils.fromLEUnsigned(buf.slice(Number(this.pos * 2n), Number((this.pos + 4n) * 2n)));
this.pos += 4n;
ret = buf.slice(Number(this.pos * 2n), Number((this.pos + l) * 2n));
}
else {
// shall not reach here
assert(false);
}
this.pos += l;
return ret;
}
readBool() {
let buf = this.buf.slice(Number(this.pos * 2n), Number((this.pos + 1n) * 2n));
this.pos++;
return (0, types_1.b)('00') != buf;
}
readInt() {
return unpack(this.readBytes());
}
static getStateStart(scriptCode) {
// locking script: code + opreturn + data(state + state_len + version)
let scriptLen = BigInt(len(scriptCode));
// read state length
let start = scriptLen - VarIntReader.StateLen - VarIntReader.VersionLen;
let end = scriptLen - VarIntReader.VersionLen;
let lb = scriptCode.slice(Number(start), Number(end));
let stateLen = unpack(lb);
// TODO: check version is as expected
return scriptLen - stateLen - VarIntReader.StateLen - VarIntReader.VersionLen;
}
}
exports.VarIntReader = VarIntReader;
// fixed number of string to denote length of serialized state
VarIntReader.StateLen = 4n;
// fixed number of string to denote version
VarIntReader.VersionLen = 1n;
// version
VarIntReader.Version = 0n;
/**

@@ -355,8 +569,36 @@ * @category Standard Contracts

// return VarInt encoding
static writestring(buf) { throw new Error('unimplemented'); }
static writeBytes(buf) {
let n = BigInt(len(buf));
let header = (0, types_1.b)('');
if (n < 0x4c) {
header = Utils.toLEUnsigned(n, 1n);
}
else if (n < 0x100) {
header = (0, types_1.b)('4c') + Utils.toLEUnsigned(n, 1n);
}
else if (n < 0x10000) {
header = (0, types_1.b)('4d') + Utils.toLEUnsigned(n, 2n);
}
else if (n < 0x100000000) {
header = (0, types_1.b)('4e') + Utils.toLEUnsigned(n, 4n);
}
else {
// shall not reach here
assert(false);
}
return header + types_1.b;
}
// uses fixed 1 byte to represent a boolean, plus length
static writeBool(x) { throw new Error('unimplemented'); }
static writeBool(x) {
return x ? (0, types_1.b)('01') : (0, types_1.b)('00');
}
// bigint is little endian
static writeInt(x) { throw new Error('unimplemented'); }
static serializeState(stateBuf) { throw new Error('unimplemented'); }
static writeInt(x) {
return VarIntWriter.writeBytes(x == 0n ? (0, types_1.b)('00') : pack(x));
}
static serializeState(stateBuf) {
// locking script: code + opreturn + data(state + state_len + version)
let lenBuf = num2bin(BigInt(len(stateBuf)), VarIntReader.StateLen);
return stateBuf + lenBuf + num2bin(VarIntReader.Version, VarIntReader.VersionLen);
}
}

@@ -398,2 +640,14 @@ exports.VarIntWriter = VarIntWriter;

exports.Constants = Constants;
// number of string to denote input sequence
Constants.InputSeqLen = 4n;
// number of string to denote output value
Constants.OutputValueLen = 8n;
// number of string to denote a public key (compressed)
Constants.PubKeyLen = 33n;
// number of string to denote a public key hash
Constants.PubKeyHashLen = 20n;
// number of string to denote a tx id
Constants.TxIdLen = 32n;
// number of string to denote a outpoint
Constants.OutpointLen = 36n;
//# sourceMappingURL=functions.js.map
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;
export declare function b(hexStr: string): string;
/**

@@ -27,3 +15,3 @@ * The auto keyword specifies that the type of the variable, of basic type, declared will be automatically deducted from its initializer.

*/
declare type Grow<T, A extends Array<T>> = ((x: T, ...xs: A) => void) extends ((...a: infer X) => void) ? X : never;
declare type Grow<T, A extends Array<T>> = ((_arrElem: T, ...xs: A) => void) extends ((...a: infer X) => void) ? X : never;
/**

@@ -49,3 +37,3 @@ * @ignore

*/
export declare type FixedArray<T, N extends number> = GrowToSize<T, [], N>;
export declare type FixedArray<T, LEN extends number> = GrowToSize<T, [], LEN>;
/**

@@ -56,2 +44,3 @@ * a public key type.

export declare class PubKey extends PubKey_ {
toString(format?: 'hex'): string;
}

@@ -63,2 +52,3 @@ /**

export declare class Sig extends Sig_ {
toString(format?: 'hex'): string;
}

@@ -70,2 +60,3 @@ /**

export declare class SigHashPreimage extends SigHashPreimage_ {
toString(format?: 'hex'): string;
}

@@ -77,2 +68,3 @@ /**

export declare class Ripemd160 extends Ripemd160_ {
toString(format?: 'hex'): string;
}

@@ -84,2 +76,3 @@ /**

export declare class PubKeyHash extends PubKeyHash_ {
toString(format?: 'hex'): string;
}

@@ -91,2 +84,3 @@ /**

export declare class Sha256 extends Sha256_ {
toString(format?: 'hex'): string;
}

@@ -98,2 +92,3 @@ /**

export declare class Sha1 extends Sha1_ {
toString(format?: 'hex'): string;
}

@@ -105,2 +100,3 @@ /**

export declare class SigHashType extends SigHashType_ {
toString(format?: 'hex'): string;
}

@@ -112,2 +108,3 @@ /**

export declare class OpCodeType extends OpCodeType_ {
toString(format?: 'hex'): string;
}

@@ -119,3 +116,4 @@ /**

export declare class PrivKey extends PrivKey_ {
toString(format?: 'hex'): string;
}
export {};

@@ -19,2 +19,5 @@ "use strict";

class PubKey extends scryptlib_1.PubKey {
toString(format = 'hex') {
return this.serialize();
}
}

@@ -27,2 +30,5 @@ exports.PubKey = PubKey;

class Sig extends scryptlib_1.Sig {
toString(format = 'hex') {
return this.serialize();
}
}

@@ -35,2 +41,5 @@ exports.Sig = Sig;

class SigHashPreimage extends scryptlib_1.SigHashPreimage {
toString(format = 'hex') {
return this.serialize();
}
}

@@ -43,2 +52,5 @@ exports.SigHashPreimage = SigHashPreimage;

class Ripemd160 extends scryptlib_1.Ripemd160 {
toString(format = 'hex') {
return this.serialize();
}
}

@@ -51,2 +63,5 @@ exports.Ripemd160 = Ripemd160;

class PubKeyHash extends scryptlib_1.PubKeyHash {
toString(format = 'hex') {
return this.serialize();
}
}

@@ -59,2 +74,5 @@ exports.PubKeyHash = PubKeyHash;

class Sha256 extends scryptlib_1.Sha256 {
toString(format = 'hex') {
return this.serialize();
}
}

@@ -67,2 +85,5 @@ exports.Sha256 = Sha256;

class Sha1 extends scryptlib_1.Sha1 {
toString(format = 'hex') {
return this.serialize();
}
}

@@ -75,2 +96,5 @@ exports.Sha1 = Sha1;

class SigHashType extends scryptlib_1.SigHashType {
toString(format = 'hex') {
return this.serialize();
}
}

@@ -83,2 +107,5 @@ exports.SigHashType = SigHashType;

class OpCodeType extends scryptlib_1.OpCodeType {
toString(format = 'hex') {
return this.serialize();
}
}

@@ -91,4 +118,7 @@ exports.OpCodeType = OpCodeType;

class PrivKey extends scryptlib_1.PrivKey {
toString(format = 'hex') {
return this.serialize();
}
}
exports.PrivKey = PrivKey;
//# sourceMappingURL=types.js.map
/// <reference types="bsv" />
import "reflect-metadata";
import { AbstractContract, VerifyResult, bsv } from "scryptlib";
import { Script } from "scryptlib/dist/abi";
import { SigHashPreimage, SigHashType } from "./builtins/types";
import { AbstractContract, VerifyResult, bsv, ContractDescription } from "scryptlib";
import { SigHashPreimage, SigHashType, Sig, PubKey } from "./builtins/types";
/**

@@ -31,2 +30,3 @@ * The input point where the smart contract is spent by the transaction

export declare class SmartContract {
flags: number;
lockingTo?: TxOutputRef;

@@ -37,7 +37,8 @@ unlockingFrom?: TxInputRef;

static compile(): Promise<void>;
static loadDesc(desc: ContractDescription): Promise<void>;
private static _getScryptFile;
constructor(...args: any[]);
verify(entryMethodInvoking: (self: typeof this) => void): VerifyResult;
getUnlockingScript(callPub: (self: typeof this) => void): Script;
get lockingScript(): Script;
getUnlockingScript(callPub: (self: typeof this) => void): bsv.Script;
get lockingScript(): bsv.Script;
clone(): this;

@@ -47,2 +48,5 @@ markAsGenesis(): void;

protected getStateScript(): string;
protected checkSig(signature: Sig, publickey: PubKey): boolean;
checkPubkeyEncoding(publickey: PubKey): boolean;
checkSignatureEncoding(signature: Sig): boolean;
protected updateState(preimage: SigHashPreimage, balance: bigint): boolean;

@@ -49,0 +53,0 @@ private delegateCall;

@@ -48,2 +48,4 @@ "use strict";

const lodash_clonedeep_1 = __importDefault(require("lodash.clonedeep"));
const md5 = require("md5");
const utils_1 = require("scryptlib/dist/utils");
/**

@@ -61,2 +63,3 @@ * The main contract class. To write a contract, extend this class as such:

constructor(...args) {
this.flags = utils_1.DEFAULT_FLAGS;
if (!SmartContract.DelegateClazz) {

@@ -86,2 +89,11 @@ throw new Error(`'${this.constructor.name}.compile' should be called before initilizing any instance!`);

}
static async loadDesc(desc) {
let filePath = this._getScryptFile();
const sourceContent = fs.readFileSync(filePath, 'utf8');
const md5Hash = md5(sourceContent);
if (this.name !== desc.contract || desc.md5 !== md5Hash) {
throw new Error(`Contract description file cannot match contract \`${this.name}\`!`);
}
SmartContract.DelegateClazz = (0, scryptlib_1.buildContractClass)(desc);
}
static _getScryptFile() {

@@ -160,2 +172,60 @@ let scryptFile = Reflect.getMetadata("scrypt:file", this);

}
checkSig(signature, publickey) {
if (!this.checkSignatureEncoding(signature) || !this.checkPubkeyEncoding(publickey)) {
return false;
}
let fSuccess = false;
try {
const sig = scryptlib_1.bsv.crypto.Signature.fromTxFormat(Buffer.from(signature.toString(), 'hex'));
const pubkey = scryptlib_1.bsv.PublicKey.fromBuffer(Buffer.from(publickey.toString(), 'hex'), false);
const tx = this.txContext.tx;
const inputIndex = this.txContext.inputIndex || 0;
const inputSatoshis = this.txContext.inputSatoshis || 100000;
fSuccess = tx.verifySignature(sig, pubkey, inputIndex, this.lockingScript, inputSatoshis, this.flags);
}
catch (e) {
// invalid sig or pubkey
fSuccess = false;
}
return fSuccess;
}
checkPubkeyEncoding(publickey) {
if ((this.flags & scryptlib_1.bsv.Script.Interpreter.SCRIPT_VERIFY_STRICTENC) !== 0 && !scryptlib_1.bsv.PublicKey.isValid(publickey.toString())) {
return false;
}
return true;
}
checkSignatureEncoding(signature) {
var buf = Buffer.from(signature.toString(), 'hex');
var sig;
// Empty signature. Not strictly DER encoded, but allowed to provide a
// compact way to provide an invalid signature for use with CHECK(MULTI)SIG
if (buf.length === 0) {
return true;
}
if ((this.flags & (scryptlib_1.bsv.Script.Interpreter.SCRIPT_VERIFY_DERSIG | scryptlib_1.bsv.Script.Interpreter.SCRIPT_VERIFY_LOW_S | scryptlib_1.bsv.Script.Interpreter.SCRIPT_VERIFY_STRICTENC)) !== 0 && !scryptlib_1.bsv.crypto.Signature.isTxDER(buf)) {
return false;
}
else if ((this.flags & scryptlib_1.bsv.Script.Interpreter.SCRIPT_VERIFY_LOW_S) !== 0) {
sig = scryptlib_1.bsv.crypto.Signature.fromTxFormat(buf);
if (!sig.hasLowS()) {
return false;
}
}
else if ((this.flags & scryptlib_1.bsv.Script.Interpreter.SCRIPT_VERIFY_STRICTENC) !== 0) {
sig = scryptlib_1.bsv.crypto.Signature.fromTxFormat(buf);
if (!sig.hasDefinedHashtype()) {
return false;
}
if (!(this.flags & scryptlib_1.bsv.Script.Interpreter.SCRIPT_ENABLE_SIGHASH_FORKID) &&
(sig.nhashtype & scryptlib_1.bsv.crypto.Signature.SIGHASH_FORKID)) {
return false;
}
if ((this.flags & scryptlib_1.bsv.Script.Interpreter.SCRIPT_ENABLE_SIGHASH_FORKID) &&
!(sig.nhashtype & scryptlib_1.bsv.crypto.Signature.SIGHASH_FORKID)) {
return false;
}
}
return true;
}
updateState(preimage, balance) {

@@ -198,2 +268,8 @@ // require(Tx.checkPreimageSigHashType(preimage, SigHash.ALL | SigHash.FORKID));

__metadata("design:type", Function),
__metadata("design:paramtypes", [types_1.Sig, types_1.PubKey]),
__metadata("design:returntype", Boolean)
], SmartContract.prototype, "checkSig", null);
__decorate([
decorators_1.method,
__metadata("design:type", Function),
__metadata("design:paramtypes", [types_1.SigHashPreimage, BigInt]),

@@ -200,0 +276,0 @@ __metadata("design:returntype", Boolean)

@@ -5,2 +5,2 @@ import * as ts from 'typescript';

*/
export default function transformProgram(program: ts.Program, host: ts.CompilerHost | undefined, pluginOptions: ts.PluginConfig, { ts: tsInstance }: ts.ProgramTransformerExtras): ts.Program;
export default function (program: ts.Program, pluginOptions: any): (ctx: ts.TransformationContext) => (sourceFile: ts.SourceFile) => ts.SourceFile;

@@ -33,6 +33,6 @@ "use strict";

*/
function transformProgram(program, host, pluginOptions, { ts: tsInstance }) {
const compilerOptions = program.getCompilerOptions();
const compilerHost = getPatchedHost(host, tsInstance, compilerOptions);
const rootFileNames = program.getRootFileNames().map(path.normalize);
function default_1(program, pluginOptions) {
if (pluginOptions.debug) {
console.log('transformer loaded with options:', pluginOptions, '\n');
}
let tsconfigDir = process.env['TS_NODE_PROJECT'] ? path.dirname(process.env['TS_NODE_PROJECT']) : '.';

@@ -53,85 +53,53 @@ tsconfigDir = path.isAbsolute(tsconfigDir) ? tsconfigDir : path.join(program.getCurrentDirectory(), tsconfigDir);

let indexer = new idexer_1.Indexer({ tsconfigDir, scryptOutDir });
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 (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;
}
return node;
return ts.visitEachChild(node, visitor, ctx);
}
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);
ts.visitEachChild(sourceFile, visitor, ctx);
const statements = [];
sourceFile.statements.forEach(s => {
statements.push(s);
if (s.kind === ts.SyntaxKind.ClassDeclaration) {
metaDataDefs.forEach(e => statements.push(ts.factory.createExpressionStatement(e)));
}
});
return ts.factory.updateSourceFile(sourceFile,
// appends meta data definitions to the end of source file.
statements, sourceFile.isDeclarationFile, sourceFile.referencedFiles, sourceFile.typeReferenceDirectives, sourceFile.hasNoDefaultLib, sourceFile.libReferenceDirectives);
};
};
}
exports.default = default_1;
//# sourceMappingURL=transformer.js.map

@@ -8,3 +8,3 @@ import * as ts from 'typescript';

sourceMap: number[][];
constructor(prefixTabs?: number, currentCol?: number, codeLines?: string, sourceMap?: never[]);
constructor(prefixTabs?: number, currentCol?: number, codeLines?: string, sourceMap?: any[]);
copy(): EmittedLine;

@@ -64,4 +64,4 @@ findByCol(col: number): number[];

private transformExpression;
private transformArrayType;
private transformTypeNode;
private transformEnclosingTypeNode;
private transformType;
private transformModifiers;

@@ -68,0 +68,0 @@ private transformImports;

@@ -196,4 +196,4 @@ "use strict";

coordinates: {
line: sourcemap[2],
character: sourcemap[3]
line: sourcemap ? sourcemap[2] : -1,
character: sourcemap ? sourcemap[3] : -1,
}

@@ -289,3 +289,3 @@ }));

transformPropertySignature(node, toSection) {
return this.transformTypeNode(node.type, toSection, this.getCoordinates(node.type.getStart()))
return this.transformEnclosingTypeNode(node.type, toSection)
.append(` ${node.name.getText()}`, this.getCoordinates(node.name.getStart()))

@@ -312,3 +312,3 @@ .append(';');

.appendWith(this, toSec => {
return this.transformTypeNode(node.type, toSec, this.getCoordinates(node.type?.getStart()));
return this.transformEnclosingTypeNode(node.type, toSec);
})

@@ -386,3 +386,3 @@ .append(` ${node.name.getText()}`, this.getCoordinates(node.name.getStart()));

.appendWith(this, toSec => {
return this.transformTypeNode(node.type, toSec, this.getCoordinates(node.type.getStart()));
return this.transformEnclosingTypeNode(node.type, toSec);
})

@@ -401,3 +401,3 @@ .append(' ');

.appendWith(this, toSec => {
return this.transformTypeNode(node.type, toSec, this.getCoordinates(node.type.getStart()));
return this.transformEnclosingTypeNode(node.type, toSec);
})

@@ -427,6 +427,3 @@ .append(' ')

const s = node;
if (s.expression.kind === ts.SyntaxKind.CallExpression && /^loop(\s*)\([\w.\s]*\)/.test(s.expression.getText())) {
return this.transformExpression(s.expression, toSection);
}
else if (s.expression.kind === ts.SyntaxKind.CallExpression && /^super(\s*)\((.+?)\)/.test(s.expression.getText())) {
if (s.expression.kind === ts.SyntaxKind.CallExpression && /^super(\s*)\(.*\)/.test(s.expression.getText())) {
return toSection;

@@ -442,15 +439,12 @@ }

if (stmt.declarationList.declarations.length > 1) {
throw new TranspileError(`untransfomable statement kind ${node.kind}`, this.getLocation(node.pos));
throw new TranspileError(`untransfomable statement: ${node.getText()}`, this.getLocation(node.pos));
}
const d = stmt.declarationList.declarations[0];
if (!d.initializer) {
throw new TranspileError(`untransfomable statement kind ${node.kind}`, this.getLocation(node.getStart()));
throw new TranspileError(`untransfomable statement: ${node.getText()}`, this.getLocation(node.getStart()));
}
if (!d.type) {
throw new TranspileError(`untransfomable statement kind ${node.kind}`, this.getLocation(node.getStart()));
}
return toSection
.appendWith(this, toSec => {
return this.transformTypeNode(d.type, toSec, this.getCoordinates(d.type.getStart()));
})
// use `d.type` as type context node if it exists,
// otherwise use `d.initializer` to provide type context so we can leverage the type inference.
const typeCtxNode = d.type || d.initializer;
return this.transformEnclosingTypeNode(typeCtxNode, toSection)
.append(` ${d.name.getText()}`, this.getCoordinates(d.name.getStart()))

@@ -491,4 +485,77 @@ .append(" = ")

}
case (ts.SyntaxKind.ForStatement): {
const s = node;
let inductionVar = undefined;
if (s.initializer?.kind === ts.SyntaxKind.VariableDeclarationList) {
let ivDeclare = s.initializer.declarations[0];
// initializer expr must match `let $i = 0;`
if (ivDeclare.initializer?.getText() === "0") {
inductionVar = ivDeclare.name;
}
}
if (!inductionVar) {
throw new TranspileError(`for statement in @method should have induction variable declaration as: 'for(let $i = 0; ...; ...)'`, this.getLocation(s.pos));
}
let loopCount = undefined;
if (s.condition?.kind === ts.SyntaxKind.BinaryExpression) {
let cond = s.condition;
// condition expr must match `$i < $constNum;`
if (cond.left.getText() === inductionVar.getText()
&& cond.operatorToken.kind === ts.SyntaxKind.LessThanToken) {
const condRight = cond.right;
if (condRight.kind === ts.SyntaxKind.NumericLiteral) {
// when `$constNum` is a number literal
loopCount = condRight;
}
else {
// when `$constNum` might refer to a CTC
const symbol = this._checker.getSymbolAtLocation(condRight);
// for const variable like `const N = xxx;`
if (symbol.valueDeclaration?.kind === ts.SyntaxKind.VariableDeclaration
&& ts.getCombinedNodeFlags(symbol.valueDeclaration) === ts.NodeFlags.Const) {
const valueDec = symbol.valueDeclaration;
const initValue = valueDec.initializer;
if (initValue?.kind === ts.SyntaxKind.NumericLiteral) {
loopCount = initValue;
}
}
// for readonly property like `readonly N = xxx;`
if (symbol.valueDeclaration?.kind === ts.SyntaxKind.PropertyDeclaration
&& ts.getModifiers(symbol.valueDeclaration)
.map(m => m.kind).includes(ts.SyntaxKind.ReadonlyKeyword)) {
const valueDec = symbol.valueDeclaration;
const initValue = valueDec.initializer;
if (initValue?.kind === ts.SyntaxKind.NumericLiteral) {
loopCount = initValue;
}
}
}
}
}
if (!loopCount) {
throw new TranspileError(`for statement in @method should have condition expression as: 'for(...; $i < $constNum; ...)'`, this.getLocation(s.pos));
}
let postIncIV = false;
// incrementor expr must match `$i++`
if (s.incrementor?.kind === ts.SyntaxKind.PostfixUnaryExpression) {
let inc = s.incrementor;
if (inc.operator === ts.SyntaxKind.PlusPlusToken
&& inc.operand.getText() === inductionVar.getText()) {
postIncIV = true;
}
}
if (!postIncIV) {
throw new TranspileError(`for statement in @method should have incrementor expression as: 'for(...; ...; $i++)'`, this.getLocation(s.pos));
}
return toSection
.append('loop (')
.append(loopCount.getText(), this.getCoordinates(loopCount.pos))
.append(') : ')
.append(`${inductionVar.getText()} `, this.getCoordinates(inductionVar.pos))
.appendWith(this, toSec => {
return this.transformStatement(s.statement, toSec);
});
}
default: {
throw new TranspileError(`untransfomable statement kind ${node.kind}`, this.getLocation(node.pos));
throw new TranspileError(`untransfomable statement ${node.getText()}`, this.getLocation(node.pos));
}

@@ -572,3 +639,2 @@ }

let e = node;
let isLoop = e.expression.kind === ts.SyntaxKind.CallExpression && e.expression.expression.getText() === 'loop';
let isSlice = e.expression.kind === ts.SyntaxKind.PropertyAccessExpression && e.expression.name.getText() === 'slice';

@@ -579,3 +645,3 @@ let isToString = e.expression.kind === ts.SyntaxKind.PropertyAccessExpression && e.expression.name.getText() === 'toString';

let isBytesLiteral = e.expression.kind === ts.SyntaxKind.Identifier && e.expression.getText() === 'b';
const isStillCallExpr = !isLoop && !isSerialize && !isToString && !isBytesLiteral && !isNumberConvert;
const isStillCallExpr = !isSerialize && !isToString && !isBytesLiteral && !isNumberConvert;
// transform the callee's identifier

@@ -587,6 +653,2 @@ toSection.appendWith(this, toSec => {

}
else if (e.expression.getText() === "loop") {
toSec.append("loop", srcLoc);
return toSec;
}
else if (isNumberConvert) {

@@ -683,9 +745,9 @@ return toSec;

}
else if (name === "ALL_ANYONECANPAY_FORKID") {
else if (name === "ANYONECANPAY_ALL_FORKID") {
toSection.append("SigHash.ALL | SigHash.ANYONECANPAY | SigHash.FORKID", this.getCoordinates(e.name.getStart()));
}
else if (name === "NONE_ANYONECANPAY_ORKID") {
else if (name === "ANYONECANPAY_NONE_FORKID") {
toSection.append("SigHash.NONE | SigHash.ANYONECANPAY | SigHash.FORKID", this.getCoordinates(e.name.getStart()));
}
else if (name === "SINGLE_ANYONECANPAY_FORKID") {
else if (name === "ANYONECANPAY_SINGLE_FORKID") {
toSection.append("SigHash.SINGLE | SigHash.ANYONECANPAY | SigHash.FORKID", this.getCoordinates(e.name.getStart()));

@@ -803,9 +865,2 @@ }

}
case (ts.SyntaxKind.TaggedTemplateExpression): {
const e = node;
if (e.tag.getText() == "b") {
toSection.append(`b'${e.template.getText().replaceAll("`", "")}'`, this.getCoordinates(node.getStart()));
}
break;
}
case (ts.SyntaxKind.ParenthesizedExpression): {

@@ -844,152 +899,112 @@ const e = node;

}
transformArrayType(tnode) {
const scryptType = (0, utils_1.getBuildInType)(tnode.getText());
if (scryptType) {
return [scryptType, []];
}
else {
let typeReferenceNode = tnode;
let typeName = typeReferenceNode.typeName.getText();
let type = this._checker.getTypeFromTypeNode(tnode);
let typeSymbol = type.getSymbol();
if (typeSymbol) {
let symbolFile = this.findDeclarationFile(typeSymbol);
if (symbolFile === this._srcFile) {
this._localTypeSymbols.set(typeName, typeSymbol);
// transform the type enclosed in the node.
transformEnclosingTypeNode(node, toSection) {
const coordinates = this.getCoordinates(node.pos);
const type = this._checker.getTypeAtLocation(node);
return this.transformType(type, node.getText(), coordinates, toSection);
}
// `typeStrCtx` is the type's literal name or the text of the node which encloses the type.
transformType(type, typeStrCtx, coordinates, toSection) {
switch (type.flags) {
case (ts.TypeFlags.Union + ts.TypeFlags.Boolean): // This is the real internal type of `boolean`, it's a union of `true` and `false`
case ts.TypeFlags.BooleanLiteral:
case ts.TypeFlags.Boolean: {
return toSection.append("bool", coordinates);
}
case ts.TypeFlags.StringLiteral:
case ts.TypeFlags.String: {
return toSection.append("bytes", coordinates);
}
case ts.TypeFlags.NumberLiteral:
case ts.TypeFlags.Number:
case ts.TypeFlags.BigIntLiteral:
case ts.TypeFlags.BigInt: {
return toSection.append("int", coordinates);
}
case ts.TypeFlags.Object: {
const typeString = type.symbol === undefined
? this._checker.typeToString(type, undefined, ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope)
: (type.aliasSymbol === undefined ? type.symbol.escapedName : type.aliasSymbol.escapedName).toString();
// for inferred array type, like `x = [1, 2]`
if (typeString === "Array") {
throw new TranspileError(`untransformable \`Array\` type, please used \`FixedArray\` type instead.`, { fileName: this._srcFile.fileName, coordinates });
}
else {
this._importedTypeSymbols.set(typeName, typeSymbol);
// for declared object literal type, like `x : {prop: number}`
if (typeString === "__type") {
throw new TranspileError(`untransformable literal object type for: '${typeStrCtx}'`, { fileName: this._srcFile.fileName, coordinates });
}
}
switch (type.flags) {
case ts.TypeFlags.String: {
return ["bytes", []];
// for inferred object literal type, like `x = {prop: 1}`
if (typeString === "__object") {
throw new TranspileError(`untransformable literal object type for: '${typeStrCtx}'`, { fileName: this._srcFile.fileName, coordinates });
}
case ts.TypeFlags.Number: {
return ["int", []];
// for bigint & number wrapper
if (typeString === "BigInt" || typeString === "Number") {
return toSection.append('int', coordinates);
}
case ts.TypeFlags.BigInt: {
return ["int", []];
// for string wrapper
if (typeString === "String") {
return toSection.append('bytes', coordinates);
}
case ts.TypeFlags.Object: {
if (typeName === "FixedArray") {
const args = typeReferenceNode.typeArguments || [];
let tn = "";
let dimensionals = [];
args.forEach((arg) => {
if (arg.kind === ts.SyntaxKind.BigIntKeyword) {
tn = "int";
}
else if (arg.kind === ts.SyntaxKind.BooleanKeyword) {
tn = "bool";
}
else if (arg.kind === ts.SyntaxKind.TypeReference) {
const [t, d] = this.transformArrayType(arg);
tn = t;
dimensionals = d;
}
else if (arg.kind === ts.SyntaxKind.LiteralType) {
dimensionals.push(arg.getText());
}
});
return [tn, dimensionals];
}
else {
return [typeName, []];
}
// for boolean wrapper
if (typeString === "Boolean") {
return toSection.append('bool', coordinates);
}
default: {
throw new TranspileError(`untransfor111mable type ${type.flags}`, { fileName: this._srcFile.fileName, coordinates: this.getCoordinates(tnode.pos) });
if (typeString.match(/\[_arrElem: .+\]/)) {
// built-in type `FixedArray` goes here.
const getBaseElemType = (typeRef) => {
if (typeRef.typeArguments?.length > 0) {
let innerTypeRef = typeRef.typeArguments[0];
return getBaseElemType(innerTypeRef);
}
else {
return typeRef;
}
};
const baseElemType = getBaseElemType(type);
// touchedTypeSymbol = baseElemType.symbol;
// `typeString` may looks like: `[_arrElem: [_arrElem: t], ...]`
const baseTypeReg = /_arrElem: ([^\[,\]]+)/;
// transform the `typeString` to an array object like: [[0, 0], ...], so we can get its lengths for each nested level
const arrStructure = JSON.parse(typeString
.replaceAll(new RegExp(baseTypeReg, 'g'), "0")
.replaceAll('_arrElem:', ''));
const getNestedArrayLens = (arr) => {
let arrLen = arr.length;
if (arr[0] instanceof Array) {
return [arrLen].concat(getNestedArrayLens(arr[0]));
}
else {
return [arrLen];
}
};
const arrLens = getNestedArrayLens(arrStructure);
this.transformType(baseElemType, typeStrCtx, coordinates, toSection)
.append(`${arrLens.map(i => '[' + i + ']').join('')}`, coordinates);
}
}
}
}
transformTypeNode(tnode, toSection, coordinates) {
const scryptType = (0, utils_1.getBuildInType)(tnode.getText());
if (scryptType) {
toSection.append(scryptType, coordinates);
}
else {
let typeReferenceNode = tnode;
let typeName = typeReferenceNode.typeName.getText();
let type = this._checker.getTypeFromTypeNode(tnode);
let typeSymbol = type.getSymbol();
if (typeSymbol) {
let symbolFile = this.findDeclarationFile(typeSymbol);
if (symbolFile === this._srcFile) {
this._localTypeSymbols.set(typeName, typeSymbol);
}
else {
this._importedTypeSymbols.set(typeName, typeSymbol);
}
}
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);
// all user defined or std types go here.
toSection.append(typeString === "PubKeyHash" ? "Ripemd160" : typeString, coordinates);
if (type.symbol) {
// record type visting if its symbol exits, for the purpose of `struct` & `import` generations.
let symbolFile = this.findDeclarationFile(type.symbol);
if (symbolFile === this._srcFile) {
this._localTypeSymbols.set(typeString, type.symbol);
}
else {
this._importedTypeSymbols.set(typeString, type.symbol);
}
}
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 });
}
break;
}
case ts.TypeFlags.Any: {
toSection.append('auto', coordinates);
break;
}
default: {
throw new TranspileError(`untransformable type in '${typeStrCtx}', flag: ${type.flags}`, { fileName: this._srcFile.fileName, coordinates });
}
}
return toSection;
}
// 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) {

@@ -1036,3 +1051,3 @@ let modifiers = ts.getModifiers(node);

let moduleUsed = false;
moduleSymbol.exports.forEach((e) => {
(moduleSymbol?.exports || []).forEach((e) => {
// mark modules as used if their exported symbols were found used in contracts.

@@ -1039,0 +1054,0 @@ moduleUsed ||= usedSymbols.includes(e);

{
"name": "scrypt-ts",
"version": "0.1.3-alpha",
"version": "0.1.3-alpha.1",
"description": "A toolset for building sCrypt smart contract applications on Bitcoin SV network written in typescript.",

@@ -5,0 +5,0 @@ "main": "dist/index.js",

# scrypt-ts
`scrypt-ts` is a Typescript framework to write smart contracts on Bitcoin SV.
`scrypt-ts` is a Typescript framework to write smart contracts on Bitcoin SV.
# Installation
Use this command to install `scrypt-ts` to your project:
`npm install -S scrypt-ts`
# Setup
## 1. Update `tsconfig.json`
`scrypt-ts` depends on [ts-patch](https://github.com/nonara/ts-patch) to provide a custom plugin support for typescript. So first we need to add `scrypt-ts` plugin and enable decorators in `tsconfig.json` file like:
```json
{
"compilerOptions": {
...
"experimentalDecorators": true,
"plugins": [
{
"transform": "scrypt-ts/dist/transformer", // Required
"transformProgram": true, // Required
"outDir": "./scrypt", // Optional, define the auto-generated `.scrypt` files folder
"debug": false // Optional, enable/disable debug log in console.
}
]
}
}
```
**Note**: Currently there is an issue with typescript version `4.9.x`, so make sure to lock typescript version to `4.8.4`:
## 2. Download sCrypt compiler
`scrypt-ts` also depends on the native sCrypt compiler which could be downloaded with command:
```
curl -Ls https://scrypt.io/setup | sh -s --
```
That's all, you're ready to go!
# Usage
## Write Contract
A contract can be written as a class that extends the `SmartContract` base, a simple example could be like this:
```ts
import { SmartContract, method, prop, assert } from "scrypt-ts";
class Demo extends SmartContract {
@prop()
x: bigint;
constructor(x: bigint) {
super(x);
this.x = x;
}
@method
public unlock(x: bigint) {
assert(this.add(this.x, 1n) === x);
}
@method
add(x0: bigint, x1:bigint) : bigint {
return x0 + x1;
}
}
```
### Decorator: `@prop(state=false)`
Use this decorator on class properties to mark them as contract properties, which means the values would be stored on chain within [tx](https://wiki.bitcoinsv.io/index.php/Bitcoin_Transactions).
This decorator can take a boolean parameter, which indicates whether it can be updated later. If it's `true`, the property is so called a `stateful` property and its value stored on chain can be updated between contract calls; otherwise, its value can not be changed since the contract deploy.
### Decorator: `@method`
Use this decorator on class methods to mark them as contract methods. The logic implemented in these methods would be serialized into [tx](https://wiki.bitcoinsv.io/index.php/Bitcoin_Transactions) and be executed on chain.
The class methods decorated by `@method` have some special requirements / restrains that should be followed:
* Within these methods, only functions provided as built-ins from `scrypt-ts` or methods also decorated by `@method` can be called; Similarly, only the properties decorated by `@prop` can be visited.
* With `public` modifier, a method is marked as an entry method that could be called outside the contract class, especially during a tx building process. The main purpose of these methods is to validate / verify / check assertions for its input parameters according to its `@prop` decorated properties. The return value must be `void`.
* Without a `public` modifier, a method is kind of an inner function usually be called within the contract class. It can return any valid types described later.
### Types
The types can be used in `@prop` and `@method` are restricted to these kinds:
* Basic types: `boolean` / `string` / `bigint`;
*Note*: the type `number` is not allowed in `@prop` because it may cause precision issues, and it's recommended to be used only in a few cases.
* Types composed by the basic types at the end level, for example like:
```ts
type ST = {
x: bigint;
}
interface ST1 {
x: ST;
y: string;
}
```
* Array types **must** be the built-in version of `FixedArray`, which has a compile time constant declared as its length, for example like:
```ts
let aaa: FixedArray<bigint, 3> = [1n, 3n, 3n];
// 2d array
let abb: FixedArray<FixedArray<bigint, 2>, 3> = [[1n, 3n], [1n, 3n], [1n, 3n]];
```
* Other `SmartContract` subclasses provided as libraries.
### Statements
There are also some other restraints / rules on the statemets that could be used within the `@method`s besides the previously mentioned.
#### `for` statement
Because of the underlaying limitation of `loop` implemetion on Bitcoin script, one can only use a compile time const number as the loop iterations.
So currently if you want to build a loop inside `@method`s, there is only one restricted version of `for` statement that could be used. It's looks like:
```ts
for(let $i = 0; $i < $constNum; $i++) {
...
}
```
Note that the initial value `0` and the `<` operator and the post unary operator `++` are all unchangeable.
* `$i` can be whatever you named the induction variable;
* `$constNum` should be an expression of a CTC numberic value of the followings:
A number literal like:
```ts
for(let i = 0; i < 5; i++ ) ...
```
Or a `const` variable name like:
```ts
const N = 3;
for(let i = 0; i < N; i++ ) ...
```
Or a `readonly` property name like:
```ts
class X {
static readonly N = 3;
}
for(let i = 0; i < X.N; i++ ) ...
```
#### `console.log` statement
As descirbed before, all the javascript/typescript built-in functions / global variables are also not allowed to be used in `@method`s, but there are few exceptions.
One exceptional statement is `console.log`, which can be used to output logs for debugging purpose.
## Build
Just run `npx tsc`, or `npm run build` if you have script as below declared in `package.json`:
```json
{
"scripts": {
"build": "tsc"
}
}
```
The `tsc` compiling process may output diagnostic informations in console about the contract class, update the source code if needed.
## Test
You could write tests using tools like `mocha`, for example:
```js
describe('Test SmartContract `Demo`', () => {
before(async () => {
await Demo.compile();
})
it('should pass the public method unit test successfully.', async () => {
let demo = new Demo(1n);
let result = demo.verify(() => demo.unlock(2n));
expect(result.success, result.error).to.eq(true);
expect(() => {
demo.unlock(3n);
}).to.throw(/Execution failed/)
})
})
```
## Deploy and Call
Generally speaking, if you want to deploy or call the contract to BSV network, it takes three steps:
### 1. Build contract instance:
Giving proper parameters to get an up-to-date contract instance.
### 2. Build tx:
Build a tx corresponding to your business logic, especially to set the tx's proper input & output script with contract instance.
For example, to get the locking script, use code like:
```js
instance.lockingScript;
```
To get the unlocking script for a certain `entryMethod`, use code like:
```js
instance.getUnlockingScript(() => {
intance.entryMethod(...);
})
```
### 3. Send tx:
The final step is to sign and send the tx to the network.
Here is an example code to deploy & call a `Demo` contract.
```js
await Demo.compile();
// build contract instance
const demo = new Demo(2n);
const balance = 1000;
// build contract deploy tx
const utxos = await fetchUtxos();
const unsignedDeployTx =
new bsv.Transaction()
.from(utxos)
.addOutput(new bsv.Transaction.Output({
// get the locking script for `demo` instance
script: demo.lockingScript,
satoshis: balance,
}));
// send contract deploy tx
const deployTx = await signAndSend(unsignedDeployTx);
console.log('contract deployed: ', deployTx.id)
// build contract call tx
const unsignedCallTx =
new bsv.Transaction()
.addInput(new bsv.Transaction.Input({
prevTxId: deployTx.id,
outputIndex: outputIdx,
script: demo.getUnlockingScript(() => {
// call public method to get the unlocking script for `demo` instance.
demo.unlock(3n);
}),
output: deployTx.outputs[outputIdx]
}))
.addOutput(
new bsv.Transaction.Output({
script: bsv.Script.buildPublicKeyHashOut(publicKey.toAddress()),
satoshis: balance / 2
})
);
// send contract call tx
const callTx = await signAndSend(unsignedCallTx);
console.log('contract called: ', callTx.id)
```
# Documentation
`scrypt-ts` documentation is available [here]().
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc