From 14a42d74090d4bac2af1542245314e5a4b3e787a Mon Sep 17 00:00:00 2001 From: Hang Su Date: Tue, 6 Jun 2023 11:04:17 -0400 Subject: [PATCH 01/11] generate model file from new oas2 for simulate trace config and exec trace --- src/client/v2/algod/models/types.ts | 223 ++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) diff --git a/src/client/v2/algod/models/types.ts b/src/client/v2/algod/models/types.ts index ace6b61e4..c49a37d71 100644 --- a/src/client/v2/algod/models/types.ts +++ b/src/client/v2/algod/models/types.ts @@ -3364,6 +3364,11 @@ export class SimulateRequest extends BaseModel { */ public allowMoreLogging?: boolean; + /** + * An object that configures simulation execution trace. + */ + public execTraceConfig?: SimulateTraceConfig; + /** * Applies extra opcode budget during simulation for each transaction group. */ @@ -3375,29 +3380,34 @@ export class SimulateRequest extends BaseModel { * @param allowEmptySignatures - Allow transactions without signatures to be simulated as if they had correct * signatures. * @param allowMoreLogging - Lifts limits on log opcode usage during simulation. + * @param execTraceConfig - An object that configures simulation execution trace. * @param extraOpcodeBudget - Applies extra opcode budget during simulation for each transaction group. */ constructor({ txnGroups, allowEmptySignatures, allowMoreLogging, + execTraceConfig, extraOpcodeBudget, }: { txnGroups: SimulateRequestTransactionGroup[]; allowEmptySignatures?: boolean; allowMoreLogging?: boolean; + execTraceConfig?: SimulateTraceConfig; extraOpcodeBudget?: number | bigint; }) { super(); this.txnGroups = txnGroups; this.allowEmptySignatures = allowEmptySignatures; this.allowMoreLogging = allowMoreLogging; + this.execTraceConfig = execTraceConfig; this.extraOpcodeBudget = extraOpcodeBudget; this.attribute_map = { txnGroups: 'txn-groups', allowEmptySignatures: 'allow-empty-signatures', allowMoreLogging: 'allow-more-logging', + execTraceConfig: 'exec-trace-config', extraOpcodeBudget: 'extra-opcode-budget', }; } @@ -3415,6 +3425,10 @@ export class SimulateRequest extends BaseModel { ), allowEmptySignatures: data['allow-empty-signatures'], allowMoreLogging: data['allow-more-logging'], + execTraceConfig: + typeof data['exec-trace-config'] !== 'undefined' + ? SimulateTraceConfig.from_obj_for_encoding(data['exec-trace-config']) + : undefined, extraOpcodeBudget: data['extra-opcode-budget'], }); /* eslint-enable dot-notation */ @@ -3486,6 +3500,11 @@ export class SimulateResponse extends BaseModel { */ public evalOverrides?: SimulationEvalOverrides; + /** + * An object that configures simulation execution trace. + */ + public execTraceConfig?: SimulateTraceConfig; + /** * Creates a new `SimulateResponse` object. * @param lastRound - The round immediately preceding this simulation. State changes through this @@ -3495,29 +3514,34 @@ export class SimulateResponse extends BaseModel { * @param evalOverrides - The set of parameters and limits override during simulation. If this set of * parameters is present, then evaluation parameters may differ from standard * evaluation in certain ways. + * @param execTraceConfig - An object that configures simulation execution trace. */ constructor({ lastRound, txnGroups, version, evalOverrides, + execTraceConfig, }: { lastRound: number | bigint; txnGroups: SimulateTransactionGroupResult[]; version: number | bigint; evalOverrides?: SimulationEvalOverrides; + execTraceConfig?: SimulateTraceConfig; }) { super(); this.lastRound = lastRound; this.txnGroups = txnGroups; this.version = version; this.evalOverrides = evalOverrides; + this.execTraceConfig = execTraceConfig; this.attribute_map = { lastRound: 'last-round', txnGroups: 'txn-groups', version: 'version', evalOverrides: 'eval-overrides', + execTraceConfig: 'exec-trace-config', }; } @@ -3546,6 +3570,42 @@ export class SimulateResponse extends BaseModel { data['eval-overrides'] ) : undefined, + execTraceConfig: + typeof data['exec-trace-config'] !== 'undefined' + ? SimulateTraceConfig.from_obj_for_encoding(data['exec-trace-config']) + : undefined, + }); + /* eslint-enable dot-notation */ + } +} + +/** + * An object that configures simulation execution trace. + */ +export class SimulateTraceConfig extends BaseModel { + /** + * A boolean option for opting in execution trace features simulation endpoint. + */ + public enable?: boolean; + + /** + * Creates a new `SimulateTraceConfig` object. + * @param enable - A boolean option for opting in execution trace features simulation endpoint. + */ + constructor({ enable }: { enable?: boolean }) { + super(); + this.enable = enable; + + this.attribute_map = { + enable: 'enable', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): SimulateTraceConfig { + /* eslint-disable dot-notation */ + return new SimulateTraceConfig({ + enable: data['enable'], }); /* eslint-enable dot-notation */ } @@ -3663,6 +3723,12 @@ export class SimulateTransactionResult extends BaseModel { */ public appBudgetConsumed?: number | bigint; + /** + * The execution trace of calling an app or a logic sig, containing the inner app + * call trace in a recursive way. + */ + public execTrace?: SimulationTransactionExecTrace; + /** * Budget used during execution of a logic sig transaction. */ @@ -3674,25 +3740,31 @@ export class SimulateTransactionResult extends BaseModel { * includes confirmation details like the round and reward details. * @param appBudgetConsumed - Budget used during execution of an app call transaction. This value includes * budged used by inner app calls spawned by this transaction. + * @param execTrace - The execution trace of calling an app or a logic sig, containing the inner app + * call trace in a recursive way. * @param logicSigBudgetConsumed - Budget used during execution of a logic sig transaction. */ constructor({ txnResult, appBudgetConsumed, + execTrace, logicSigBudgetConsumed, }: { txnResult: PendingTransactionResponse; appBudgetConsumed?: number | bigint; + execTrace?: SimulationTransactionExecTrace; logicSigBudgetConsumed?: number | bigint; }) { super(); this.txnResult = txnResult; this.appBudgetConsumed = appBudgetConsumed; + this.execTrace = execTrace; this.logicSigBudgetConsumed = logicSigBudgetConsumed; this.attribute_map = { txnResult: 'txn-result', appBudgetConsumed: 'app-budget-consumed', + execTrace: 'exec-trace', logicSigBudgetConsumed: 'logic-sig-budget-consumed', }; } @@ -3711,6 +3783,12 @@ export class SimulateTransactionResult extends BaseModel { data['txn-result'] ), appBudgetConsumed: data['app-budget-consumed'], + execTrace: + typeof data['exec-trace'] !== 'undefined' + ? SimulationTransactionExecTrace.from_obj_for_encoding( + data['exec-trace'] + ) + : undefined, logicSigBudgetConsumed: data['logic-sig-budget-consumed'], }); /* eslint-enable dot-notation */ @@ -3792,6 +3870,151 @@ export class SimulationEvalOverrides extends BaseModel { } } +/** + * The set of trace information and effect from evaluating a single opcode. + */ +export class SimulationOpcodeTraceUnit extends BaseModel { + /** + * The program counter of the current opcode being evaluated. + */ + public pc: number | bigint; + + /** + * The indexes of the traces for inner transactions spawned by this opcode, if any. + */ + public spawnedInners?: (number | bigint)[]; + + /** + * Creates a new `SimulationOpcodeTraceUnit` object. + * @param pc - The program counter of the current opcode being evaluated. + * @param spawnedInners - The indexes of the traces for inner transactions spawned by this opcode, if any. + */ + constructor({ + pc, + spawnedInners, + }: { + pc: number | bigint; + spawnedInners?: (number | bigint)[]; + }) { + super(); + this.pc = pc; + this.spawnedInners = spawnedInners; + + this.attribute_map = { + pc: 'pc', + spawnedInners: 'spawned-inners', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): SimulationOpcodeTraceUnit { + /* eslint-disable dot-notation */ + if (typeof data['pc'] === 'undefined') + throw new Error(`Response is missing required field 'pc': ${data}`); + return new SimulationOpcodeTraceUnit({ + pc: data['pc'], + spawnedInners: data['spawned-inners'], + }); + /* eslint-enable dot-notation */ + } +} + +/** + * The execution trace of calling an app or a logic sig, containing the inner app + * call trace in a recursive way. + */ +export class SimulationTransactionExecTrace extends BaseModel { + /** + * Program trace that contains a trace of opcode effects in an approval program. + */ + public approvalProgramTrace?: SimulationOpcodeTraceUnit[]; + + /** + * Program trace that contains a trace of opcode effects in a clear state program. + */ + public clearStateProgramTrace?: SimulationOpcodeTraceUnit[]; + + /** + * An array of SimulationTransactionExecTrace representing the execution trace of + * any inner transactions executed. + */ + public innerTrace?: SimulationTransactionExecTrace[]; + + /** + * Program trace that contains a trace of opcode effects in a logic sig. + */ + public logicSigTrace?: SimulationOpcodeTraceUnit[]; + + /** + * Creates a new `SimulationTransactionExecTrace` object. + * @param approvalProgramTrace - Program trace that contains a trace of opcode effects in an approval program. + * @param clearStateProgramTrace - Program trace that contains a trace of opcode effects in a clear state program. + * @param innerTrace - An array of SimulationTransactionExecTrace representing the execution trace of + * any inner transactions executed. + * @param logicSigTrace - Program trace that contains a trace of opcode effects in a logic sig. + */ + constructor({ + approvalProgramTrace, + clearStateProgramTrace, + innerTrace, + logicSigTrace, + }: { + approvalProgramTrace?: SimulationOpcodeTraceUnit[]; + clearStateProgramTrace?: SimulationOpcodeTraceUnit[]; + innerTrace?: SimulationTransactionExecTrace[]; + logicSigTrace?: SimulationOpcodeTraceUnit[]; + }) { + super(); + this.approvalProgramTrace = approvalProgramTrace; + this.clearStateProgramTrace = clearStateProgramTrace; + this.innerTrace = innerTrace; + this.logicSigTrace = logicSigTrace; + + this.attribute_map = { + approvalProgramTrace: 'approval-program-trace', + clearStateProgramTrace: 'clear-state-program-trace', + innerTrace: 'inner-trace', + logicSigTrace: 'logic-sig-trace', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding( + data: Record + ): SimulationTransactionExecTrace { + /* eslint-disable dot-notation */ + return new SimulationTransactionExecTrace({ + approvalProgramTrace: + typeof data['approval-program-trace'] !== 'undefined' + ? data['approval-program-trace'].map( + SimulationOpcodeTraceUnit.from_obj_for_encoding + ) + : undefined, + clearStateProgramTrace: + typeof data['clear-state-program-trace'] !== 'undefined' + ? data['clear-state-program-trace'].map( + SimulationOpcodeTraceUnit.from_obj_for_encoding + ) + : undefined, + innerTrace: + typeof data['inner-trace'] !== 'undefined' + ? data['inner-trace'].map( + SimulationTransactionExecTrace.from_obj_for_encoding + ) + : undefined, + logicSigTrace: + typeof data['logic-sig-trace'] !== 'undefined' + ? data['logic-sig-trace'].map( + SimulationOpcodeTraceUnit.from_obj_for_encoding + ) + : undefined, + }); + /* eslint-enable dot-notation */ + } +} + /** * Represents a state proof and its corresponding message */ From edb5304f29f4c77935725dbd126bae5c524b5691 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Sun, 30 Jul 2023 11:40:50 -0400 Subject: [PATCH 02/11] generate model --- src/client/v2/algod/models/types.ts | 218 ++++++++++++++++++++++++++-- 1 file changed, 208 insertions(+), 10 deletions(-) diff --git a/src/client/v2/algod/models/types.ts b/src/client/v2/algod/models/types.ts index c49a37d71..e2c8e50dc 100644 --- a/src/client/v2/algod/models/types.ts +++ b/src/client/v2/algod/models/types.ts @@ -1397,6 +1397,69 @@ export class AssetParams extends BaseModel { } } +/** + * Represents an AVM value. + */ +export class AvmValue extends BaseModel { + /** + * value type. Value `1` refers to **bytes**, value `2` refers to **uint64** + */ + public type: number | bigint; + + /** + * bytes value. + */ + public bytes?: Uint8Array; + + /** + * uint value. + */ + public uint?: number | bigint; + + /** + * Creates a new `AvmValue` object. + * @param type - value type. Value `1` refers to **bytes**, value `2` refers to **uint64** + * @param bytes - bytes value. + * @param uint - uint value. + */ + constructor({ + type, + bytes, + uint, + }: { + type: number | bigint; + bytes?: string | Uint8Array; + uint?: number | bigint; + }) { + super(); + this.type = type; + this.bytes = + typeof bytes === 'string' + ? new Uint8Array(Buffer.from(bytes, 'base64')) + : bytes; + this.uint = uint; + + this.attribute_map = { + type: 'type', + bytes: 'bytes', + uint: 'uint', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): AvmValue { + /* eslint-disable dot-notation */ + if (typeof data['type'] === 'undefined') + throw new Error(`Response is missing required field 'type': ${data}`); + return new AvmValue({ + type: data['type'], + bytes: data['bytes'], + uint: data['uint'], + }); + /* eslint-enable dot-notation */ + } +} + /** * Hash of a block header. */ @@ -1493,6 +1556,11 @@ export class Box extends BaseModel { */ public name: Uint8Array; + /** + * The round for which this information is relevant + */ + public round: number | bigint; + /** * (value) box value, base64 encoded. */ @@ -1501,13 +1569,16 @@ export class Box extends BaseModel { /** * Creates a new `Box` object. * @param name - (name) box name, base64 encoded + * @param round - The round for which this information is relevant * @param value - (value) box value, base64 encoded. */ constructor({ name, + round, value, }: { name: string | Uint8Array; + round: number | bigint; value: string | Uint8Array; }) { super(); @@ -1515,6 +1586,7 @@ export class Box extends BaseModel { typeof name === 'string' ? new Uint8Array(Buffer.from(name, 'base64')) : name; + this.round = round; this.value = typeof value === 'string' ? new Uint8Array(Buffer.from(value, 'base64')) @@ -1522,6 +1594,7 @@ export class Box extends BaseModel { this.attribute_map = { name: 'name', + round: 'round', value: 'value', }; } @@ -1531,10 +1604,13 @@ export class Box extends BaseModel { /* eslint-disable dot-notation */ if (typeof data['name'] === 'undefined') throw new Error(`Response is missing required field 'name': ${data}`); + if (typeof data['round'] === 'undefined') + throw new Error(`Response is missing required field 'round': ${data}`); if (typeof data['value'] === 'undefined') throw new Error(`Response is missing required field 'value': ${data}`); return new Box({ name: data['name'], + round: data['round'], value: data['value'], }); /* eslint-enable dot-notation */ @@ -2580,8 +2656,8 @@ export class LedgerStateDeltaForTransactionGroup extends BaseModel { this.ids = ids; this.attribute_map = { - delta: 'delta', - ids: 'ids', + delta: 'Delta', + ids: 'Ids', }; } @@ -2590,15 +2666,15 @@ export class LedgerStateDeltaForTransactionGroup extends BaseModel { data: Record ): LedgerStateDeltaForTransactionGroup { /* eslint-disable dot-notation */ - if (typeof data['delta'] === 'undefined') - throw new Error(`Response is missing required field 'delta': ${data}`); - if (!Array.isArray(data['ids'])) + if (typeof data['Delta'] === 'undefined') + throw new Error(`Response is missing required field 'Delta': ${data}`); + if (!Array.isArray(data['Ids'])) throw new Error( - `Response is missing required array field 'ids': ${data}` + `Response is missing required array field 'Ids': ${data}` ); return new LedgerStateDeltaForTransactionGroup({ - delta: data['delta'], - ids: data['ids'], + delta: data['Delta'], + ids: data['Ids'], }); /* eslint-enable dot-notation */ } @@ -3344,6 +3420,59 @@ export class PostTransactionsResponse extends BaseModel { } } +/** + * A write operation into a scratch slot. + */ +export class ScratchChange extends BaseModel { + /** + * Represents an AVM value. + */ + public newValue: AvmValue; + + /** + * The scratch slot written. + */ + public slot: number | bigint; + + /** + * Creates a new `ScratchChange` object. + * @param newValue - Represents an AVM value. + * @param slot - The scratch slot written. + */ + constructor({ + newValue, + slot, + }: { + newValue: AvmValue; + slot: number | bigint; + }) { + super(); + this.newValue = newValue; + this.slot = slot; + + this.attribute_map = { + newValue: 'new-value', + slot: 'slot', + }; + } + + // eslint-disable-next-line camelcase + static from_obj_for_encoding(data: Record): ScratchChange { + /* eslint-disable dot-notation */ + if (typeof data['new-value'] === 'undefined') + throw new Error( + `Response is missing required field 'new-value': ${data}` + ); + if (typeof data['slot'] === 'undefined') + throw new Error(`Response is missing required field 'slot': ${data}`); + return new ScratchChange({ + newValue: AvmValue.from_obj_for_encoding(data['new-value']), + slot: data['slot'], + }); + /* eslint-enable dot-notation */ + } +} + /** * Request type for simulation endpoint. */ @@ -3588,16 +3717,44 @@ export class SimulateTraceConfig extends BaseModel { */ public enable?: boolean; + /** + * A boolean option enabling returning scratch slot changes together with execution + * trace during simulation. + */ + public scratchChange?: boolean; + + /** + * A boolean option enabling returning stack changes together with execution trace + * during simulation. + */ + public stackChange?: boolean; + /** * Creates a new `SimulateTraceConfig` object. * @param enable - A boolean option for opting in execution trace features simulation endpoint. + * @param scratchChange - A boolean option enabling returning scratch slot changes together with execution + * trace during simulation. + * @param stackChange - A boolean option enabling returning stack changes together with execution trace + * during simulation. */ - constructor({ enable }: { enable?: boolean }) { + constructor({ + enable, + scratchChange, + stackChange, + }: { + enable?: boolean; + scratchChange?: boolean; + stackChange?: boolean; + }) { super(); this.enable = enable; + this.scratchChange = scratchChange; + this.stackChange = stackChange; this.attribute_map = { enable: 'enable', + scratchChange: 'scratch-change', + stackChange: 'stack-change', }; } @@ -3606,6 +3763,8 @@ export class SimulateTraceConfig extends BaseModel { /* eslint-disable dot-notation */ return new SimulateTraceConfig({ enable: data['enable'], + scratchChange: data['scratch-change'], + stackChange: data['stack-change'], }); /* eslint-enable dot-notation */ } @@ -3879,30 +4038,60 @@ export class SimulationOpcodeTraceUnit extends BaseModel { */ public pc: number | bigint; + /** + * The writes into scratch slots. + */ + public scratchChanges?: ScratchChange[]; + /** * The indexes of the traces for inner transactions spawned by this opcode, if any. */ public spawnedInners?: (number | bigint)[]; + /** + * The values added by this opcode to the stack. + */ + public stackAdditions?: AvmValue[]; + + /** + * The number of deleted stack values by this opcode. + */ + public stackPopCount?: number | bigint; + /** * Creates a new `SimulationOpcodeTraceUnit` object. * @param pc - The program counter of the current opcode being evaluated. + * @param scratchChanges - The writes into scratch slots. * @param spawnedInners - The indexes of the traces for inner transactions spawned by this opcode, if any. + * @param stackAdditions - The values added by this opcode to the stack. + * @param stackPopCount - The number of deleted stack values by this opcode. */ constructor({ pc, + scratchChanges, spawnedInners, + stackAdditions, + stackPopCount, }: { pc: number | bigint; + scratchChanges?: ScratchChange[]; spawnedInners?: (number | bigint)[]; + stackAdditions?: AvmValue[]; + stackPopCount?: number | bigint; }) { super(); this.pc = pc; + this.scratchChanges = scratchChanges; this.spawnedInners = spawnedInners; + this.stackAdditions = stackAdditions; + this.stackPopCount = stackPopCount; this.attribute_map = { pc: 'pc', + scratchChanges: 'scratch-changes', spawnedInners: 'spawned-inners', + stackAdditions: 'stack-additions', + stackPopCount: 'stack-pop-count', }; } @@ -3915,7 +4104,16 @@ export class SimulationOpcodeTraceUnit extends BaseModel { throw new Error(`Response is missing required field 'pc': ${data}`); return new SimulationOpcodeTraceUnit({ pc: data['pc'], + scratchChanges: + typeof data['scratch-changes'] !== 'undefined' + ? data['scratch-changes'].map(ScratchChange.from_obj_for_encoding) + : undefined, spawnedInners: data['spawned-inners'], + stackAdditions: + typeof data['stack-additions'] !== 'undefined' + ? data['stack-additions'].map(AvmValue.from_obj_for_encoding) + : undefined, + stackPopCount: data['stack-pop-count'], }); /* eslint-enable dot-notation */ } @@ -4373,7 +4571,7 @@ export class TransactionGroupLedgerStateDeltasForRoundResponse extends BaseModel this.deltas = deltas; this.attribute_map = { - deltas: 'deltas', + deltas: 'Deltas', }; } From 95916513005d4d3ea82cbb9bac9d82a6783a0198 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Mon, 31 Jul 2023 14:56:04 -0400 Subject: [PATCH 03/11] step for js sdk, working --- .test-env | 2 +- tests/cucumber/steps/steps.js | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/.test-env b/.test-env index 049289d7b..463721601 100644 --- a/.test-env +++ b/.test-env @@ -1,6 +1,6 @@ # Configs for testing repo download: SDK_TESTING_URL="https://github.com/algorand/algorand-sdk-testing" -SDK_TESTING_BRANCH="master" +SDK_TESTING_BRANCH="simulate-exec-trace-stack-scratch" SDK_TESTING_HARNESS="test-harness" INSTALL_ONLY=0 diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index 6f5542332..c8b500063 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -4815,6 +4815,22 @@ module.exports = function getSteps(options) { } ); + Then( + 'I allow exec trace options {string} on that simulate request.', + async function (execTraceOptions) { + const optionList = execTraceOptions.split(','); + + assert.ok(this.simulateRequest); + this.simulateRequest.execTraceConfig = new algosdk.modelsv2.SimulateTraceConfig( + { + enable: true, + scratchChange: optionList.includes('scratch'), + stackChange: optionList.includes('stack'), + } + ); + } + ); + When('we make a Ready call', async function () { await this.v2Client.ready().do(); }); From 8ce7d87651477ba1d519bf15d923169a9739c60a Mon Sep 17 00:00:00 2001 From: Hang Su Date: Mon, 31 Jul 2023 15:21:54 -0400 Subject: [PATCH 04/11] minor --- tests/cucumber/steps/steps.js | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index c8b500063..bd5d67c52 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -4821,6 +4821,7 @@ module.exports = function getSteps(options) { const optionList = execTraceOptions.split(','); assert.ok(this.simulateRequest); + // TODO: check if this is the best practice this.simulateRequest.execTraceConfig = new algosdk.modelsv2.SimulateTraceConfig( { enable: true, From bb561955346d5d1cfbe55c22246d06a9bec92fe7 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Mon, 31 Jul 2023 18:26:10 -0400 Subject: [PATCH 05/11] the step implementation --- tests/cucumber/steps/steps.js | 94 +++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index bd5d67c52..8267420a1 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -4832,6 +4832,100 @@ module.exports = function getSteps(options) { } ); + Then( + '{int}th unit in the {string} trace at txn-groups path {string} should add to stack {string}, pop from stack by {int}, write to {string} scratch slot by {string}.', + async function ( + unitIndex, + traceType, + txnGroupPath, + stackAddition, + stackPopCount, + slotID, + scratchWriteContent + ) { + const unitFinder = (txnGroupPathStr, traceTypeStr, unitIndexInt) => { + const txnGroupPathSplit = txnGroupPathStr + .split(',') + .filter((r) => r !== '') + .map(Number); + assert.ok(txnGroupPathSplit.length > 0); + + let traces = this.simulateResponse.txnGroups[0].txnResults[ + txnGroupPathSplit[0] + ].execTrace; + assert.ok(traces); + + for (let i = 1; i < txnGroupPathSplit.length; i++) { + traces = traces.innerTrace[txnGroupPathSplit[i]]; + assert.ok(traces); + } + + let trace = traces.approvalProgramTrace; + + if (traceTypeStr === 'approval') { + trace = traces.approvalProgramTrace; + } else if (traceTypeStr === 'clearState') { + trace = traces.clearStateProgramTrace; + } else if (traceTypeStr === 'logic') { + trace = traces.logicSigTrace; + } + const changeUnit = trace[unitIndexInt]; + return changeUnit; + }; + + const avmValueCheck = (scratchWriteStr, avmValue) => { + const [scratchType, writeContent] = scratchWriteStr.split(','); + + if (scratchType === 'uint64') { + assert.equal(avmValue.type, 2); + assert.ok(avmValue.uint); + assert.equal(avmValue.uint, Number(writeContent)); + } else if (scratchType === 'bytes') { + assert.equal(avmValue.type, 1); + assert.ok(avmValue.bytes); + assert.equal(avmValue.bytes, writeContent); + } + }; + + assert.ok(this.simulateResponse); + + const changeUnit = unitFinder(txnGroupPath, traceType, unitIndex); + assert.equal(changeUnit.stackPopCount, stackPopCount); + + const stackAdditionSplit = stackAddition + .split(',') + .filter((r) => r !== ''); + + if (changeUnit.stackAdditions) { + assert.equal( + changeUnit.stackAdditions.length, + stackAdditionSplit.length + ); + for (let i = 0; i < stackAdditionSplit.length; i++) { + avmValueCheck(stackAdditionSplit[i], changeUnit.stackAdditions[i]); + } + } else { + assert.equal(stackAdditionSplit.length, 0); + } + + if (slotID !== 'none') { + assert.equal(changeUnit.scratchChanges.length, 1); + + const slotIDint = Number(slotID); + assert.equal(changeUnit.scratchChanges[0].slot, slotIDint); + assert.notEqual(scratchWriteContent, 'none'); + + const newValue = changeUnit.scratchChanges[0]?.newValue; + assert.ok(newValue); + + avmValueCheck(scratchWriteContent, newValue); + } else { + assert.ok(!changeUnit.scratchChanges); + assert.equal(scratchWriteContent, 'none'); + } + } + ); + When('we make a Ready call', async function () { await this.v2Client.ready().do(); }); From c1adcad5733f03101815ce9160138aa0d234b97f Mon Sep 17 00:00:00 2001 From: Hang Su Date: Mon, 31 Jul 2023 19:47:57 -0400 Subject: [PATCH 06/11] tag --- tests/cucumber/integration.tags | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/cucumber/integration.tags b/tests/cucumber/integration.tags index 32a59c3a6..fc6d7852b 100644 --- a/tests/cucumber/integration.tags +++ b/tests/cucumber/integration.tags @@ -16,3 +16,4 @@ @simulate @simulate.lift_log_limits @simulate.extra_opcode_budget +@simulate.exec_trace_with_stack_scratch From 0886acf13a18e391d23c8c08eb8d0a8b807dee0c Mon Sep 17 00:00:00 2001 From: Hang Su Date: Tue, 1 Aug 2023 11:00:29 -0400 Subject: [PATCH 07/11] oops --- tests/cucumber/steps/steps.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index 8267420a1..dcd18b086 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -4873,17 +4873,17 @@ module.exports = function getSteps(options) { return changeUnit; }; - const avmValueCheck = (scratchWriteStr, avmValue) => { - const [scratchType, writeContent] = scratchWriteStr.split(','); + const avmValueCheck = (stringLiteral, avmValue) => { + const [avmType, value] = stringLiteral.split(':'); - if (scratchType === 'uint64') { + if (avmType === 'uint64') { assert.equal(avmValue.type, 2); assert.ok(avmValue.uint); - assert.equal(avmValue.uint, Number(writeContent)); - } else if (scratchType === 'bytes') { + assert.equal(avmValue.uint, Number(value)); + } else if (avmType === 'bytes') { assert.equal(avmValue.type, 1); assert.ok(avmValue.bytes); - assert.equal(avmValue.bytes, writeContent); + assert.equal(avmValue.bytes, value); } }; From 1da15d26d70c8893c2eb695df371a959fc6d0988 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Tue, 1 Aug 2023 11:20:37 -0400 Subject: [PATCH 08/11] bigint --- tests/cucumber/steps/steps.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index dcd18b086..edbd94d0c 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -4879,7 +4879,7 @@ module.exports = function getSteps(options) { if (avmType === 'uint64') { assert.equal(avmValue.type, 2); assert.ok(avmValue.uint); - assert.equal(avmValue.uint, Number(value)); + assert.equal(avmValue.uint, BigInt(value)); } else if (avmType === 'bytes') { assert.equal(avmValue.type, 1); assert.ok(avmValue.bytes); From 394a972ae08afc75abf701c7be53a33de63400b1 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Tue, 1 Aug 2023 16:08:39 -0400 Subject: [PATCH 09/11] minor --- tests/cucumber/steps/steps.js | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index edbd94d0c..241e6454d 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -4833,15 +4833,15 @@ module.exports = function getSteps(options) { ); Then( - '{int}th unit in the {string} trace at txn-groups path {string} should add to stack {string}, pop from stack by {int}, write to {string} scratch slot by {string}.', + '{int}th unit in the {string} trace at txn-groups path {string} should add value {string} to stack, pop {int} values from stack, write value {string} to scratch slot {string}.', async function ( unitIndex, traceType, txnGroupPath, stackAddition, stackPopCount, - slotID, - scratchWriteContent + scratchWriteContent, + slotID ) { const unitFinder = (txnGroupPathStr, traceTypeStr, unitIndexInt) => { const txnGroupPathSplit = txnGroupPathStr @@ -4883,14 +4883,23 @@ module.exports = function getSteps(options) { } else if (avmType === 'bytes') { assert.equal(avmValue.type, 1); assert.ok(avmValue.bytes); - assert.equal(avmValue.bytes, value); + assert.deepEqual( + avmValue.bytes, + makeUint8Array(Buffer.from(value, 'base64')) + ); + } else { + assert.fail('avmType should be either uint64 or bytes'); } }; assert.ok(this.simulateResponse); const changeUnit = unitFinder(txnGroupPath, traceType, unitIndex); - assert.equal(changeUnit.stackPopCount, stackPopCount); + if (stackPopCount > 0) { + assert.equal(changeUnit.stackPopCount, stackPopCount); + } else { + assert.ok(!changeUnit.stackPopCount); + } const stackAdditionSplit = stackAddition .split(',') @@ -4908,12 +4917,12 @@ module.exports = function getSteps(options) { assert.equal(stackAdditionSplit.length, 0); } - if (slotID !== 'none') { + if (slotID !== '') { assert.equal(changeUnit.scratchChanges.length, 1); const slotIDint = Number(slotID); assert.equal(changeUnit.scratchChanges[0].slot, slotIDint); - assert.notEqual(scratchWriteContent, 'none'); + assert.notEqual(scratchWriteContent, ''); const newValue = changeUnit.scratchChanges[0]?.newValue; assert.ok(newValue); @@ -4921,7 +4930,7 @@ module.exports = function getSteps(options) { avmValueCheck(scratchWriteContent, newValue); } else { assert.ok(!changeUnit.scratchChanges); - assert.equal(scratchWriteContent, 'none'); + assert.equal(scratchWriteContent, ''); } } ); From bb5a76b69ad1143d5bf6eba01f6b336d42a1d2d6 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Wed, 9 Aug 2023 09:57:07 -0400 Subject: [PATCH 10/11] remove comment --- tests/cucumber/steps/steps.js | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index 241e6454d..d10accc98 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -4821,7 +4821,6 @@ module.exports = function getSteps(options) { const optionList = execTraceOptions.split(','); assert.ok(this.simulateRequest); - // TODO: check if this is the best practice this.simulateRequest.execTraceConfig = new algosdk.modelsv2.SimulateTraceConfig( { enable: true, From 3298a73b550cfd3f025752b51d7232dd6056b828 Mon Sep 17 00:00:00 2001 From: Hang Su <87964331+ahangsu@users.noreply.github.com> Date: Wed, 9 Aug 2023 12:23:02 -0400 Subject: [PATCH 11/11] Update .test-env --- .test-env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.test-env b/.test-env index 463721601..a321a7f1e 100644 --- a/.test-env +++ b/.test-env @@ -1,6 +1,6 @@ # Configs for testing repo download: SDK_TESTING_URL="https://github.com/algorand/algorand-sdk-testing" -SDK_TESTING_BRANCH="simulate-exec-trace-stack-scratch" +SDK_TESTING_BRANCH="V2" SDK_TESTING_HARNESS="test-harness" INSTALL_ONLY=0