readProgram static method
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);
}