Skip to content

Commit

Permalink
update template variables spec
Browse files Browse the repository at this point in the history
  • Loading branch information
joe-p committed Oct 10, 2024
1 parent 3476dad commit 1e31ba0
Showing 1 changed file with 52 additions and 24 deletions.
76 changes: 52 additions & 24 deletions ARCs/arc-0056.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,35 +350,21 @@ interface SourceInfo {

### Template Variables

Template variables are variables in the TEAL that should be substitued prior to compilation. Template variables **MUST** be an argument to `pusbytes` (not the pseudo opcode `byte`) to ensure that the program does not add them to the constant blocks. `pushint` (or `int`) is not used due to the variance in integer byte sizes which impact source mapping.
Template variables are variables in the TEAL that should be substitued prior to compilation. The usage of the variable **MUST** appear in the TEAL starting with `TMPL_`. Template variables **MUST** be an argument to either `bytecblock` or `intcblock`. If a program has template variables, `bytecblock` and `intcblock` **MUST** be the first two opcodes in the program (unless one is not used).

#### Examples

##### Valid

```
pushbytes TMPL_FOO
```

#### Invalid

Template variables MUST use `pushint` or `pushbyte`
#### Example

```js
#pragma version 10
bytecblock 0xdeadbeef TMPL_FOO
intcblock 0x12345678 TMPL_BAR
```
pushbytes TMPL_FOO
```

Template variables MUST be the entire argument

```
pusbytes 0xdeadTMPL_BAR
```
### Dynamic Template Variables

Template variables MUST NOT be used with `pushint`
When a program has a template variable with a dynamic length, the `pcOffsetMethod` in `ProgramSourceInfo` **MUST** be `cblocks`. The `pc` value in each `SourceInfo` **MUST** be the pc determined at compilation minus the last `pc` value of the last `cblock` at compilation.

```
pushint TMPL_FOO
```
When a client is leveraging a source map with `cblocks` as the `pcOffsetMethod`, it **MUST** determine the `pc` value by parsing the bytecode to get the PC value of the last `cblock` at the top of the program. See the reference implementation section for an example of how to do this.

## Rationale

Expand Down Expand Up @@ -406,7 +392,49 @@ NA

## Reference Implementation

TODO
### Calculating cblock Offsets

```ts
const BYTE_CBLOCK = 38;
const INT_CBLOCK = 32;

function parseConstantBlocks(bytes: number[]) {
const programSize = bytes.length;
bytes.shift(); // remove version
const offsets: { bytecblock?: number; intcblock?: number; cblocks: number } =
{ cblocks: 0 };

while (bytes.length > 0) {
const byte = bytes.shift()!;

if (byte === BYTE_CBLOCK || byte === INT_CBLOCK) {
const isBytecblock = byte === BYTE_CBLOCK;
const valuesRemaining = bytes.shift()!;

for (let i = 0; i < valuesRemaining; i++) {
if (isBytecblock) {
// byte is the length of the next element
bytes.splice(0, bytes.shift()!);
} else {
// intcblock is a uvarint, so we need to keep reading until we find the end (MSB is not set)
while ((bytes.shift()! & 0x80) !== 0) {}
}
}

offsets[isBytecblock ? "bytecblock" : "intcblock"] =
programSize - bytes.length;

if (bytes[0] !== BYTE_CBLOCK && bytes[0] !== INT_CBLOCK) {
// if the next opcode isn't a constant block, we're done
break;
}
}
}

offsets.cblocks = Math.max(...Object.values(offsets));
return offsets;
}
```

## Security Considerations

Expand Down

0 comments on commit 1e31ba0

Please sign in to comment.