Skip to content

Commit

Permalink
feat: add gloadss opcode (#606)
Browse files Browse the repository at this point in the history
* add gloadss opcode

* add gloadss opcode test

* add gloadss to runtime parser

* Apply suggestions from code review

Co-authored-by: Robert Zaremba <[email protected]>

* bump PyTEAL

* changelog cleanup

* fix guide docs layout

* Opcode docs (#608)

* runtime: update opcodes docs

* fix

* rename

* fix tests

* Update packages/runtime/src/parser/parser.ts

Co-authored-by: Robert Zaremba <[email protected]>

* use mock value when inherited opcode class

* fix types

* chore: merge develop branch

* fix eslint error

* update CHANGELOG.md

Co-authored-by: Robert Zaremba <[email protected]>
  • Loading branch information
vuvoth and robert-zaremba authored Mar 4, 2022
1 parent 262f43a commit e551c3b
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 99 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ Changed:
```bash
algob init --infrastructure
```
- Teal V6 support:
- Add new opcode gloadss([#606](https://github.com/scale-it/algo-builder/pull/606)).


### Template improvements
Expand Down
108 changes: 69 additions & 39 deletions packages/runtime/src/interpreter/opcode-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,11 @@ export class Arg extends Op {
}

execute (stack: TEALStack): void {
// get args from context
const args = this.interpreter.runtime.ctx.args ?? [];
this.checkIndexBound(
this.index, this.interpreter.runtime.ctx.args as Uint8Array[], this.line);
const argN = this.assertBytes(this.interpreter.runtime.ctx.args?.[this.index], this.line);
this.index, args, this.line);
const argN = this.assertBytes(args?.[this.index], this.line);
stack.push(argN);
}
}
Expand Down Expand Up @@ -1344,7 +1346,7 @@ export class Txna extends Op {
/// placeholder values
const mockTxIdx = "100";
const mockTxFieldIdx = "200";

const mockScratchIndex = "100";
/**
* push value of a field to the stack from a transaction in the current transaction group
* push to stack [...stack, value of field]
Expand Down Expand Up @@ -1615,8 +1617,8 @@ export class Global extends Op {
break;
}
case 'CreatorAddress': {
const appID = this.interpreter.runtime.ctx.tx.apid;
const app = this.interpreter.getApp(appID as number, this.line);
const appID = this.interpreter.runtime.ctx.tx.apid ?? 0;
const app = this.interpreter.getApp(appID, this.line);
result = decodeAddress(app.creator).publicKey;
break;
}
Expand Down Expand Up @@ -2571,9 +2573,9 @@ export class Gtxns extends Gtxn {
* @param interpreter interpreter object
*/
constructor (args: string[], line: number, interpreter: Interpreter) {
// NOTE: 100 is a mock value (max no of txns in group can be 16 atmost).
// NOTE: mockTxIdx is a mock value (max no of txns in group can be 16 atmost).
// In gtxns & gtxnsa opcodes, index is fetched from top of stack.
super(["100", ...args], line, interpreter);
super([mockTxIdx, ...args], line, interpreter);
}

execute (stack: TEALStack): void {
Expand Down Expand Up @@ -2657,7 +2659,7 @@ export class MinBalance extends Op {
// Args expected: [{uint8 transaction group index}(T),
// {uint8 position in scratch space to load from}(I)]
export class Gload extends Op {
readonly scratchIndex: number;
scratchIndex: number;
txIndex: number;
readonly interpreter: Interpreter;
readonly line: number;
Expand Down Expand Up @@ -2705,8 +2707,8 @@ export class Gloads extends Gload {
* @param interpreter interpreter object
*/
constructor (args: string[], line: number, interpreter: Interpreter) {
// "11" is mock value, will be updated when poping from stack in execute
super(["11", ...args], line, interpreter);
// mockTxIdx is place holder value, will be updated when poping from stack in execute
super([mockTxIdx, ...args], line, interpreter);
}

execute (stack: TEALStack): void {
Expand All @@ -2716,6 +2718,29 @@ export class Gloads extends Gload {
}
}

// Loads a scratch space value of another transaction from the current group
// Stack: ..., A: uint64, B: uint64 → ..., any
// Availability: v6
export class Gloadss extends Gload {
/**
* Stores scratch space index number according to argument passed.
* @param args Expected arguments: [index number]
* @param line line number in TEAL file
* @param interpreter interpreter object
*/
constructor (args: string[], line: number, interpreter: Interpreter) {
// Just place holder field;
super([mockTxIdx, mockScratchIndex, ...args], line, interpreter);
}

execute (stack: TEALStack): void {
this.assertMinStackLen(stack, 2, this.line);
this.scratchIndex = Number(this.assertBigInt(stack.pop(), this.line));
this.txIndex = Number(this.assertBigInt(stack.pop(), this.line));
super.execute(stack);
}
}

/**
* Provide subroutine functionality. When callsub is called, the current location in
* the program is saved and immediately jumps to the label passed to the opcode.
Expand Down Expand Up @@ -3320,8 +3345,8 @@ export class Gaids extends Gaid {
* @param interpreter interpreter object
*/
constructor (args: string[], line: number, interpreter: Interpreter) {
// "11" is mock value, will be updated when poping from stack in execute
super(["11", ...args], line, interpreter);
// mockTxIdx is place holder argument, will be updated when poping from stack in execute
super([mockTxIdx, ...args], line, interpreter);
}

execute (stack: TEALStack): void {
Expand Down Expand Up @@ -3701,8 +3726,8 @@ export class Loads extends Load {
* @param interpreter interpreter object
*/
constructor (args: string[], line: number, interpreter: Interpreter) {
// "11" is mock value, will be updated when poping from stack in execute
super(["11", ...args], line, interpreter);
// mockScratchIndex is place holder arguments, will be updated when poping from stack in execute
super([mockScratchIndex, ...args], line, interpreter);
}

execute (stack: TEALStack): void {
Expand Down Expand Up @@ -3925,7 +3950,7 @@ export class ITxnSubmit extends Op {
}

// initial contract account.
const appID = this.interpreter.runtime.ctx.tx.apid as number;
const appID = this.interpreter.runtime.ctx.tx.apid ?? 0;
const contractAddress = getApplicationAddress(appID);
const contractAccount = this.interpreter.runtime.getAccount(contractAddress).account;

Expand Down Expand Up @@ -4183,7 +4208,8 @@ export class Args extends Arg {
* @param interpreter interpreter object
*/
constructor (args: string[], line: number, interpreter: Interpreter) {
super([...args, "100"], line, interpreter);
// just place holder value
super([...args, mockTxIdx], line, interpreter);
assertLen(args.length, 0, line);
}

Expand Down Expand Up @@ -4219,32 +4245,36 @@ export class Log extends Op {
this.assertMinStackLen(stack, 1, this.line);
const logByte = this.assertBytes(stack.pop(), this.line);
const txID = this.interpreter.runtime.ctx.tx.txID;
const txReceipt = this.interpreter.runtime.ctx.state.txReceipts.get(txID) as TxReceipt;
if (txReceipt.logs === undefined) { txReceipt.logs = []; }
const txReceipt = this.interpreter.runtime.ctx.state.txReceipts.get(txID);
// for Log opcode we assume receipt is alway exist
// TODO: recheck when log opcode failed
if (txReceipt) {
if (txReceipt.logs === undefined) { txReceipt.logs = []; }

// max no. of logs exceeded
if (txReceipt.logs.length === ALGORAND_MAX_LOGS_COUNT) {
throw new RuntimeError(
RUNTIME_ERRORS.TEAL.LOGS_COUNT_EXCEEDED_THRESHOLD, {
maxLogs: ALGORAND_MAX_LOGS_COUNT,
line: this.line
}
);
}

// max no. of logs exceeded
if (txReceipt.logs.length === ALGORAND_MAX_LOGS_COUNT) {
throw new RuntimeError(
RUNTIME_ERRORS.TEAL.LOGS_COUNT_EXCEEDED_THRESHOLD, {
maxLogs: ALGORAND_MAX_LOGS_COUNT,
line: this.line
}
);
}
// max "length" of logs exceeded
const length = txReceipt.logs.join("").length + logByte.length;
if (length > ALGORAND_MAX_LOGS_LENGTH) {
throw new RuntimeError(
RUNTIME_ERRORS.TEAL.LOGS_LENGTH_EXCEEDED_THRESHOLD, {
maxLength: ALGORAND_MAX_LOGS_LENGTH,
origLength: length,
line: this.line
}
);
}

// max "length" of logs exceeded
const length = txReceipt.logs.join("").length + logByte.length;
if (length > ALGORAND_MAX_LOGS_LENGTH) {
throw new RuntimeError(
RUNTIME_ERRORS.TEAL.LOGS_LENGTH_EXCEEDED_THRESHOLD, {
maxLength: ALGORAND_MAX_LOGS_LENGTH,
origLength: length,
line: this.line
}
);
txReceipt.logs.push(convertToString(logByte));
}

txReceipt.logs.push(convertToString(logByte));
}
}

Expand Down
13 changes: 7 additions & 6 deletions packages/runtime/src/parser/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
EcdsaPkRecover, EcdsaVerify, Ed25519verify,
EqualTo, Err, Exp, Expw, Extract, Extract3, ExtractUint16,
ExtractUint32, ExtractUint64, Gaid, Gaids, GetAssetDef, GetAssetHolding,
GetBit, GetByte, Gload, Gloads, Global, GreaterThan,
GetBit, GetByte, Gload, Gloads, Gloadss, Global, GreaterThan,
GreaterThanEqualTo, Gtxn, Gtxna, Gtxnas, Gtxns, Gtxnsa, Gtxnsas, Int, Intc, Intcblock, Itob,
ITxn, ITxna, ITxnBegin, ITxnField, ITxnSubmit,
Keccak256, Label, Len, LessThan, LessThanEqualTo, Load, Loads, Log, MinBalance, Mod,
Expand Down Expand Up @@ -243,10 +243,11 @@ opCodeMap[5] = {
};

opCodeMap[6] = {
...opCodeMap[5]
...opCodeMap[5],
gloadss: Gloadss
};

// list of opcodes that require one extra parameter than others: `interpreter`.
// list of opcodes with exactly one parameter.
const interpreterReqList = new Set([
"#pragma", "arg", "bytecblock", "bytec", "intcblock", "intc", "store",
"load", "b", "bz", "bnz", "return", "txn", "gtxn", "txna", "gtxna", "global",
Expand All @@ -255,7 +256,7 @@ const interpreterReqList = new Set([
"app_local_put", "app_global_put", "app_local_del", "app_global_del",
"gtxns", "gtxnsa", "min_balance", "gload", "gloads", "callsub", "retsub",
"gaid", "gaids", "loads", "stores", "itxn_begin", "itxn_field", "itxn_submit",
"itxn", "itxna", "txnas", "gtxnas", "gtxnsas", "args", "log", 'app_params_get'
"itxn", "itxna", "txnas", "gtxnas", "gtxnsas", "args", "log", 'app_params_get', 'gloadss'
]);

const signatureModeOps = new Set([
Expand All @@ -266,7 +267,7 @@ const applicationModeOps = new Set([
"gload", "gloads", "gaid", "gaids", "balance", "app_opted_in", "app_local_get",
"app_local_get_ex", "app_global_get", "app_global_get_ex", "app_local_put", "app_global_put",
"app_local_del", "app_global_del", "asset_holding_get", "asset_params_get", "app_params_get",
"min_balance", "log", "itxn_begin", "itxn_field", "itxn_submit", "itxn", "itxna"
"min_balance", "log", "itxn_begin", "itxn_field", "itxn_submit", "itxn", "itxna", 'gloadss'
]);

// opcodes allowed in both application and signature mode
Expand All @@ -281,7 +282,7 @@ const commonModeOps = new Set([
"extract", "extract3", "extract_uint16", "extract_uint32", "extract_uint64", "pushbytes",
"pushint", "callsub", "retsub", "shl", "shr", "sqrt", "bitlen", "exp", "expw", 'b+',
'b-', 'b*', 'b/', 'b%', 'b<', 'b>', 'b<=', 'b>=', 'b==', 'b!=', 'b|', 'b&', 'b^', 'b~', 'bzero',
"txnas", "gtxnas", "gtxnsas"
"txnas", "gtxnas", "gtxnsas", 'gloadss'
]);

/**
Expand Down
Loading

0 comments on commit e551c3b

Please sign in to comment.