readProgram static method

ProgramData readProgram({
  1. required Uint8List program,
  2. required List<Uint8List> arguments,
})

Performs basic program validation: instruction count and program cost

Implementation

static ProgramData readProgram({
  required Uint8List program,
  required List<Uint8List> arguments,
}) {
  final ints = <int>[];
  final bytes = <Uint8List>[];
  if (langSpec == null) {
    loadLangSpec();
  }

  final result = getUVarint(program, 0);
  var vlen = result.length;
  var version = result.value;
  if (vlen <= 0) {
    throw AlgorandException(message: 'Version parsing error');
  }

  if (version > langSpec!.evalMaxVersion) {
    throw AlgorandException(message: 'Unsupported version');
  }

  var cost = 0;
  var length = program.length;
  for (var i = 0; i < arguments.length; i++) {
    length += arguments[i].length;
  }

  if (length > MAX_LENGTH) {
    throw AlgorandException(message: 'program too long');
  }

  final opcodes = List<Operation?>.filled(256, null);
  for (var i = 0; i < langSpec!.operations.length; i++) {
    var op = langSpec!.operations[i];
    opcodes[op.opCode] = op;
  }

  var pc = vlen;
  while (pc < program.length) {
    var opcode = program[pc];
    var op = opcodes[opcode];
    if (op == null) {
      throw AlgorandException(message: 'invalid instruction: $opcode');
    }

    cost += op.cost;
    var size = op.size;
    if (size == 0) {
      switch (op.opCode) {
        case INTCBLOCK_OPCODE:
          final intsBlock = readIntConstBlock(program, pc);
          size += intsBlock.size;
          ints.addAll(intsBlock.results);
          break;
        case BYTECBLOCK_OPCODE:
          final bytesBlock = readByteConstBlock(program, pc);
          size += bytesBlock.size;
          bytes.addAll(bytesBlock.results);
          break;
        case PUSHINT_OPCODE:
          final pushInt = readPushIntOp(program, pc);
          size += pushInt.size;
          ints.addAll(pushInt.results);
          break;
        case PUSHBYTES_OPCODE:
          final pushBytes = readPushByteOp(program, pc);
          size += pushBytes.size;
          bytes.addAll(pushBytes.results);
          break;
        default:
          throw AlgorandException(message: 'invalid instruction');
      }
    }

    pc += size;
  }

  // costs calculated dynamically starting in v4
  if (version < 4 && cost > MAX_COST) {
    throw AlgorandException(
      message: 'program too costly for Teal version < 4. consider using v4.',
    );
  }

  return ProgramData(good: true, intBlock: ints, byteBlock: bytes);
}