From 1186e8891a1c0dd83a2dd8d2a8f549f19794ff03 Mon Sep 17 00:00:00 2001 From: Vu Vo Date: Thu, 10 Mar 2022 01:46:20 +0700 Subject: [PATCH 1/7] create AcctParamsGet class to handle acct_params_get opcode --- packages/runtime/src/errors/errors-list.ts | 7 ++ .../runtime/src/interpreter/opcode-list.ts | 65 +++++++++++++++++++ packages/runtime/src/lib/constants.ts | 7 ++ 3 files changed, 79 insertions(+) diff --git a/packages/runtime/src/errors/errors-list.ts b/packages/runtime/src/errors/errors-list.ts index 61309bcad..bc6416aa0 100644 --- a/packages/runtime/src/errors/errors-list.ts +++ b/packages/runtime/src/errors/errors-list.ts @@ -377,6 +377,13 @@ maximun uint128`, title: "Execution mode not valid", description: `Execution mode not valid`, }, + UNKNOWN_ACCT_FIELD: { + number: 1053, + message: + "Account Field Error - Unknown Field: %field% at line %line% for teal version #%tealV%", + title: "Account Field Error at line %line%", + description: `Account field unknown`, + }, }; const runtimeGeneralErrors = { diff --git a/packages/runtime/src/interpreter/opcode-list.ts b/packages/runtime/src/interpreter/opcode-list.ts index 3685342bd..e070867d4 100644 --- a/packages/runtime/src/interpreter/opcode-list.ts +++ b/packages/runtime/src/interpreter/opcode-list.ts @@ -20,6 +20,7 @@ import { RUNTIME_ERRORS } from "../errors/errors-list"; import { RuntimeError } from "../errors/runtime-errors"; import { compareArray } from "../lib/compare"; import { + AcctParamQueryFields, ALGORAND_MAX_LOGS_COUNT, ALGORAND_MAX_LOGS_LENGTH, AppParamDefined, @@ -4562,3 +4563,67 @@ export class AppParamsGet extends Op { } } } + +export class AcctParamsGet extends Op { + readonly interpreter: Interpreter; + readonly line: number; + readonly field: string; + /** + * Asserts 1 arguments are passed. + * @param args Expected arguments: [] // none + * @param line line number in TEAL file + * @param interpreter interpreter object + */ + constructor(args: string[], line: number, interpreter: Interpreter) { + super(); + this.line = line; + this.interpreter = interpreter; + assertLen(args.length, 1, line); + + if ( + !AcctParamQueryFields[args[0]] || + AcctParamQueryFields[args[0]].version > interpreter.tealVersion + ) { + throw new RuntimeError(RUNTIME_ERRORS.TEAL.UNKNOWN_ACCT_FIELD, { + field: args[0], + line: line, + tealV: interpreter.tealVersion, + }); + } + + this.field = args[0]; + } + + execute(stack: Stack): void { + this.assertMinStackLen(stack, 1, this.line); + + const acctAddress = this.assertAlgorandAddress(stack.pop(), this.line); + + const accountInfo = this.interpreter.runtime.ctx.state.accounts.get( + encodeAddress(acctAddress) + ); + + if (accountInfo && accountInfo.balance() > 0) { + let value: StackElem = 0n; + switch (this.field) { + case "AcctBalance": { + value = BigInt(accountInfo.balance()); + break; + } + case "AcctMinBalance": { + value = BigInt(accountInfo.minBalance); + break; + } + case "AcctAuthAddr": { + value = convertToBuffer(accountInfo.getSpendAddress()); + break; + } + } + stack.push(value); + stack.push(1n); + } else { + stack.push(0n); + stack.push(0n); + } + } +} diff --git a/packages/runtime/src/lib/constants.ts b/packages/runtime/src/lib/constants.ts index 888e499e8..31e9e2a1e 100644 --- a/packages/runtime/src/lib/constants.ts +++ b/packages/runtime/src/lib/constants.ts @@ -293,6 +293,13 @@ export const AppParamDefined: { [key: number]: Set } = { AppParamDefined[6] = cloneDeep(AppParamDefined[5]); +// param use for query acct_params_get opcode + +export const AcctParamQueryFields: { [key: string]: { version: number } } = { + AcctBalance: { version: 6 }, + AcctMinBalance: { version: 6 }, + AcctAuthAddr: { version: 6 }, +}; export const reDigit = /^\d+$/; export const reDec = /^(0|[1-9]\d*)$/; export const reHex = /^0x[0-9a-fA-F]+$/; From 566fb753843a8e6ca902650a6d6b446e1012a7b8 Mon Sep 17 00:00:00 2001 From: Vu Vo Date: Thu, 10 Mar 2022 22:34:01 +0700 Subject: [PATCH 2/7] add test for AcctParamsGet --- .../runtime/src/interpreter/opcode-list.ts | 13 ++- .../test/src/interpreter/opcode-list.ts | 103 +++++++++++++++++- 2 files changed, 110 insertions(+), 6 deletions(-) diff --git a/packages/runtime/src/interpreter/opcode-list.ts b/packages/runtime/src/interpreter/opcode-list.ts index e070867d4..7cd65e438 100644 --- a/packages/runtime/src/interpreter/opcode-list.ts +++ b/packages/runtime/src/interpreter/opcode-list.ts @@ -4599,11 +4599,10 @@ export class AcctParamsGet extends Op { const acctAddress = this.assertAlgorandAddress(stack.pop(), this.line); - const accountInfo = this.interpreter.runtime.ctx.state.accounts.get( - encodeAddress(acctAddress) - ); + // get account from current context + const accountInfo = this.interpreter.getAccount(acctAddress, this.line); - if (accountInfo && accountInfo.balance() > 0) { + if (accountInfo.balance() > 0) { let value: StackElem = 0n; switch (this.field) { case "AcctBalance": { @@ -4615,7 +4614,11 @@ export class AcctParamsGet extends Op { break; } case "AcctAuthAddr": { - value = convertToBuffer(accountInfo.getSpendAddress()); + if (accountInfo.getSpendAddress() === accountInfo.address) { + value = ZERO_ADDRESS; + } else { + value = Buffer.from(decodeAddress(accountInfo.getSpendAddress()).publicKey); + } break; } } diff --git a/packages/runtime/test/src/interpreter/opcode-list.ts b/packages/runtime/test/src/interpreter/opcode-list.ts index 7e6771b7a..6a2306ec6 100644 --- a/packages/runtime/test/src/interpreter/opcode-list.ts +++ b/packages/runtime/test/src/interpreter/opcode-list.ts @@ -15,6 +15,7 @@ import { RUNTIME_ERRORS } from "../../../src/errors/errors-list"; import { getProgram, Runtime } from "../../../src/index"; import { Interpreter } from "../../../src/interpreter/interpreter"; import { + AcctParamsGet, Add, Addr, Addw, @@ -6189,7 +6190,7 @@ describe("Teal Opcodes", function () { }); }); - describe("TEALv6 opcodes", function () { + describe("TEALv6: divw and bsqrt opcodes", function () { let stack: Stack; const initStack = (values: StackElem[]): Stack => { @@ -6252,4 +6253,104 @@ describe("Teal Opcodes", function () { expectRuntimeError(() => op.execute(stack), RUNTIME_ERRORS.TEAL.BYTES_LEN_EXCEEDED); }); }); + + + describe("Tealv6: acct_params_get opcode", function(){ + const stack = new Stack(); + let interpreter: Interpreter; + let alice: AccountStoreI; + let bob: AccountStoreI; + let op: AcctParamsGet; + const zeroBalanceAddr = "WWYNX3TKQYVEREVSW6QQP3SXSFOCE3SKUSEIVJ7YAGUPEACNI5UGI4DZCE"; + + this.beforeEach(() => { + interpreter = new Interpreter(); + interpreter.runtime = new Runtime([]); + [alice, bob] = interpreter.runtime.defaultAccounts() + // init tx + interpreter.runtime.ctx.tx = {...TXN_OBJ, apat: [ + Buffer.from(decodeAddress(alice.address).publicKey), + Buffer.from(decodeAddress(zeroBalanceAddr).publicKey) + ]}; + interpreter.tealVersion = MaxTEALVersion; + interpreter.runtime.ctx.gtxs = [TXN_OBJ]; + interpreter.tealVersion = 6; + stack.push(decodeAddress(alice.address).publicKey); + }); + + it("Should return balance", () => { + op = new AcctParamsGet(["AcctBalance"], 1, interpreter); + op.execute(stack); + assert.equal(stack.pop(), 1n); // balance > 0 + assert.equal(stack.pop(), alice.balance()); + }); + + it("Should return min balance", () => { + op = new AcctParamsGet(["AcctMinBalance"], 1, interpreter); + op.execute(stack); + assert.equal(stack.pop(), 1n); // balance > 0 + assert.equal(stack.pop(), BigInt(alice.minBalance)); + }); + + it("Should return Auth Address", () => { + op = new AcctParamsGet(["AcctAuthAddr"], 1, interpreter); + op.execute(stack); + assert.equal(stack.pop(), 1n); // balance > 0 + assert.deepEqual(stack.pop(), ZERO_ADDRESS); + }); + + it("Shoud return Auth Address - rekey case", () => { + // set spend key for alice is bob + alice.rekeyTo(bob.address); + interpreter.runtime.ctx.state.accounts.set(alice.address, alice); + op = new AcctParamsGet(["AcctAuthAddr"], 1, interpreter); + op.execute(stack); + assert.equal(stack.pop(), 1n); // balance > 0 + assert.deepEqual(stack.pop(), decodeAddress(bob.address).publicKey); + }); + + // TODO: create un strictgetAccount + it.skip("Should return Auth Address", () => { + op = new AcctParamsGet(["AcctBalance"], 1, interpreter); + stack.push(decodeAddress(zeroBalanceAddr).publicKey); + op.execute(stack); + assert.equal(stack.pop(), 0n); // balance > 0 + assert.deepEqual(stack.pop(), 0n); + }); + + + it("Should throw error when query unknow field", () => { + expectRuntimeError( + () => new AcctParamsGet(["Miles"], 1, interpreter), + RUNTIME_ERRORS.TEAL.UNKNOWN_ACCT_FIELD + ); + }); + + it("Should throw error if query account not in ref account list", () => { + op = new AcctParamsGet(["AcctBalance"], 1, interpreter); + stack.push(decodeAddress(bob.address).publicKey) + + expectRuntimeError( + () => op.execute(stack), + RUNTIME_ERRORS.TEAL.ADDR_NOT_FOUND_IN_TXN_ACCOUNT + ) + + // valid address but not in tx accounts list + stack.push(parsing.stringToBytes("01234567890123456789012345678901")); + expectRuntimeError( + () => op.execute(stack), + RUNTIME_ERRORS.TEAL.ADDR_NOT_FOUND_IN_TXN_ACCOUNT + ) + }); + + it("Should throw error if top element in stack is not address type", () => { + op = new AcctParamsGet(["AcctBalance"], 1, interpreter); + stack.push(parsing.stringToBytes("ABCDE")); + + expectRuntimeError( + () => op.execute(stack), + RUNTIME_ERRORS.TEAL.INVALID_ADDR + ) + }); + }); }); From f7a043ba38409d39ebbff9e10cbe880d2e4bc14e Mon Sep 17 00:00:00 2001 From: Vu Vo Date: Thu, 10 Mar 2022 23:08:31 +0700 Subject: [PATCH 3/7] add acct_params_get to parse --- packages/runtime/src/parser/parser.ts | 5 +++ .../fixtures/teal-files/assets/teal-v6.teal | 5 +++ packages/runtime/test/src/parser/parser.ts | 42 +++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 packages/runtime/test/fixtures/teal-files/assets/teal-v6.teal diff --git a/packages/runtime/src/parser/parser.ts b/packages/runtime/src/parser/parser.ts index 9c9328c20..28d0172a0 100644 --- a/packages/runtime/src/parser/parser.ts +++ b/packages/runtime/src/parser/parser.ts @@ -2,6 +2,7 @@ import { RUNTIME_ERRORS } from "../errors/errors-list"; import { RuntimeError } from "../errors/runtime-errors"; import { Interpreter } from "../interpreter/interpreter"; import { + AcctParamsGet, Add, Addr, Addw, @@ -374,6 +375,7 @@ opCodeMap[6] = { divw: Divw, bsqrt: Bsqrt, gloadss: Gloadss, + acct_params_get: AcctParamsGet, }; // list of opcodes with exactly one parameter. @@ -430,6 +432,7 @@ const interpreterReqList = new Set([ "log", "app_params_get", "gloadss", + "acct_params_get", ]); const signatureModeOps = new Set(["arg", "args", "arg_0", "arg_1", "arg_2", "arg_3"]); @@ -460,6 +463,7 @@ const applicationModeOps = new Set([ "itxn", "itxna", "gloadss", + "acct_params_get", ]); // opcodes allowed in both application and signature mode @@ -575,6 +579,7 @@ const commonModeOps = new Set([ "divw", "bsqrt", "gloadss", + "acct_params_get", ]); /** diff --git a/packages/runtime/test/fixtures/teal-files/assets/teal-v6.teal b/packages/runtime/test/fixtures/teal-files/assets/teal-v6.teal new file mode 100644 index 000000000..180d41e18 --- /dev/null +++ b/packages/runtime/test/fixtures/teal-files/assets/teal-v6.teal @@ -0,0 +1,5 @@ +#pragma version 6 +bsqrt +divw +gloadss +acct_params_get AcctBalance \ No newline at end of file diff --git a/packages/runtime/test/src/parser/parser.ts b/packages/runtime/test/src/parser/parser.ts index c5bac6292..d302297a7 100644 --- a/packages/runtime/test/src/parser/parser.ts +++ b/packages/runtime/test/src/parser/parser.ts @@ -4,6 +4,7 @@ import { getProgram } from "../../../src"; import { RUNTIME_ERRORS } from "../../../src/errors/errors-list"; import { Interpreter } from "../../../src/interpreter/interpreter"; import { + AcctParamsGet, Add, Addr, Addw, @@ -29,6 +30,7 @@ import { Branch, BranchIfNotZero, BranchIfZero, + Bsqrt, Btoi, Byte, Bytec, @@ -115,6 +117,7 @@ import { Uncover, } from "../../../src/interpreter/opcode-list"; import { + AcctParamQueryFields, AppParamDefined, MAX_UINT64, MaxTEALVersion, @@ -1990,6 +1993,30 @@ describe("Parser", function () { RUNTIME_ERRORS.TEAL.EXECUTION_MODE_NOT_VALID ); }); + + it("acct_params_get", () => { + Object.keys(AcctParamQueryFields).forEach((appParam: string) => { + const res = opcodeFromSentence( + ["acct_params_get", appParam], + 1, + interpreter, + ExecutionMode.APPLICATION + ); + const expected = new AcctParamsGet([appParam], 1, interpreter); + assert.deepEqual(res, expected); + }); + + expectRuntimeError( + () => + opcodeFromSentence( + ["acct_params_get", "unknow", "hello"], + 1, + interpreter, + ExecutionMode.APPLICATION + ), + RUNTIME_ERRORS.TEAL.ASSERT_LENGTH + ); + }); }); }); @@ -2307,6 +2334,21 @@ describe("Parser", function () { assert.deepEqual(res, expected); }); + + it("should rreturn correct opcode list for `teal v6`", async () => { + const file = "teal-v6.teal"; + + const res = parser(getProgram(file), ExecutionMode.APPLICATION, interpreter); + const expected = [ + new Pragma(["version", "6"], 1, interpreter), + new Divw([], 2), + new Bsqrt([], 3), + new Gloadss([], 4, interpreter), + new AcctParamsGet(["AcctBalance"], 5, interpreter), + ]; + + assert.deepEqual(res, expected); + }); }); describe("Gas cost of Opcodes from TEAL file", () => { From fc11f3cf3fcdb03e3525a9b900e5c8163ba4a7a6 Mon Sep 17 00:00:00 2001 From: Vu Vo Date: Fri, 11 Mar 2022 00:03:05 +0700 Subject: [PATCH 4/7] create strict flag for interpreter getAccount --- .../runtime/src/interpreter/interpreter.ts | 38 ++++++++++++++--- .../runtime/src/interpreter/opcode-list.ts | 42 +++++++++---------- .../test/src/interpreter/opcode-list.ts | 29 +++++++++---- 3 files changed, 75 insertions(+), 34 deletions(-) diff --git a/packages/runtime/src/interpreter/interpreter.ts b/packages/runtime/src/interpreter/interpreter.ts index 55e16dd37..d352a7d42 100644 --- a/packages/runtime/src/interpreter/interpreter.ts +++ b/packages/runtime/src/interpreter/interpreter.ts @@ -8,7 +8,7 @@ import { import { RUNTIME_ERRORS } from "../errors/errors-list"; import { RuntimeError } from "../errors/runtime-errors"; -import { Runtime } from "../index"; +import { AccountStore, Runtime } from "../index"; import { checkIndexBound, compareArray } from "../lib/compare"; import { ALGORAND_MAX_APP_ARGS_LEN, @@ -22,6 +22,7 @@ import { keyToBytes } from "../lib/parsing"; import { Stack } from "../lib/stack"; import { assertMaxCost, parser } from "../parser/parser"; import { + AccountAddress, AccountStoreI, AppInfo, BaseTxReceipt, @@ -107,6 +108,20 @@ export class Interpreter { return this.runtime.ctx.getApp(appID, line); } + /** + * Create new account with `address` if this is undefined in ctx and return + * else return state of this account in ctx. + * @param addr address we want to query. + */ + private createAccountIfAbsent(addr: AccountAddress): AccountStoreI { + let account = this.runtime.ctx.state.accounts.get(addr); + if (!account) { + account = new AccountStore(0, {addr, sk: new Uint8Array(0)}); + this.runtime.ctx.state.accounts.set(addr, account); + } + return account; + } + /** * Beginning from TEALv4, user can directly pass address instead of index to Txn.Accounts. * However, the address must still be present in tx.Accounts OR should be equal to Txn.Sender @@ -114,7 +129,7 @@ export class Interpreter { * @param line line number in TEAL file * https://developer.algorand.org/articles/introducing-algorand-virtual-machine-avm-09-release/ */ - private _getAccountFromAddr(accountPk: Uint8Array, line: number): AccountStoreI { + private _getAccountFromAddr(accountPk: Uint8Array, line: number, strict = true): AccountStoreI { const txAccounts = this.runtime.ctx.tx.apat; // tx.Accounts array const appID = this.runtime.ctx.tx.apid ?? 0; if (this.tealVersion <= 3) { @@ -145,7 +160,13 @@ export class Interpreter { compareArray(accountPk, decodeAddress(getApplicationAddress(appID)).publicKey) ) { const address = encodeAddress(pkBuffer); - const account = this.runtime.ctx.state.accounts.get(address); + let account = this.runtime.ctx.state.accounts.get(address); + // strict is turn on we will create account store + // we will create account if it not exist + if (!strict) { + account = this.createAccountIfAbsent(address); + } + return this.runtime.assertAccountDefined(address, account, line); } else { throw new RuntimeError(RUNTIME_ERRORS.TEAL.ADDR_NOT_FOUND_IN_TXN_ACCOUNT, { @@ -158,12 +179,14 @@ export class Interpreter { /** * Queries account by accountIndex or `ctx.tx.snd` (if `accountIndex==0`). * If account address is passed, then queries account by address. - * Throws exception if account is not found. + * when `strict` is true we will throws exception if account is not found. + * when `strict` is false we will create new account and add it to context. * @param accountRef index of account to fetch from account list * @param line line number + * @param strict strict flag * NOTE: index 0 represents txn sender account */ - getAccount(accountRef: StackElem, line: number): AccountStoreI { + getAccount(accountRef: StackElem, line: number, strict = true): AccountStoreI { let account: AccountStoreI | undefined; let address: string; if (typeof accountRef === "bigint") { @@ -181,9 +204,12 @@ export class Interpreter { } address = encodeAddress(pkBuffer); account = this.runtime.ctx.state.accounts.get(address); + if (!strict) { + account = this.createAccountIfAbsent(address); + } } } else { - return this._getAccountFromAddr(accountRef, line); + return this._getAccountFromAddr(accountRef, line, strict); } return this.runtime.assertAccountDefined(address, account, line); diff --git a/packages/runtime/src/interpreter/opcode-list.ts b/packages/runtime/src/interpreter/opcode-list.ts index 7cd65e438..9a589e183 100644 --- a/packages/runtime/src/interpreter/opcode-list.ts +++ b/packages/runtime/src/interpreter/opcode-list.ts @@ -4600,33 +4600,33 @@ export class AcctParamsGet extends Op { const acctAddress = this.assertAlgorandAddress(stack.pop(), this.line); // get account from current context - const accountInfo = this.interpreter.getAccount(acctAddress, this.line); + const accountInfo = this.interpreter.getAccount(acctAddress, this.line, false); - if (accountInfo.balance() > 0) { - let value: StackElem = 0n; - switch (this.field) { - case "AcctBalance": { - value = BigInt(accountInfo.balance()); - break; - } - case "AcctMinBalance": { - value = BigInt(accountInfo.minBalance); - break; - } - case "AcctAuthAddr": { - if (accountInfo.getSpendAddress() === accountInfo.address) { - value = ZERO_ADDRESS; - } else { - value = Buffer.from(decodeAddress(accountInfo.getSpendAddress()).publicKey); - } - break; + let value: StackElem = 0n; + switch (this.field) { + case "AcctBalance": { + value = BigInt(accountInfo.balance()); + break; + } + case "AcctMinBalance": { + value = BigInt(accountInfo.minBalance); + break; + } + case "AcctAuthAddr": { + if (accountInfo.getSpendAddress() === accountInfo.address) { + value = ZERO_ADDRESS; + } else { + value = Buffer.from(decodeAddress(accountInfo.getSpendAddress()).publicKey); } + break; } - stack.push(value); + } + stack.push(value); + + if (accountInfo.balance() > 0) { stack.push(1n); } else { stack.push(0n); - stack.push(0n); } } } diff --git a/packages/runtime/test/src/interpreter/opcode-list.ts b/packages/runtime/test/src/interpreter/opcode-list.ts index 6a2306ec6..413246217 100644 --- a/packages/runtime/test/src/interpreter/opcode-list.ts +++ b/packages/runtime/test/src/interpreter/opcode-list.ts @@ -2383,7 +2383,8 @@ describe("Teal Opcodes", function () { describe("Gtxn", function () { before(function () { const tx = interpreter.runtime.ctx.tx; - // a) 'apas' represents 'foreignAssets', b) 'apfa' represents 'foreignApps' (id's of foreign apps) + // a) 'apas' represents 'foreignAssets', b) + // 'apfa' represents 'foreignApps' (id's of foreign apps) // https://developer.algorand.org/docs/reference/transactions/ const tx2 = { ...tx, fee: 2222, apas: [3033, 4044], apfa: [5005, 6006, 7077] }; interpreter.runtime.ctx.gtxs = [tx, tx2]; @@ -6299,7 +6300,7 @@ describe("Teal Opcodes", function () { assert.deepEqual(stack.pop(), ZERO_ADDRESS); }); - it("Shoud return Auth Address - rekey case", () => { + it("Shoud return Auth Address - rekey case", () => { // set spend key for alice is bob alice.rekeyTo(bob.address); interpreter.runtime.ctx.state.accounts.set(alice.address, alice); @@ -6309,16 +6310,30 @@ describe("Teal Opcodes", function () { assert.deepEqual(stack.pop(), decodeAddress(bob.address).publicKey); }); - // TODO: create un strictgetAccount - it.skip("Should return Auth Address", () => { + it("Should return balance with account own zero balance", () => { op = new AcctParamsGet(["AcctBalance"], 1, interpreter); stack.push(decodeAddress(zeroBalanceAddr).publicKey); op.execute(stack); - assert.equal(stack.pop(), 0n); // balance > 0 - assert.deepEqual(stack.pop(), 0n); + assert.equal(stack.pop(), 0n); // balance = 0 + assert.equal(stack.pop(), 0n); }); - + it("Should return min balance with account own zero balance", () => { + op = new AcctParamsGet(["AcctMinBalance"], 1, interpreter); + stack.push(decodeAddress(zeroBalanceAddr).publicKey); + op.execute(stack); + assert.equal(stack.pop(), 0n); // balance = 0 + assert.equal(stack.pop(), BigInt(ALGORAND_ACCOUNT_MIN_BALANCE)); + }); + + it("Should return Auth Address with account own zero balance", () => { + op = new AcctParamsGet(["AcctAuthAddr"], 1, interpreter); + stack.push(decodeAddress(zeroBalanceAddr).publicKey); + op.execute(stack); + assert.equal(stack.pop(), 0n); // balance = 0 + assert.deepEqual(stack.pop(), ZERO_ADDRESS); + }); + it("Should throw error when query unknow field", () => { expectRuntimeError( () => new AcctParamsGet(["Miles"], 1, interpreter), From 505c5bf6545f43adbdb4adb00454eebce334b53e Mon Sep 17 00:00:00 2001 From: Vu Vo Date: Fri, 11 Mar 2022 07:27:17 +0700 Subject: [PATCH 5/7] Apply suggestions from code review Co-authored-by: Robert Zaremba --- packages/runtime/src/errors/errors-list.ts | 2 +- packages/runtime/src/interpreter/interpreter.ts | 12 ++---------- packages/runtime/src/interpreter/opcode-list.ts | 3 +-- packages/runtime/test/src/interpreter/opcode-list.ts | 2 +- 4 files changed, 5 insertions(+), 14 deletions(-) diff --git a/packages/runtime/src/errors/errors-list.ts b/packages/runtime/src/errors/errors-list.ts index bc6416aa0..b91873ecf 100644 --- a/packages/runtime/src/errors/errors-list.ts +++ b/packages/runtime/src/errors/errors-list.ts @@ -378,7 +378,7 @@ maximun uint128`, description: `Execution mode not valid`, }, UNKNOWN_ACCT_FIELD: { - number: 1053, + number: 1055, message: "Account Field Error - Unknown Field: %field% at line %line% for teal version #%tealV%", title: "Account Field Error at line %line%", diff --git a/packages/runtime/src/interpreter/interpreter.ts b/packages/runtime/src/interpreter/interpreter.ts index d352a7d42..b1158df7c 100644 --- a/packages/runtime/src/interpreter/interpreter.ts +++ b/packages/runtime/src/interpreter/interpreter.ts @@ -160,12 +160,7 @@ export class Interpreter { compareArray(accountPk, decodeAddress(getApplicationAddress(appID)).publicKey) ) { const address = encodeAddress(pkBuffer); - let account = this.runtime.ctx.state.accounts.get(address); - // strict is turn on we will create account store - // we will create account if it not exist - if (!strict) { - account = this.createAccountIfAbsent(address); - } + let account = create ? this.createAccountIfAbsent(address) : this.runtime.ctx.state.accounts.get(address); return this.runtime.assertAccountDefined(address, account, line); } else { @@ -203,10 +198,7 @@ export class Interpreter { throw new Error("pk Buffer not found"); } address = encodeAddress(pkBuffer); - account = this.runtime.ctx.state.accounts.get(address); - if (!strict) { - account = this.createAccountIfAbsent(address); - } + account = create ? this.createAccountIfAbsent(address) : this.runtime.ctx.state.accounts.get(address); } } else { return this._getAccountFromAddr(accountRef, line, strict); diff --git a/packages/runtime/src/interpreter/opcode-list.ts b/packages/runtime/src/interpreter/opcode-list.ts index 9a589e183..bd5652cd9 100644 --- a/packages/runtime/src/interpreter/opcode-list.ts +++ b/packages/runtime/src/interpreter/opcode-list.ts @@ -4569,8 +4569,7 @@ export class AcctParamsGet extends Op { readonly line: number; readonly field: string; /** - * Asserts 1 arguments are passed. - * @param args Expected arguments: [] // none + * @param args Expected arguments: [account_param] * @param line line number in TEAL file * @param interpreter interpreter object */ diff --git a/packages/runtime/test/src/interpreter/opcode-list.ts b/packages/runtime/test/src/interpreter/opcode-list.ts index 413246217..8c5c5a29f 100644 --- a/packages/runtime/test/src/interpreter/opcode-list.ts +++ b/packages/runtime/test/src/interpreter/opcode-list.ts @@ -6358,7 +6358,7 @@ describe("Teal Opcodes", function () { ) }); - it("Should throw error if top element in stack is not address type", () => { + it("Should throw error if top element in stack is not an address", () => { op = new AcctParamsGet(["AcctBalance"], 1, interpreter); stack.push(parsing.stringToBytes("ABCDE")); From 06a3ac1e294ea7a9c505e99be475c26052bb6cb5 Mon Sep 17 00:00:00 2001 From: Vu Vo Date: Fri, 11 Mar 2022 14:51:16 +0700 Subject: [PATCH 6/7] apply the reviewer suggestions --- .../runtime/src/interpreter/interpreter.ts | 37 ++++++++++++------- .../runtime/src/interpreter/opcode-list.ts | 3 +- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/packages/runtime/src/interpreter/interpreter.ts b/packages/runtime/src/interpreter/interpreter.ts index b1158df7c..26f6eafcb 100644 --- a/packages/runtime/src/interpreter/interpreter.ts +++ b/packages/runtime/src/interpreter/interpreter.ts @@ -109,27 +109,34 @@ export class Interpreter { } /** - * Create new account with `address` if this is undefined in ctx and return - * else return state of this account in ctx. + * Create new account with `address` if this is undefined in ctx. + * return state of this account in ctx. * @param addr address we want to query. */ private createAccountIfAbsent(addr: AccountAddress): AccountStoreI { let account = this.runtime.ctx.state.accounts.get(addr); if (!account) { - account = new AccountStore(0, {addr, sk: new Uint8Array(0)}); + account = new AccountStore(0, { addr, sk: new Uint8Array(0) }); this.runtime.ctx.state.accounts.set(addr, account); - } + } return account; } - + /** * Beginning from TEALv4, user can directly pass address instead of index to Txn.Accounts. * However, the address must still be present in tx.Accounts OR should be equal to Txn.Sender + * When `create` flag is true we will throws exception if account is not found. + * When `create` flag is false we will create new account and add it to context. * @param accountPk public key of account * @param line line number in TEAL file + * @param create create flag * https://developer.algorand.org/articles/introducing-algorand-virtual-machine-avm-09-release/ */ - private _getAccountFromAddr(accountPk: Uint8Array, line: number, strict = true): AccountStoreI { + private _getAccountFromAddr( + accountPk: Uint8Array, + line: number, + create: boolean + ): AccountStoreI { const txAccounts = this.runtime.ctx.tx.apat; // tx.Accounts array const appID = this.runtime.ctx.tx.apid ?? 0; if (this.tealVersion <= 3) { @@ -160,7 +167,9 @@ export class Interpreter { compareArray(accountPk, decodeAddress(getApplicationAddress(appID)).publicKey) ) { const address = encodeAddress(pkBuffer); - let account = create ? this.createAccountIfAbsent(address) : this.runtime.ctx.state.accounts.get(address); + const account = create + ? this.createAccountIfAbsent(address) + : this.runtime.ctx.state.accounts.get(address); return this.runtime.assertAccountDefined(address, account, line); } else { @@ -174,14 +183,14 @@ export class Interpreter { /** * Queries account by accountIndex or `ctx.tx.snd` (if `accountIndex==0`). * If account address is passed, then queries account by address. - * when `strict` is true we will throws exception if account is not found. - * when `strict` is false we will create new account and add it to context. + * When `create` flag is true we will throws exception if account is not found. + * When `create` flag is false we will create new account and add it to context. * @param accountRef index of account to fetch from account list * @param line line number - * @param strict strict flag + * @param create create flag, default is true * NOTE: index 0 represents txn sender account */ - getAccount(accountRef: StackElem, line: number, strict = true): AccountStoreI { + getAccount(accountRef: StackElem, line: number, create = false): AccountStoreI { let account: AccountStoreI | undefined; let address: string; if (typeof accountRef === "bigint") { @@ -198,10 +207,12 @@ export class Interpreter { throw new Error("pk Buffer not found"); } address = encodeAddress(pkBuffer); - account = create ? this.createAccountIfAbsent(address) : this.runtime.ctx.state.accounts.get(address); + account = create + ? this.createAccountIfAbsent(address) + : this.runtime.ctx.state.accounts.get(address); } } else { - return this._getAccountFromAddr(accountRef, line, strict); + return this._getAccountFromAddr(accountRef, line, create); } return this.runtime.assertAccountDefined(address, account, line); diff --git a/packages/runtime/src/interpreter/opcode-list.ts b/packages/runtime/src/interpreter/opcode-list.ts index bd5652cd9..d68c81168 100644 --- a/packages/runtime/src/interpreter/opcode-list.ts +++ b/packages/runtime/src/interpreter/opcode-list.ts @@ -4599,7 +4599,8 @@ export class AcctParamsGet extends Op { const acctAddress = this.assertAlgorandAddress(stack.pop(), this.line); // get account from current context - const accountInfo = this.interpreter.getAccount(acctAddress, this.line, false); + // not `create` flag = true + const accountInfo = this.interpreter.getAccount(acctAddress, this.line, true); let value: StackElem = 0n; switch (this.field) { From 6f8ce9dd8624a015a7001b3074dbbfbf3826ed7e Mon Sep 17 00:00:00 2001 From: Vu Vo Date: Fri, 11 Mar 2022 15:17:11 +0700 Subject: [PATCH 7/7] update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a01da8e84..dd48510b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,7 +39,8 @@ Added: - Teal V6 support: - Add new opcode bsqrt and divw([##605](https://github.com/scale-it/algo-builder/pull/605)). - Add new opcode gloadss([#606](https://github.com/scale-it/algo-builder/pull/606)). - + - Add new opcode acct_params_get([#618](https://github.com/scale-it/algo-builder/pull/618)). + ### Template improvements - Using App instead of Lsig (Smart Signature) in `examples/dao` to simplify deposit management.