scryptlib
Version:
Javascript SDK for integration of Bitcoin SV Smart Contracts written in sCrypt language.
243 lines • 10 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const _1 = require(".");
const deserializer_1 = require("./deserializer");
const internal_1 = require("./internal");
const scryptTypes_1 = require("./scryptTypes");
class Stateful {
static int2hex(n) {
let asm = '';
const num = new _1.bsv.crypto.BN(n);
if (num.eqn(0)) {
asm = '00';
}
else {
asm = num.toSM({ endian: 'little' }).toString('hex');
}
return _1.bsv.Script.fromASM(asm).toHex();
}
static hex2int(hex) {
const s = _1.bsv.Script.fromHex(hex);
const chuck = s.chunks[0];
return (0, _1.bin2num)(chuck.buf.toString('hex'));
}
static bool2hex(b) {
if (b) {
return '01';
}
return '00';
}
static hex2bool(hex) {
if (hex === '01') {
return true;
}
else if (hex === '00') {
return false;
}
throw new Error(`invalid hex ${hex}`);
}
static bytes2hex(b) {
if (b === '') {
return '00';
}
return _1.bsv.Script.fromASM(b).toHex();
}
static hex2bytes(hex) {
if (hex === '00') {
return '';
}
const s = _1.bsv.Script.fromHex(hex);
const chuck = s.chunks[0];
return chuck.buf.toString('hex');
}
static toHex(x, type) {
if (type === scryptTypes_1.ScryptType.INT || type === scryptTypes_1.ScryptType.PRIVKEY) {
return Stateful.int2hex(x);
}
else if (type === scryptTypes_1.ScryptType.BOOL) {
return Stateful.bool2hex(x);
}
else if ((0, scryptTypes_1.isBytes)(type)) {
return Stateful.bytes2hex(x);
}
throw new Error(`unsupported type: ${type}`);
}
static serialize(x, type) {
if (type === scryptTypes_1.ScryptType.INT || type === scryptTypes_1.ScryptType.PRIVKEY) {
const num = new _1.bsv.crypto.BN(x);
if (num.eqn(0)) {
return '';
}
else {
return num.toSM({ endian: 'little' }).toString('hex');
}
}
else if (type === scryptTypes_1.ScryptType.BOOL) {
if (x) {
return '01';
}
return '';
}
else if ((0, scryptTypes_1.isBytes)(type)) {
return x;
}
throw new Error(`unsupported type: ${type}`);
}
/**
* only used for state contract
* @param args
* @param isGenesis
* @param finalTypeResolver
* @returns
*/
static buildState(args, isGenesis, resolver) {
const args_ = args.map(arg => {
return (0, internal_1.flatternArg)(arg, resolver, { state: true, ignoreValue: false });
}).flat(Infinity);
if (args_.length <= 0) {
throw new Error('no state property found, buildContractState only used for state contract');
}
// append isGenesis which is a hidden built-in state
let state_hex = `${Stateful.toHex(isGenesis, scryptTypes_1.ScryptType.BOOL)}`;
state_hex += args_.map(a => Stateful.toHex(a.value, a.type)).join('');
//append meta
if (state_hex) {
const state_len = state_hex.length / 2;
state_hex += (0, _1.num2bin)(BigInt(state_len), 4) + (0, _1.num2bin)(Stateful.CURRENT_STATE_VERSION, 1);
return state_hex;
}
return state_hex;
}
static buildDefaultStateArgs(contract) {
const dummyArgs = contract.stateProps.map(p => {
const dummyArg = Object.assign({}, p, { value: false });
return (0, internal_1.flatternArg)(dummyArg, contract.resolver, { state: true, ignoreValue: true });
}).flat(Infinity);
const hexTemplateMap = new Map();
dummyArgs.forEach(p => {
if (p.type === scryptTypes_1.ScryptType.INT || p.type === scryptTypes_1.ScryptType.PRIVKEY) {
hexTemplateMap.set(`<${p.name}>`, Stateful.int2hex((0, scryptTypes_1.Int)(0)));
}
else if (p.type === scryptTypes_1.ScryptType.BOOL) {
hexTemplateMap.set(`<${p.name}>`, Stateful.bool2hex(true));
}
else if (p.type === scryptTypes_1.ScryptType.BYTES
|| p.type === scryptTypes_1.ScryptType.PUBKEY
|| p.type === scryptTypes_1.ScryptType.SIG
|| p.type === scryptTypes_1.ScryptType.RIPEMD160
|| p.type === scryptTypes_1.ScryptType.SHA1
|| p.type === scryptTypes_1.ScryptType.SHA256
|| p.type === scryptTypes_1.ScryptType.SIGHASHTYPE
|| p.type === scryptTypes_1.ScryptType.SIGHASHPREIMAGE
|| p.type === scryptTypes_1.ScryptType.OPCODETYPE) {
hexTemplateMap.set(`<${p.name}>`, Stateful.bytes2hex('00'));
}
else {
throw new Error(`param ${p.name} has unknown type ${p.type}`);
}
});
return contract.stateProps.map(param => (0, deserializer_1.deserializeArgfromHex)(contract.resolver, Object.assign(param, {
value: undefined
}), hexTemplateMap, { state: true }));
}
static deserializer(type, hex) {
switch (type) {
case scryptTypes_1.ScryptType.BOOL:
return (0, scryptTypes_1.Bool)(Stateful.hex2bool(hex));
case scryptTypes_1.ScryptType.INT:
return (0, scryptTypes_1.Int)(Stateful.hex2int(hex));
case scryptTypes_1.ScryptType.BYTES:
return (0, scryptTypes_1.Bytes)(Stateful.hex2bytes(hex));
case scryptTypes_1.ScryptType.PRIVKEY:
return (0, scryptTypes_1.PrivKey)(Stateful.hex2int(hex));
case scryptTypes_1.ScryptType.PUBKEY:
return (0, scryptTypes_1.PubKey)(Stateful.hex2bytes(hex));
case scryptTypes_1.ScryptType.SIG:
return (0, scryptTypes_1.Sig)(Stateful.hex2bytes(hex));
case scryptTypes_1.ScryptType.RIPEMD160:
return (0, scryptTypes_1.Ripemd160)(Stateful.hex2bytes(hex));
case scryptTypes_1.ScryptType.SHA1:
return (0, scryptTypes_1.Sha1)(Stateful.hex2bytes(hex));
case scryptTypes_1.ScryptType.SHA256:
return (0, scryptTypes_1.Sha256)(Stateful.hex2bytes(hex));
case scryptTypes_1.ScryptType.SIGHASHTYPE:
return (0, scryptTypes_1.SigHashType)(Number(Stateful.hex2int(hex)));
case scryptTypes_1.ScryptType.SIGHASHPREIMAGE:
return (0, scryptTypes_1.SigHashPreimage)(Stateful.hex2bytes(hex));
case scryptTypes_1.ScryptType.OPCODETYPE:
return (0, scryptTypes_1.OpCodeType)(Stateful.hex2bytes(hex));
default:
throw new Error(`<${type}> cannot be cast to ScryptType, only sCrypt native types supported`);
}
}
static readBytes(br) {
try {
const opcodenum = br.readUInt8();
let len, data;
if (opcodenum == 0) {
data = '';
}
else if (opcodenum > 0 && opcodenum < _1.bsv.Opcode.OP_PUSHDATA1) {
len = opcodenum;
data = br.read(len).toString('hex');
}
else if (opcodenum === _1.bsv.Opcode.OP_PUSHDATA1) {
len = br.readUInt8();
data = br.read(len).toString('hex');
}
else if (opcodenum === _1.bsv.Opcode.OP_PUSHDATA2) {
len = br.readUInt16LE();
data = br.read(len).toString('hex');
}
else if (opcodenum === _1.bsv.Opcode.OP_PUSHDATA4) {
len = br.readUInt32LE();
data = br.read(len).toString('hex');
}
else {
data = (0, _1.num2bin)(BigInt(opcodenum - 80), 1);
}
return {
data: data,
opcodenum: opcodenum
};
}
catch (e) {
throw new Error('readBytes: ' + e);
}
}
static parseStateHex(contract, scriptHex) {
const metaScript = scriptHex.substr(scriptHex.length - 10, 10);
/* eslint-disable @typescript-eslint/no-unused-vars */
const version = Number((0, _1.bin2num)(metaScript.substr(metaScript.length - 2, 2)));
const stateLen = Number((0, _1.bin2num)(metaScript.substr(0, 8)));
const stateHex = scriptHex.substr(scriptHex.length - 10 - stateLen * 2, stateLen * 2);
const br = new _1.bsv.encoding.BufferReader(Buffer.from(stateHex, 'hex'));
const opcodenum = br.readUInt8();
const isGenesis = opcodenum == 1;
const stateTemplateArgs = new Map();
const dummyArgs = contract.stateProps.map(p => {
const dummyArg = Object.assign({}, p, { value: false });
return (0, internal_1.flatternArg)(dummyArg, contract.resolver, { state: true, ignoreValue: true });
}).flat(Infinity);
dummyArgs.forEach((param) => {
if (param.type === scryptTypes_1.ScryptType.BOOL) {
const opcodenum = br.readUInt8();
stateTemplateArgs.set(`<${param.name}>`, opcodenum === 1 ? '01' : '00');
}
else {
const { data } = Stateful.readBytes(br);
stateTemplateArgs.set(`<${param.name}>`, data ? _1.bsv.Script.fromASM(data).toHex() : '00');
}
});
const args = contract.stateProps.map(param => Object.assign({}, param, {
value: false
})).map(arg => {
return (0, deserializer_1.deserializeArgfromHex)(contract.resolver, arg, stateTemplateArgs, { state: true });
});
return [isGenesis, args];
}
}
exports.default = Stateful;
// state version
Stateful.CURRENT_STATE_VERSION = (0, scryptTypes_1.Int)(0);
//# sourceMappingURL=stateful.js.map