UNPKG

@unruggable/gateways

Version:

Trustless Ethereum Multichain CCIP-Read Gateway

114 lines (113 loc) 3.36 kB
import { getBytes, hexlify, toUtf8String } from 'ethers/utils'; import { GatewayRequest } from './vm.mjs'; import { GATEWAY_OP as OP } from './ops.mjs'; const NAMES = []; Object.entries(OP).forEach(([name, op]) => (NAMES[op] = name)); export class ProgramReader { ops; static actions(program) { const reader = this.fromProgram(program); const actions = []; if (program instanceof GatewayRequest) { actions.push({ pos: 0, op: reader.readByte(), // outputCount, name: 'OUTPUT_COUNT', }); } while (reader.remaining) { actions.push(reader.readAction()); } return actions; } static fromProgram(program) { return new this(Uint8Array.from(program.ops)); } static fromBytes(v) { return new this(getBytes(v)); } pos = 0; constructor(ops) { this.ops = ops; } get remaining() { return this.ops.length - this.pos; } checkRead(n) { if (this.pos + n > this.ops.length) throw new Error('reader overflow'); } readByte() { this.checkRead(1); return this.ops[this.pos++]; } readBytes(n) { this.checkRead(n); return hexlify(this.ops.subarray(this.pos, (this.pos += n))); } readUint() { const n = this.readByte(); if (n > 32) throw new Error(`expected word size: ${n}`); return n ? BigInt(this.readBytes(n)) : 0n; } readSmallStr() { return toUtf8String(this.readBytes(this.readByte())); } parseArgs(op) { // TODO: this is probably incomplete switch (op) { //case OP.PUSH_0: case OP.PUSH_1: case OP.PUSH_2: case OP.PUSH_3: case OP.PUSH_4: case OP.PUSH_5: case OP.PUSH_6: case OP.PUSH_7: case OP.PUSH_8: case OP.PUSH_9: case OP.PUSH_10: case OP.PUSH_11: case OP.PUSH_12: case OP.PUSH_13: case OP.PUSH_14: case OP.PUSH_15: case OP.PUSH_16: case OP.PUSH_17: case OP.PUSH_18: case OP.PUSH_19: case OP.PUSH_20: case OP.PUSH_21: case OP.PUSH_22: case OP.PUSH_23: case OP.PUSH_24: case OP.PUSH_25: case OP.PUSH_26: case OP.PUSH_27: case OP.PUSH_28: case OP.PUSH_29: case OP.PUSH_30: case OP.PUSH_31: case OP.PUSH_32: return { bytes: this.readBytes(op) }; case OP.PUSH_BYTES: return { bytes: this.readBytes(Number(this.readUint())) }; case OP.EVAL_LOOP: return { flags: this.readByte() }; case OP.ASSERT: return { exitCode: this.readByte() }; case OP.DEBUG: return { label: this.readSmallStr() }; default: return {}; } } readAction() { const op = this.readByte(); const name = NAMES[op]; if (!name) throw new Error(`unknown op: ${op}`); return { pos: this.pos, op, name, ...this.parseArgs(op) }; } }