UNPKG

clvm

Version:

Javascript implementation of chia lisp

461 lines (460 loc) 17.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.op_softfork = exports.op_all = exports.op_any = exports.op_not = exports.op_lognot = exports.op_logxor = exports.op_logior = exports.op_logand = exports.binop_reduction = exports.op_lsh = exports.op_ash = exports.op_concat = exports.op_substr = exports.op_strlen = exports.op_point_add = exports.op_pubkey_for_exp = exports.op_gr_bytes = exports.op_gr = exports.op_div = exports.op_divmod = exports.op_multiply = exports.op_subtract = exports.op_add = exports.args_as_bool_list = exports.args_as_bools = exports.args_as_int_list = exports.args_as_int32 = exports.args_as_ints = exports.op_sha256 = exports.malloc_cost = void 0; const SHA256_1 = require("jscrypto/SHA256"); const SExp_1 = require("./SExp"); const costs_1 = require("./costs"); const __type_compatibility__1 = require("./__type_compatibility__"); const EvalError_1 = require("./EvalError"); const casts_1 = require("./casts"); const CLVMObject_1 = require("./CLVMObject"); const __bls_signatures__1 = require("./__bls_signatures__"); function malloc_cost(cost, atom) { if (!atom.atom) { throw new EvalError_1.EvalError("atom is None", atom); } return (0, __type_compatibility__1.t)(cost + atom.atom.length * costs_1.MALLOC_COST_PER_BYTE, atom); } exports.malloc_cost = malloc_cost; function op_sha256(args) { let cost = costs_1.SHA256_BASE_COST; let arg_len = 0; const h = new SHA256_1.SHA256(); for (const _ of args.as_iter()) { const atom = _.atom; if (!atom) { throw new EvalError_1.EvalError("sha256 on list", _); } arg_len += atom.length; cost += costs_1.SHA256_COST_PER_ARG; h.update(atom.as_word()); } cost += arg_len * costs_1.SHA256_COST_PER_BYTE; return malloc_cost(cost, SExp_1.SExp.to(__type_compatibility__1.Bytes.from(h.finalize().toUint8Array()))); } exports.op_sha256 = op_sha256; function* args_as_ints(op_name, args) { for (const arg of args.as_iter()) { if (arg.pair || !arg.atom) { throw new EvalError_1.EvalError(`${op_name} requires int args`, arg); } yield (0, __type_compatibility__1.t)(arg.as_bigint(), arg.atom.length); } } exports.args_as_ints = args_as_ints; function* args_as_int32(op_name, args) { for (const arg of args.as_iter()) { if (arg.pair || !arg.atom) { throw new EvalError_1.EvalError(`${op_name} requires int32 args`, arg); } else if (arg.atom.length > 4) { throw new EvalError_1.EvalError(`${op_name} requires int32 args (with no leading zeros)`, arg); } yield arg.as_int(); } } exports.args_as_int32 = args_as_int32; function args_as_int_list(op_name, args, count) { const int_list = (0, __type_compatibility__1.list)(args_as_ints(op_name, args)); if (int_list.length !== count) { const plural = count !== 1 ? "s" : ""; throw new EvalError_1.EvalError(`${op_name} takes exactly ${count} argument${plural}`, args); } return int_list; } exports.args_as_int_list = args_as_int_list; function* args_as_bools(op_name, args) { for (const arg of args.as_iter()) { const v = arg.atom; if (v === null || v === void 0 ? void 0 : v.equal_to(__type_compatibility__1.Bytes.NULL)) { yield SExp_1.SExp.FALSE; } else { yield SExp_1.SExp.TRUE; } } } exports.args_as_bools = args_as_bools; function args_as_bool_list(op_name, args, count) { const bool_list = (0, __type_compatibility__1.list)(args_as_bools(op_name, args)); if (bool_list.length !== count) { const plural = count !== 1 ? "s" : ""; throw new EvalError_1.EvalError(`${op_name} takes exactly ${count} argument${plural}`, args); } return bool_list; } exports.args_as_bool_list = args_as_bool_list; function op_add(args) { let total = BigInt(0); let cost = costs_1.ARITH_BASE_COST; let arg_size = 0; for (const ints of args_as_ints("+", args)) { const [r, l] = ints; total += r; arg_size += l; cost += costs_1.ARITH_COST_PER_ARG; } cost += arg_size * costs_1.ARITH_COST_PER_BYTE; return malloc_cost(cost, SExp_1.SExp.to(total)); } exports.op_add = op_add; function op_subtract(args) { let cost = costs_1.ARITH_BASE_COST; if (args.nullp()) { return malloc_cost(cost, SExp_1.SExp.to(0)); } let sign = BigInt(1); let total = BigInt(0); let arg_size = 0; for (const ints of args_as_ints("-", args)) { const [r, l] = ints; total += sign * r; sign = BigInt(-1); arg_size += l; cost += costs_1.ARITH_COST_PER_ARG; } cost += arg_size * costs_1.ARITH_COST_PER_BYTE; return malloc_cost(cost, SExp_1.SExp.to(total)); } exports.op_subtract = op_subtract; function op_multiply(args) { let cost = costs_1.MUL_BASE_COST; const operands = args_as_ints("*", args); const res = operands.next(); if (res.done) { return malloc_cost(cost, SExp_1.SExp.to(1)); } let [v, vs] = res.value; for (const o of operands) { const [r, rs] = o; cost += costs_1.MUL_COST_PER_OP; cost += (rs + vs) * costs_1.MUL_LINEAR_COST_PER_BYTE; cost += ((rs * vs) / costs_1.MUL_SQUARE_COST_PER_BYTE_DIVIDER) | 0; v = v * r; vs = (0, casts_1.limbs_for_int)(v); } return malloc_cost(cost, SExp_1.SExp.to(v)); } exports.op_multiply = op_multiply; function op_divmod(args) { let cost = costs_1.DIVMOD_BASE_COST; const [t1, t2] = args_as_int_list("divmod", args, 2); const [i0, l0] = t1; const [i1, l1] = t2; if (i1 === BigInt(0)) { throw new EvalError_1.EvalError("divmod with 0", SExp_1.SExp.to(i0)); } cost += (l0 + l1) * costs_1.DIVMOD_COST_PER_BYTE; const [q, r] = (0, __type_compatibility__1.divmod)(i0, i1); const q1 = SExp_1.SExp.to(q); const r1 = SExp_1.SExp.to(r); cost += (q1.atom.length + r1.atom.length) * costs_1.MALLOC_COST_PER_BYTE; return (0, __type_compatibility__1.t)(cost, SExp_1.SExp.to((0, __type_compatibility__1.t)(q, r))); } exports.op_divmod = op_divmod; function op_div(args) { let cost = costs_1.DIV_BASE_COST; const [t1, t2] = args_as_int_list("/", args, 2); const [i0, l0] = t1; const [i1, l1] = t2; if (i1 === BigInt(0)) { throw new EvalError_1.EvalError("div with 0", SExp_1.SExp.to(i0)); } if (i0 < BigInt(0) || i1 < BigInt(0)) { throw new EvalError_1.EvalError("div operator with negative operands is deprecated", args); } cost += (l0 + l1) * costs_1.DIV_COST_PER_BYTE; const divmod_result = (0, __type_compatibility__1.divmod)(i0, i1); const q = divmod_result[0]; // const r = divmod_result[1]; return malloc_cost(cost, SExp_1.SExp.to(q)); } exports.op_div = op_div; function op_gr(args) { const [t1, t2] = args_as_int_list(">", args, 2); const [i0, l0] = t1; const [i1, l1] = t2; let cost = costs_1.GR_BASE_COST; cost += (l0 + l1) * costs_1.GR_COST_PER_BYTE; return (0, __type_compatibility__1.t)(cost, i0 > i1 ? SExp_1.SExp.TRUE : SExp_1.SExp.FALSE); } exports.op_gr = op_gr; function op_gr_bytes(args) { const arg_list = (0, __type_compatibility__1.list)(args.as_iter()); if (arg_list.length !== 2) { throw new EvalError_1.EvalError(">s takes exactly 2 arguments", args); } const [a0, a1] = arg_list; if (a0.pair || a1.pair) { throw new EvalError_1.EvalError(">s on list", a0.pair ? a0 : a1); } const b0 = a0.atom; const b1 = a1.atom; let cost = costs_1.GRS_BASE_COST; cost += (b0.length + b1.length) * costs_1.GRS_COST_PER_BYTE; return (0, __type_compatibility__1.t)(cost, b0.compare(b1) > 0 /* b0 > b1 */ ? SExp_1.SExp.TRUE : SExp_1.SExp.FALSE); } exports.op_gr_bytes = op_gr_bytes; function op_pubkey_for_exp(args) { const t0 = args_as_int_list("pubkey_for_exp", args, 1)[0]; let i0 = t0[0]; const l0 = t0[1]; i0 = (0, __type_compatibility__1.modulo)(i0, BigInt("0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001")); // i0 % BigInt("0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001") const { PrivateKey } = (0, __bls_signatures__1.getBLSModule)(); const bytes = new Uint8Array(32); const u0 = (0, casts_1.bigint_to_bytes)(i0, { signed: false }).raw(); if (u0.length > 0) { bytes.set(u0, 32 - u0.length); } const exponent = PrivateKey.from_bytes(bytes, false); try { const r = SExp_1.SExp.to(__type_compatibility__1.Bytes.from(exponent.get_g1(), "G1Element")); let cost = costs_1.PUBKEY_BASE_COST; cost += l0 * costs_1.PUBKEY_COST_PER_BYTE; return malloc_cost(cost, r); } catch (e) { throw new EvalError_1.EvalError(`problem in op_pubkey_for_exp: ${e}`, args); } } exports.op_pubkey_for_exp = op_pubkey_for_exp; function op_point_add(items) { let cost = costs_1.POINT_ADD_BASE_COST; const { G1Element } = (0, __bls_signatures__1.getBLSModule)(); let p = new G1Element(); for (const _ of items.as_iter()) { if (!(0, CLVMObject_1.isAtom)(_)) { throw new EvalError_1.EvalError("point_add on list", _); } try { const atom_g1 = (0, __bls_signatures__1.G1Element_from_bytes)(_.atom.raw()); p = (0, __bls_signatures__1.G1Element_add)(p, atom_g1); cost += costs_1.POINT_ADD_COST_PER_ARG; } catch (e) { const eMsg = e instanceof Error ? e.message : typeof e === "string" ? e : JSON.stringify(e); throw new EvalError_1.EvalError(`point_add expects blob, got ${_}: ${eMsg}`, items); } } return malloc_cost(cost, SExp_1.SExp.to(p)); } exports.op_point_add = op_point_add; function op_strlen(args) { if (args.list_len() !== 1) { throw new EvalError_1.EvalError("strlen takes exactly 1 argument", args); } const a0 = args.first(); if (!(0, CLVMObject_1.isAtom)(a0)) { throw new EvalError_1.EvalError("strlen on list", a0); } const size = a0.atom.length; const cost = costs_1.STRLEN_BASE_COST + size * costs_1.STRLEN_COST_PER_BYTE; return malloc_cost(cost, SExp_1.SExp.to(size)); } exports.op_strlen = op_strlen; function op_substr(args) { const arg_count = args.list_len(); if (![2, 3].includes(arg_count)) { throw new EvalError_1.EvalError("substr takes exactly 2 or 3 arguments", args); } const a0 = args.first(); if (!(0, CLVMObject_1.isAtom)(a0)) { throw new EvalError_1.EvalError("substr on list", a0); } const s0 = a0.atom; let i1; let i2; if (arg_count === 2) { i1 = args_as_int32("substr", args.rest()).next().value; i2 = s0.length; } else { const ints = []; for (const i of args_as_int32("substr", args.rest())) { ints.push(i); } ([i1, i2] = ints); } if (i2 > s0.length || i2 < i1 || i2 < 0 || i1 < 0) { throw new EvalError_1.EvalError("invalid indices for substr", args); } const s = s0.subarray(i1, i2 - i1); const cost = 1; return (0, __type_compatibility__1.t)(cost, SExp_1.SExp.to(s)); } exports.op_substr = op_substr; function op_concat(args) { let cost = costs_1.CONCAT_BASE_COST; const s = new __type_compatibility__1.Stream(); for (const arg of args.as_iter()) { if (!(0, CLVMObject_1.isAtom)(arg)) { throw new EvalError_1.EvalError("concat on list", arg); } s.write(arg.atom); cost += costs_1.CONCAT_COST_PER_ARG; } const r = s.getValue(); cost += r.length * costs_1.CONCAT_COST_PER_BYTE; return malloc_cost(cost, SExp_1.SExp.to(r)); } exports.op_concat = op_concat; function op_ash(args) { const [t1, t2] = args_as_int_list("ash", args, 2); const [i0, l0] = t1; const [i1, l1] = t2; if (l1 > 4) { throw new EvalError_1.EvalError("ash requires int32 args (with no leading zeros)", args.rest().first()); } else if ((i1 >= BigInt(0) ? i1 : -i1) > BigInt(65535)) { throw new EvalError_1.EvalError("shift too large", SExp_1.SExp.to(i1)); } let r; if (i1 >= 0) { r = i0 << i1; } else { r = i0 >> -i1; } let cost = costs_1.ASHIFT_BASE_COST; cost += (l0 + (0, casts_1.limbs_for_int)(r)) * costs_1.ASHIFT_COST_PER_BYTE; return malloc_cost(cost, SExp_1.SExp.to(r)); } exports.op_ash = op_ash; function op_lsh(args) { const [t1, t2] = args_as_int_list("lsh", args, 2); const l0 = t1[1]; const [i1, l1] = t2; if (l1 > 4) { throw new EvalError_1.EvalError("lsh requires int32 args (with no leading zeros)", args.rest().first()); } else if ((i1 >= BigInt(0) ? i1 : -i1) > BigInt(65535)) { throw new EvalError_1.EvalError("shift too large", SExp_1.SExp.to(i1)); } // we actually want i0 to be an *unsigned* int const a0 = args.first().atom; const i0 = (0, casts_1.bigint_from_bytes)(a0, { signed: false }); let r; if (i1 >= 0) { r = i0 << i1; } else { r = i0 >> -i1; } let cost = costs_1.LSHIFT_BASE_COST; cost += (l0 + (0, casts_1.limbs_for_int)(r)) * costs_1.LSHIFT_COST_PER_BYTE; return malloc_cost(cost, SExp_1.SExp.to(r)); } exports.op_lsh = op_lsh; // eslint-disable-next-line @typescript-eslint/ban-types function binop_reduction(op_name, initial_value, args, op_f) { let total = initial_value; let arg_size = 0; let cost = costs_1.LOG_BASE_COST; for (const t of args_as_ints(op_name, args)) { const [r, l] = t; total = op_f(total, r); arg_size += l; cost += costs_1.LOG_COST_PER_ARG; } cost += arg_size * costs_1.LOG_COST_PER_BYTE; return malloc_cost(cost, SExp_1.SExp.to(total)); } exports.binop_reduction = binop_reduction; function op_logand(args) { const binop = (a, b) => { a &= b; return a; }; return binop_reduction("logand", BigInt(-1), args, binop); } exports.op_logand = op_logand; function op_logior(args) { const binop = (a, b) => { a |= b; return a; }; return binop_reduction("logior", BigInt(0), args, binop); } exports.op_logior = op_logior; function op_logxor(args) { const binop = (a, b) => { a ^= b; return a; }; return binop_reduction("logxor", BigInt(0), args, binop); } exports.op_logxor = op_logxor; function op_lognot(args) { const t = args_as_int_list("lognot", args, 1); const [i0, l0] = t[0]; const cost = costs_1.LOGNOT_BASE_COST + l0 * costs_1.LOGNOT_COST_PER_BYTE; return malloc_cost(cost, SExp_1.SExp.to(~i0)); } exports.op_lognot = op_lognot; function op_not(args) { const boolList = args_as_bool_list("not", args, 1); const i0 = boolList[0]; if (!(0, CLVMObject_1.isAtom)(i0)) { throw new EvalError_1.EvalError("not on list", args); } let r; if (i0.atom.equal_to(__type_compatibility__1.Bytes.NULL)) { r = SExp_1.SExp.TRUE; } else { r = SExp_1.SExp.FALSE; } return (0, __type_compatibility__1.t)(costs_1.BOOL_BASE_COST, SExp_1.SExp.to(r)); } exports.op_not = op_not; function op_any(args) { const items = (0, __type_compatibility__1.list)(args_as_bools("any", args)); const cost = costs_1.BOOL_BASE_COST + items.length * costs_1.BOOL_COST_PER_ARG; let r = SExp_1.SExp.FALSE; for (const v of items) { if (!(0, CLVMObject_1.isAtom)(v)) { throw new EvalError_1.EvalError("any on list", args); } if (!v.atom.equal_to(__type_compatibility__1.Bytes.NULL)) { r = SExp_1.SExp.TRUE; break; } } return (0, __type_compatibility__1.t)(cost, SExp_1.SExp.to(r)); } exports.op_any = op_any; function op_all(args) { const items = (0, __type_compatibility__1.list)(args_as_bools("all", args)); const cost = costs_1.BOOL_BASE_COST + items.length * costs_1.BOOL_COST_PER_ARG; let r = SExp_1.SExp.TRUE; for (const v of items) { if (!(0, CLVMObject_1.isAtom)(v)) { throw new EvalError_1.EvalError("all on list", args); } if (v.atom.equal_to(__type_compatibility__1.Bytes.NULL)) { r = SExp_1.SExp.FALSE; break; } } return (0, __type_compatibility__1.t)(cost, SExp_1.SExp.to(r)); } exports.op_all = op_all; function op_softfork(args) { if (args.list_len() < 1) { throw new EvalError_1.EvalError("softfork takes at least 1 argument", args); } const a = args.first(); if (!(0, CLVMObject_1.isAtom)(a)) { throw new EvalError_1.EvalError("softfork requires int args", a); } const cost_bigint = a.as_bigint(); if (cost_bigint > BigInt(Number.MAX_SAFE_INTEGER)) { throw new Error("Cost greater than 2**53-1 is not supported at this time"); } const cost = Number(cost_bigint); if (cost < 1) { throw new EvalError_1.EvalError("cost must be > 0", args); } return (0, __type_compatibility__1.t)(cost, SExp_1.SExp.FALSE); } exports.op_softfork = op_softfork;