From f7ec88dd58261fe93fb326264111dcb532beb8bf Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Thu, 26 May 2022 15:55:34 -0400 Subject: [PATCH 01/22] Initial work to define box types in appl calls --- src/makeTxn.ts | 1 + src/types/transactions/application.ts | 1 + src/types/transactions/base.ts | 20 ++++++++++++++++++++ src/types/transactions/encoded.ts | 7 +++++++ 4 files changed, 29 insertions(+) diff --git a/src/makeTxn.ts b/src/makeTxn.ts index d0782e6de..e8186ceb3 100644 --- a/src/makeTxn.ts +++ b/src/makeTxn.ts @@ -1104,6 +1104,7 @@ export function makeAssetTransferTxnWithSuggestedParamsFromObject( * @param lease - Lease a transaction * @param rekeyTo - String representation of the Algorand address that will be used to authorize all future transactions * @param extraPages - integer extra pages of memory to rent on creation of application + * @param boxes - Array of BoxReference, app ID and name of box to be accessed */ export function makeApplicationCreateTxn( from: AppCreateTxn['from'], diff --git a/src/types/transactions/application.ts b/src/types/transactions/application.ts index f5d2f19d3..9e86bea41 100644 --- a/src/types/transactions/application.ts +++ b/src/types/transactions/application.ts @@ -20,6 +20,7 @@ type SpecificParametersForCreate = Pick< | 'appForeignApps' | 'appForeignAssets' | 'extraPages' + | 'boxes' >; interface OverwritesForCreate { diff --git a/src/types/transactions/base.ts b/src/types/transactions/base.ts index 739c88e76..5e1bb87b6 100644 --- a/src/types/transactions/base.ts +++ b/src/types/transactions/base.ts @@ -125,6 +125,21 @@ export interface SuggestedParams { genesisHash: string; } +/** + * A grouping of the app ID and name of the box in an Uint8Array + */ +export interface BoxReference { + /** + * A unique application index + */ + appIndex: number; + + /** + * Name of box to reference + */ + name: Uint8Array; +} + /** * A full list of all available transaction parameters * @@ -385,4 +400,9 @@ export interface TransactionParams { * Int representing extra pages of memory to rent during an application create transaction. */ extraPages?: number; + + /** + * A grouping of the app ID and name of the box in an Uint8Array + */ + boxes?: BoxReference; } diff --git a/src/types/transactions/encoded.ts b/src/types/transactions/encoded.ts index bc5b9e96c..a50e8cb8f 100644 --- a/src/types/transactions/encoded.ts +++ b/src/types/transactions/encoded.ts @@ -2,6 +2,8 @@ * Interfaces for the encoded transaction object. Every property is labelled with its associated Transaction type property */ +import { BoxReference } from './base'; + export interface EncodedAssetParams { /** * assetTotal @@ -296,6 +298,11 @@ export interface EncodedTransaction { * extraPages */ apep?: number; + + /** + * boxes + */ + apbx?: BoxReference; } export interface EncodedSubsig { From 6a68e658290f809bd09b1f54554cf1efb8016e5c Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Thu, 26 May 2022 17:30:38 -0400 Subject: [PATCH 02/22] More changes to boxes field --- src/makeTxn.ts | 10 ++++++++-- src/transaction.ts | 13 +++++++++++++ src/types/transactions/encoded.ts | 16 +++++++++++++--- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/makeTxn.ts b/src/makeTxn.ts index e8186ceb3..5847dcf1f 100644 --- a/src/makeTxn.ts +++ b/src/makeTxn.ts @@ -1123,7 +1123,8 @@ export function makeApplicationCreateTxn( note?: AppCreateTxn['note'], lease?: AppCreateTxn['lease'], rekeyTo?: AppCreateTxn['reKeyTo'], - extraPages?: AppCreateTxn['extraPages'] + extraPages?: AppCreateTxn['extraPages'], + boxes?: AppCreateTxn['boxes'] ) { const o: AppCreateTxn = { type: TransactionType.appl, @@ -1145,6 +1146,7 @@ export function makeApplicationCreateTxn( lease, reKeyTo: rekeyTo, extraPages, + boxes, }; return new txnBuilder.Transaction(o); } @@ -1186,6 +1188,7 @@ export function makeApplicationCreateTxnFromObject( | 'lease' | 'rekeyTo' | 'extraPages' + | 'boxes' > > ) { @@ -1206,7 +1209,8 @@ export function makeApplicationCreateTxnFromObject( o.note, o.lease, o.rekeyTo, - o.extraPages + o.extraPages, + o.boxes ); } @@ -1786,6 +1790,7 @@ export function makeApplicationCallTxnFromObject( | 'lease' | 'rekeyTo' | 'extraPages' + | 'boxes' > & Partial< Pick< @@ -1830,6 +1835,7 @@ export function makeApplicationCallTxnFromObject( lease: options.lease, reKeyTo: options.rekeyTo, extraPages: options.extraPages, + boxes: options.boxes, }; return new txnBuilder.Transaction(o); } diff --git a/src/transaction.ts b/src/transaction.ts index 10825a43e..90951ea6f 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -8,6 +8,7 @@ import { TransactionParams, TransactionType, isTransactionType, + BoxReference, } from './types/transactions/base'; import AnyTransaction, { MustHaveSuggestedParams, @@ -110,6 +111,7 @@ interface TransactionStorageStructure nonParticipation?: boolean; group?: Buffer; extraPages?: number; + boxes?: BoxReference; } function getKeyregKey( @@ -198,6 +200,7 @@ export class Transaction implements TransactionStorageStructure { nonParticipation?: boolean; group?: Buffer; extraPages?: number; + boxes?: BoxReference; constructor({ ...transaction }: AnyTransaction) { // Populate defaults @@ -783,6 +786,10 @@ export class Transaction implements TransactionStorageStructure { apfa: this.appForeignApps, apas: this.appForeignAssets, apep: this.extraPages, + apbx: { + i: this.boxes.appIndex, + n: this.boxes.name, + }, }; if (this.reKeyTo !== undefined) { txn.rekey = Buffer.from(this.reKeyTo.publicKey); @@ -993,6 +1000,12 @@ export class Transaction implements TransactionStorageStructure { if (txnForEnc.apas !== undefined) { txn.appForeignAssets = txnForEnc.apas; } + if (txnForEnc.apbx !== undefined) { + txn.boxes = { + appIndex: txnForEnc.apbx.i, + name: txnForEnc.apbx.n, + }; + } } return txn; } diff --git a/src/types/transactions/encoded.ts b/src/types/transactions/encoded.ts index a50e8cb8f..b201987b1 100644 --- a/src/types/transactions/encoded.ts +++ b/src/types/transactions/encoded.ts @@ -2,8 +2,6 @@ * Interfaces for the encoded transaction object. Every property is labelled with its associated Transaction type property */ -import { BoxReference } from './base'; - export interface EncodedAssetParams { /** * assetTotal @@ -85,6 +83,18 @@ export interface EncodedGlobalStateSchema { nbs: number; } +export interface EncodedBoxReference { + /** + * appIndex + */ + i: number; + + /** + * name + */ + n: Uint8Array; +} + /** * A rough structure for the encoded transaction object. Every property is labelled with its associated Transaction type property */ @@ -302,7 +312,7 @@ export interface EncodedTransaction { /** * boxes */ - apbx?: BoxReference; + apbx?: EncodedBoxReference; } export interface EncodedSubsig { From 3996d72c8ff2886fb937751af16839713215fd38 Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Fri, 27 May 2022 16:41:15 -0400 Subject: [PATCH 03/22] Change boxes to array types, fix transactions, and tests --- src/composer.ts | 5 +++ src/makeTxn.ts | 47 ++++++++++++++++++++++----- src/transaction.ts | 34 +++++++++++++------ src/types/transactions/application.ts | 4 ++- src/types/transactions/base.ts | 2 +- src/types/transactions/encoded.ts | 2 +- tests/5.Transaction.js | 3 ++ 7 files changed, 76 insertions(+), 21 deletions(-) diff --git a/src/composer.ts b/src/composer.ts index 0117a9984..54bb61d8a 100644 --- a/src/composer.ts +++ b/src/composer.ts @@ -21,6 +21,7 @@ import { isTransactionWithSigner, } from './signer'; import { + BoxReference, OnApplicationComplete, SuggestedParams, } from './types/transactions/base'; @@ -197,6 +198,7 @@ export class AtomicTransactionComposer { numLocalInts, numLocalByteSlices, extraPages, + boxes, note, lease, rekeyTo, @@ -228,6 +230,8 @@ export class AtomicTransactionComposer { numLocalByteSlices?: number; /** The number of extra pages to allocate for the application's programs. Only set this if this is an application creation call. If omitted, defaults to 0. */ extraPages?: number; + /** The box references for this application call */ + boxes?: BoxReference[]; /** The note value for this application call */ note?: Uint8Array; /** The lease value for this application call */ @@ -442,6 +446,7 @@ export class AtomicTransactionComposer { numLocalInts, numLocalByteSlices, extraPages, + boxes, lease, note, rekeyTo, diff --git a/src/makeTxn.ts b/src/makeTxn.ts index 5847dcf1f..325b1064d 100644 --- a/src/makeTxn.ts +++ b/src/makeTxn.ts @@ -1100,11 +1100,11 @@ export function makeAssetTransferTxnWithSuggestedParamsFromObject( * @param accounts - Array of Address strings, any additional accounts to supply to the application * @param foreignApps - Array of int, any other apps used by the application, identified by index * @param foreignAssets - Array of int, any assets used by the application, identified by index + * @param boxes - Array of BoxReference, app ID and name of box to be accessed * @param note - Arbitrary data for sender to store * @param lease - Lease a transaction * @param rekeyTo - String representation of the Algorand address that will be used to authorize all future transactions * @param extraPages - integer extra pages of memory to rent on creation of application - * @param boxes - Array of BoxReference, app ID and name of box to be accessed */ export function makeApplicationCreateTxn( from: AppCreateTxn['from'], @@ -1120,11 +1120,11 @@ export function makeApplicationCreateTxn( accounts?: AppCreateTxn['appAccounts'], foreignApps?: AppCreateTxn['appForeignApps'], foreignAssets?: AppCreateTxn['appForeignAssets'], + boxes?: AppCreateTxn['boxes'], note?: AppCreateTxn['note'], lease?: AppCreateTxn['lease'], rekeyTo?: AppCreateTxn['reKeyTo'], - extraPages?: AppCreateTxn['extraPages'], - boxes?: AppCreateTxn['boxes'] + extraPages?: AppCreateTxn['extraPages'] ) { const o: AppCreateTxn = { type: TransactionType.appl, @@ -1142,11 +1142,11 @@ export function makeApplicationCreateTxn( appAccounts: accounts, appForeignApps: foreignApps, appForeignAssets: foreignAssets, + boxes, note, lease, reKeyTo: rekeyTo, extraPages, - boxes, }; return new txnBuilder.Transaction(o); } @@ -1184,11 +1184,11 @@ export function makeApplicationCreateTxnFromObject( | 'accounts' | 'foreignApps' | 'foreignAssets' + | 'boxes' | 'note' | 'lease' | 'rekeyTo' | 'extraPages' - | 'boxes' > > ) { @@ -1206,11 +1206,11 @@ export function makeApplicationCreateTxnFromObject( o.accounts, o.foreignApps, o.foreignAssets, + o.boxes, o.note, o.lease, o.rekeyTo, - o.extraPages, - o.boxes + o.extraPages ); } @@ -1232,6 +1232,7 @@ export function makeApplicationCreateTxnFromObject( * @param accounts - Array of Address strings, any additional accounts to supply to the application * @param foreignApps - Array of int, any other apps used by the application, identified by index * @param foreignAssets - Array of int, any assets used by the application, identified by index + * @param boxes - Array of BoxReference, app ID and name of box to be accessed * @param note - Arbitrary data for sender to store * @param lease - Lease a transaction * @param rekeyTo - String representation of the Algorand address that will be used to authorize all future transactions @@ -1246,6 +1247,7 @@ export function makeApplicationUpdateTxn( accounts?: AppUpdateTxn['appAccounts'], foreignApps?: AppUpdateTxn['appForeignApps'], foreignAssets?: AppUpdateTxn['appForeignAssets'], + boxes?: AppUpdateTxn['boxes'], note?: AppUpdateTxn['note'], lease?: AppUpdateTxn['lease'], rekeyTo?: AppUpdateTxn['reKeyTo'] @@ -1262,6 +1264,7 @@ export function makeApplicationUpdateTxn( appAccounts: accounts, appForeignApps: foreignApps, appForeignAssets: foreignAssets, + boxes, note, lease, reKeyTo: rekeyTo, @@ -1294,6 +1297,7 @@ export function makeApplicationUpdateTxnFromObject( | 'foreignApps' | 'foreignAssets' | 'note' + | 'boxes' | 'lease' | 'rekeyTo' > @@ -1309,6 +1313,7 @@ export function makeApplicationUpdateTxnFromObject( o.accounts, o.foreignApps, o.foreignAssets, + o.boxes, o.note, o.lease, o.rekeyTo @@ -1331,6 +1336,7 @@ export function makeApplicationUpdateTxnFromObject( * @param accounts - Array of Address strings, any additional accounts to supply to the application * @param foreignApps - Array of int, any other apps used by the application, identified by index * @param foreignAssets - Array of int, any assets used by the application, identified by index + * @param boxes - Array of BoxReference, app ID and name of box to be accessed * @param note - Arbitrary data for sender to store * @param lease - Lease a transaction * @param rekeyTo - String representation of the Algorand address that will be used to authorize all future transactions @@ -1343,6 +1349,7 @@ export function makeApplicationDeleteTxn( accounts?: AppDeleteTxn['appAccounts'], foreignApps?: AppDeleteTxn['appForeignApps'], foreignAssets?: AppDeleteTxn['appForeignAssets'], + boxes?: AppDeleteTxn['boxes'], note?: AppDeleteTxn['note'], lease?: AppDeleteTxn['lease'], rekeyTo?: AppDeleteTxn['reKeyTo'] @@ -1357,6 +1364,7 @@ export function makeApplicationDeleteTxn( appAccounts: accounts, appForeignApps: foreignApps, appForeignAssets: foreignAssets, + boxes, note, lease, reKeyTo: rekeyTo, @@ -1384,6 +1392,7 @@ export function makeApplicationDeleteTxnFromObject( | 'accounts' | 'foreignApps' | 'foreignAssets' + | 'boxes' | 'note' | 'lease' | 'rekeyTo' @@ -1398,6 +1407,7 @@ export function makeApplicationDeleteTxnFromObject( o.accounts, o.foreignApps, o.foreignAssets, + o.boxes, o.note, o.lease, o.rekeyTo @@ -1420,6 +1430,7 @@ export function makeApplicationDeleteTxnFromObject( * @param accounts - Array of Address strings, any additional accounts to supply to the application * @param foreignApps - Array of int, any other apps used by the application, identified by index * @param foreignAssets - Array of int, any assets used by the application, identified by index + * @param boxes - Array of BoxReference, app ID and name of box to be accessed * @param note - Arbitrary data for sender to store * @param lease - Lease a transaction * @param rekeyTo - String representation of the Algorand address that will be used to authorize all future transactions @@ -1432,6 +1443,7 @@ export function makeApplicationOptInTxn( accounts?: AppOptInTxn['appAccounts'], foreignApps?: AppOptInTxn['appForeignApps'], foreignAssets?: AppOptInTxn['appForeignAssets'], + boxes?: AppOptInTxn['boxes'], note?: AppOptInTxn['note'], lease?: AppOptInTxn['lease'], rekeyTo?: AppOptInTxn['reKeyTo'] @@ -1446,6 +1458,7 @@ export function makeApplicationOptInTxn( appAccounts: accounts, appForeignApps: foreignApps, appForeignAssets: foreignAssets, + boxes, note, lease, reKeyTo: rekeyTo, @@ -1473,6 +1486,7 @@ export function makeApplicationOptInTxnFromObject( | 'accounts' | 'foreignApps' | 'foreignAssets' + | 'boxes' | 'note' | 'lease' | 'rekeyTo' @@ -1487,6 +1501,7 @@ export function makeApplicationOptInTxnFromObject( o.accounts, o.foreignApps, o.foreignAssets, + o.boxes, o.note, o.lease, o.rekeyTo @@ -1509,6 +1524,7 @@ export function makeApplicationOptInTxnFromObject( * @param accounts - Array of Address strings, any additional accounts to supply to the application * @param foreignApps - Array of int, any other apps used by the application, identified by index * @param foreignAssets - Array of int, any assets used by the application, identified by index + * @param boxes - Array of BoxReference, app ID and name of box to be accessed * @param note - Arbitrary data for sender to store * @param lease - Lease a transaction * @param rekeyTo - String representation of the Algorand address that will be used to authorize all future transactions @@ -1521,6 +1537,7 @@ export function makeApplicationCloseOutTxn( accounts?: AppCloseOutTxn['appAccounts'], foreignApps?: AppCloseOutTxn['appForeignApps'], foreignAssets?: AppCloseOutTxn['appForeignAssets'], + boxes?: AppCloseOutTxn['boxes'], note?: AppCloseOutTxn['note'], lease?: AppCloseOutTxn['lease'], rekeyTo?: AppCloseOutTxn['reKeyTo'] @@ -1535,6 +1552,7 @@ export function makeApplicationCloseOutTxn( appAccounts: accounts, appForeignApps: foreignApps, appForeignAssets: foreignAssets, + boxes, note, lease, reKeyTo: rekeyTo, @@ -1562,6 +1580,7 @@ export function makeApplicationCloseOutTxnFromObject( | 'accounts' | 'foreignApps' | 'foreignAssets' + | 'boxes' | 'note' | 'lease' | 'rekeyTo' @@ -1576,6 +1595,7 @@ export function makeApplicationCloseOutTxnFromObject( o.accounts, o.foreignApps, o.foreignAssets, + o.boxes, o.note, o.lease, o.rekeyTo @@ -1598,6 +1618,7 @@ export function makeApplicationCloseOutTxnFromObject( * @param accounts - Array of Address strings, any additional accounts to supply to the application * @param foreignApps - Array of int, any other apps used by the application, identified by index * @param foreignAssets - Array of int, any assets used by the application, identified by index + * @param boxes - Array of BoxReference, app ID and name of box to be accessed * @param note - Arbitrary data for sender to store * @param lease - Lease a transaction * @param rekeyTo - String representation of the Algorand address that will be used to authorize all future transactions @@ -1610,6 +1631,7 @@ export function makeApplicationClearStateTxn( accounts?: AppClearStateTxn['appAccounts'], foreignApps?: AppClearStateTxn['appForeignApps'], foreignAssets?: AppClearStateTxn['appForeignAssets'], + boxes?: AppClearStateTxn['boxes'], note?: AppClearStateTxn['note'], lease?: AppClearStateTxn['lease'], rekeyTo?: AppClearStateTxn['reKeyTo'] @@ -1624,6 +1646,7 @@ export function makeApplicationClearStateTxn( appAccounts: accounts, appForeignApps: foreignApps, appForeignAssets: foreignAssets, + boxes, note, lease, reKeyTo: rekeyTo, @@ -1651,6 +1674,7 @@ export function makeApplicationClearStateTxnFromObject( | 'accounts' | 'foreignApps' | 'foreignAssets' + | 'boxes' | 'note' | 'lease' | 'rekeyTo' @@ -1665,6 +1689,7 @@ export function makeApplicationClearStateTxnFromObject( o.accounts, o.foreignApps, o.foreignAssets, + o.boxes, o.note, o.lease, o.rekeyTo @@ -1687,6 +1712,7 @@ export function makeApplicationClearStateTxnFromObject( * @param accounts - Array of Address strings, any additional accounts to supply to the application * @param foreignApps - Array of int, any other apps used by the application, identified by index * @param foreignAssets - Array of int, any assets used by the application, identified by index + * @param boxes - Array of BoxReference, app ID and name of box to be accessed * @param note - Arbitrary data for sender to store * @param lease - Lease a transaction * @param rekeyTo - String representation of the Algorand address that will be used to authorize all future transactions @@ -1699,6 +1725,7 @@ export function makeApplicationNoOpTxn( accounts?: AppNoOpTxn['appAccounts'], foreignApps?: AppNoOpTxn['appForeignApps'], foreignAssets?: AppNoOpTxn['appForeignAssets'], + boxes?: AppNoOpTxn['boxes'], note?: AppNoOpTxn['note'], lease?: AppNoOpTxn['lease'], rekeyTo?: AppNoOpTxn['reKeyTo'] @@ -1713,6 +1740,7 @@ export function makeApplicationNoOpTxn( appAccounts: accounts, appForeignApps: foreignApps, appForeignAssets: foreignAssets, + boxes, note, lease, reKeyTo: rekeyTo, @@ -1740,6 +1768,7 @@ export function makeApplicationNoOpTxnFromObject( | 'accounts' | 'foreignApps' | 'foreignAssets' + | 'boxes' | 'note' | 'lease' | 'rekeyTo' @@ -1754,6 +1783,7 @@ export function makeApplicationNoOpTxnFromObject( o.accounts, o.foreignApps, o.foreignAssets, + o.boxes, o.note, o.lease, o.rekeyTo @@ -1786,6 +1816,7 @@ export function makeApplicationCallTxnFromObject( | 'accounts' | 'foreignApps' | 'foreignAssets' + | 'boxes' | 'note' | 'lease' | 'rekeyTo' @@ -1831,11 +1862,11 @@ export function makeApplicationCallTxnFromObject( appAccounts: options.accounts, appForeignApps: options.foreignApps, appForeignAssets: options.foreignAssets, + boxes: options.boxes, note: options.note, lease: options.lease, reKeyTo: options.rekeyTo, extraPages: options.extraPages, - boxes: options.boxes, }; return new txnBuilder.Transaction(o); } diff --git a/src/transaction.ts b/src/transaction.ts index 90951ea6f..ec893511b 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -111,7 +111,7 @@ interface TransactionStorageStructure nonParticipation?: boolean; group?: Buffer; extraPages?: number; - boxes?: BoxReference; + boxes?: BoxReference[]; } function getKeyregKey( @@ -200,7 +200,7 @@ export class Transaction implements TransactionStorageStructure { nonParticipation?: boolean; group?: Buffer; extraPages?: number; - boxes?: BoxReference; + boxes?: BoxReference[]; constructor({ ...transaction }: AnyTransaction) { // Populate defaults @@ -418,6 +418,20 @@ export class Transaction implements TransactionStorageStructure { ); }); } + if (txn.boxes !== undefined) { + if (!Array.isArray(txn.boxes)) + throw Error('boxes must be an Array of BoxReference.'); + txn.boxes = txn.boxes.slice(); + txn.boxes.forEach((box) => { + if ( + !Number.isSafeInteger(box.appIndex) || + box.name.constructor !== Uint8Array + ) + throw Error('each element of boxes must be a Uint8Array.'); + }); + } else { + txn.boxes = []; + } if ( txn.assetMetadataHash !== undefined && txn.assetMetadataHash.length !== 0 @@ -786,10 +800,10 @@ export class Transaction implements TransactionStorageStructure { apfa: this.appForeignApps, apas: this.appForeignAssets, apep: this.extraPages, - apbx: { - i: this.boxes.appIndex, - n: this.boxes.name, - }, + apbx: this.boxes.map((bx) => ({ + i: bx.appIndex, + n: bx.name, + })), }; if (this.reKeyTo !== undefined) { txn.rekey = Buffer.from(this.reKeyTo.publicKey); @@ -1001,10 +1015,10 @@ export class Transaction implements TransactionStorageStructure { txn.appForeignAssets = txnForEnc.apas; } if (txnForEnc.apbx !== undefined) { - txn.boxes = { - appIndex: txnForEnc.apbx.i, - name: txnForEnc.apbx.n, - }; + txn.boxes = txnForEnc.apbx.map((box) => ({ + appIndex: box.i, + name: box.n, + })); } } return txn; diff --git a/src/types/transactions/application.ts b/src/types/transactions/application.ts index 9e86bea41..701240f25 100644 --- a/src/types/transactions/application.ts +++ b/src/types/transactions/application.ts @@ -19,8 +19,8 @@ type SpecificParametersForCreate = Pick< | 'appAccounts' | 'appForeignApps' | 'appForeignAssets' - | 'extraPages' | 'boxes' + | 'extraPages' >; interface OverwritesForCreate { @@ -46,6 +46,7 @@ type SpecificParametersForUpdate = Pick< | 'appAccounts' | 'appForeignApps' | 'appForeignAssets' + | 'boxes' >; interface OverwritesForUpdate { @@ -69,6 +70,7 @@ type SpecificParametersForDelete = Pick< | 'appAccounts' | 'appForeignApps' | 'appForeignAssets' + | 'boxes' >; interface OverwritesForDelete { diff --git a/src/types/transactions/base.ts b/src/types/transactions/base.ts index 5e1bb87b6..f94ebdcbd 100644 --- a/src/types/transactions/base.ts +++ b/src/types/transactions/base.ts @@ -404,5 +404,5 @@ export interface TransactionParams { /** * A grouping of the app ID and name of the box in an Uint8Array */ - boxes?: BoxReference; + boxes?: BoxReference[]; } diff --git a/src/types/transactions/encoded.ts b/src/types/transactions/encoded.ts index b201987b1..beb6b46d3 100644 --- a/src/types/transactions/encoded.ts +++ b/src/types/transactions/encoded.ts @@ -312,7 +312,7 @@ export interface EncodedTransaction { /** * boxes */ - apbx?: EncodedBoxReference; + apbx?: EncodedBoxReference[]; } export interface EncodedSubsig { diff --git a/tests/5.Transaction.js b/tests/5.Transaction.js index 710d16e22..bec0fda1d 100644 --- a/tests/5.Transaction.js +++ b/tests/5.Transaction.js @@ -35,6 +35,7 @@ describe('Sign', () => { ]; const appForeignApps = [17, 200]; const appForeignAssets = [7, 8, 9]; + const boxes = [{ appIndex: 0, name: Uint8Array.from([0]) }]; const o = { from: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', fee: 10, @@ -48,6 +49,7 @@ describe('Sign', () => { appAccounts, appForeignApps, appForeignAssets, + boxes, }; const txn = new algosdk.Transaction(o); assert.deepStrictEqual(appArgs, [ @@ -527,6 +529,7 @@ describe('Sign', () => { extraPages: 2, foreignApps: [3, 4], foreignAssets: [5, 6], + boxes: [{ appIndex: 0, name: Uint8Array.from([0]) }], lease: Uint8Array.from(new Array(32).fill(7)), note: new Uint8Array(Buffer.from('note value')), rekeyTo: 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM', From 67dcb162401efb7d5897cde5ab3e4b74d29e7d1e Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Tue, 31 May 2022 15:58:33 -0400 Subject: [PATCH 04/22] Write box translation tests --- src/boxStorage.ts | 38 ++++++++++++++++++++++ src/transaction.ts | 16 ++++++--- src/types/transactions/encoded.ts | 4 +-- tests/5.Transaction.js | 54 +++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 src/boxStorage.ts diff --git a/src/boxStorage.ts b/src/boxStorage.ts new file mode 100644 index 000000000..7de981c53 --- /dev/null +++ b/src/boxStorage.ts @@ -0,0 +1,38 @@ +import { BoxReference } from './types/transactions/base'; + +function translateBoxReference( + reference: BoxReference, + foreignApps: number[], + appIndex: number +) { + const referenceId = reference.appIndex; + const referenceName = reference.name; + // Foreign apps start from index 1; index 0 is its own app ID. + let index = foreignApps.indexOf(referenceId) + 1; + if (index === 0) { + // Check if the app referenced is itself after checking the foreign apps array + + if (referenceId === 0 || referenceId === appIndex) { + index = 0; + } else { + throw new Error( + `Box ref with appId ${referenceId} not in foreign-apps ${appIndex}` + ); + } + } + return { i: index, n: referenceName }; +} + +/** + * translateBoxReferences translates an array of BoxReferences with app IDs + * into an array of EncodedBoxReferences with foreign indices. + */ +export function translateBoxReferences( + references: BoxReference[], + foreignApps: number[], + appIndex: number +) { + return references.map((bx) => + translateBoxReference(bx, foreignApps, appIndex) + ); +} diff --git a/src/transaction.ts b/src/transaction.ts index ec893511b..7e1f9e2d0 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -19,6 +19,7 @@ import AnyTransaction, { EncodedLogicSig, } from './types/transactions'; import { Address } from './types/address'; +import { translateBoxReferences } from './boxStorage'; const ALGORAND_TRANSACTION_LENGTH = 52; export const ALGORAND_MIN_TX_FEE = 1000; // version v5 @@ -800,10 +801,11 @@ export class Transaction implements TransactionStorageStructure { apfa: this.appForeignApps, apas: this.appForeignAssets, apep: this.extraPages, - apbx: this.boxes.map((bx) => ({ - i: bx.appIndex, - n: bx.name, - })), + apbx: translateBoxReferences( + this.boxes, + this.appForeignApps, + this.appIndex + ), }; if (this.reKeyTo !== undefined) { txn.rekey = Buffer.from(this.reKeyTo.publicKey); @@ -1016,7 +1018,11 @@ export class Transaction implements TransactionStorageStructure { } if (txnForEnc.apbx !== undefined) { txn.boxes = txnForEnc.apbx.map((box) => ({ - appIndex: box.i, + // Translate foreign app index to app ID + appIndex: + box.i !== 0 || box.i !== txn.appIndex + ? txn.appForeignApps[box.i - 1] + : txn.appIndex, name: box.n, })); } diff --git a/src/types/transactions/encoded.ts b/src/types/transactions/encoded.ts index beb6b46d3..2fdd7e48d 100644 --- a/src/types/transactions/encoded.ts +++ b/src/types/transactions/encoded.ts @@ -85,12 +85,12 @@ export interface EncodedGlobalStateSchema { export interface EncodedBoxReference { /** - * appIndex + * index of the app ID in the foreign apps array */ i: number; /** - * name + * box name */ n: Uint8Array; } diff --git a/tests/5.Transaction.js b/tests/5.Transaction.js index bec0fda1d..9dfe7e781 100644 --- a/tests/5.Transaction.js +++ b/tests/5.Transaction.js @@ -1,5 +1,6 @@ const assert = require('assert'); const algosdk = require('../index'); +const { translateBoxReferences } = require('../src/boxStorage'); const group = require('../src/group'); describe('Sign', () => { @@ -66,6 +67,7 @@ describe('Sign', () => { assert.ok(txn.appAccounts !== appAccounts); assert.ok(txn.appForeignApps !== appForeignApps); assert.ok(txn.appForeignAssets !== appForeignAssets); + assert.ok(txn.boxes !== boxes); }); it('should not complain on a missing note', () => { @@ -1518,5 +1520,57 @@ describe('Sign', () => { assert.deepStrictEqual(txgroup[0].group, txgroup[1].group); }); + it('should be able to translate box references to encoded references', () => { + const testCases = [ + [ + [{ appIndex: 100, name: [0, 1, 2, 3] }], + [100], + 9999, + [{ i: 1, n: [0, 1, 2, 3] }], + ], + [ + [ + { appIndex: 0, name: [0, 1, 2, 3] }, + { appIndex: 9999, name: [4, 5, 6, 7] }, + ], + [100], + 9999, + [ + { i: 0, n: [0, 1, 2, 3] }, + { i: 0, n: [4, 5, 6, 7] }, + ], + ], + [ + [{ appIndex: 100, name: [0, 1, 2, 3] }], + [100], + 100, + [{ i: 1, n: [0, 1, 2, 3] }], + ], + [ + [ + { appIndex: 7777, name: [0, 1, 2, 3] }, + { appIndex: 8888, name: [4, 5, 6, 7] }, + ], + [100, 7777, 8888, 9999], + 9999, + [ + { i: 2, n: [0, 1, 2, 3] }, + { i: 3, n: [4, 5, 6, 7] }, + ], + ], + ]; + for (const testCase of testCases) { + const expected = testCase[3]; + const actual = translateBoxReferences( + testCase[0], + testCase[1], + testCase[2] + ); + assert.deepStrictEqual(expected.length, actual.length); + for (let i = 0; i < expected.length; i++) { + assert.deepStrictEqual(expected[i], actual[i]); + } + } + }); }); }); From 720ba89ac5b44599e93ce191f142cc27351cefaf Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Tue, 31 May 2022 17:56:08 -0400 Subject: [PATCH 05/22] Add cucumber test steps --- tests/cucumber/docker/run_docker.sh | 2 +- tests/cucumber/steps/steps.js | 60 ++++++++++++++++++++++++----- 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/tests/cucumber/docker/run_docker.sh b/tests/cucumber/docker/run_docker.sh index 72e6bb977..5db4bb447 100755 --- a/tests/cucumber/docker/run_docker.sh +++ b/tests/cucumber/docker/run_docker.sh @@ -7,7 +7,7 @@ rm -rf test-harness rm -rf tests/cucumber/features # clone test harness -git clone --single-branch --branch master https://github.com/algorand/algorand-sdk-testing.git test-harness +git clone --single-branch --branch box-reference https://github.com/algorand/algorand-sdk-testing.git test-harness # move feature files and example files to destination mv test-harness/features tests/cucumber/features diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index ff6716232..d31ed1494 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -4021,8 +4021,28 @@ module.exports = function getSteps(options) { return appArgs; } + function splitAndProcessBoxReferences(boxRefs) { + const splitRefs = boxRefs.split(','); + const boxRefArray = []; + let appIndex = 0; + + for (let i = 0; i < splitRefs.length; i++) { + if (i % 2 === 0) { + appIndex = splitRefs[i]; + } else { + boxRefArray.push( + algosdk.BoxReference( + appIndex, + makeUint8Array(Buffer.from(splitRefs[1])) + ) + ); + } + } + return boxRefArray; + } + When( - 'I build an application transaction with operation {string}, application-id {int}, sender {string}, approval-program {string}, clear-program {string}, global-bytes {int}, global-ints {int}, local-bytes {int}, local-ints {int}, app-args {string}, foreign-apps {string}, foreign-assets {string}, app-accounts {string}, fee {int}, first-valid {int}, last-valid {int}, genesis-hash {string}, extra-pages {int}', + 'I build an application transaction with operation {string}, application-id {int}, sender {string}, approval-program {string}, clear-program {string}, global-bytes {int}, global-ints {int}, local-bytes {int}, local-ints {int}, app-args {string}, foreign-apps {string}, foreign-assets {string}, app-accounts {string}, fee {int}, first-valid {int}, last-valid {int}, genesis-hash {string}, extra-pages {int}, boxes {string}', async function ( operationString, appIndex, @@ -4041,7 +4061,8 @@ module.exports = function getSteps(options) { firstValid, lastValid, genesisHashBase64, - extraPages + extraPages, + boxesCommaSeparatedString ) { // operation string to enum const operation = operationStringToEnum(operationString); @@ -4091,6 +4112,11 @@ module.exports = function getSteps(options) { if (appAccountsCommaSeparatedString !== '') { appAccounts = appAccountsCommaSeparatedString.split(','); } + // split and process box references + let boxes; + if (boxesCommaSeparatedString !== '') { + boxes = splitAndProcessBoxReferences(boxesCommaSeparatedString); + } // build suggested params object const sp = { genesisHash: genesisHashBase64, @@ -4109,7 +4135,8 @@ module.exports = function getSteps(options) { appArgs, appAccounts, foreignApps, - foreignAssets + foreignAssets, + boxes ); return; case 'create': @@ -4127,6 +4154,7 @@ module.exports = function getSteps(options) { appAccounts, foreignApps, foreignAssets, + boxes, undefined, undefined, undefined, @@ -4143,7 +4171,8 @@ module.exports = function getSteps(options) { appArgs, appAccounts, foreignApps, - foreignAssets + foreignAssets, + boxes ); return; case 'optin': @@ -4154,7 +4183,8 @@ module.exports = function getSteps(options) { appArgs, appAccounts, foreignApps, - foreignAssets + foreignAssets, + boxes ); return; case 'delete': @@ -4165,7 +4195,8 @@ module.exports = function getSteps(options) { appArgs, appAccounts, foreignApps, - foreignAssets + foreignAssets, + boxes ); return; case 'clear': @@ -4176,7 +4207,8 @@ module.exports = function getSteps(options) { appArgs, appAccounts, foreignApps, - foreignAssets + foreignAssets, + boxes ); return; case 'closeout': @@ -4187,7 +4219,8 @@ module.exports = function getSteps(options) { appArgs, appAccounts, foreignApps, - foreignAssets + foreignAssets, + boxes ); return; default: @@ -4263,7 +4296,7 @@ module.exports = function getSteps(options) { ); Given( - 'I build an application transaction with the transient account, the current application, suggested params, operation {string}, approval-program {string}, clear-program {string}, global-bytes {int}, global-ints {int}, local-bytes {int}, local-ints {int}, app-args {string}, foreign-apps {string}, foreign-assets {string}, app-accounts {string}, extra-pages {int}', + 'I build an application transaction with the transient account, the current application, suggested params, operation {string}, approval-program {string}, clear-program {string}, global-bytes {int}, global-ints {int}, local-bytes {int}, local-ints {int}, app-args {string}, foreign-apps {string}, foreign-assets {string}, app-accounts {string}, extra-pages {int}, boxes {string}', async function ( operationString, approvalProgramFile, @@ -4276,7 +4309,8 @@ module.exports = function getSteps(options) { foreignAppsCommaSeparatedString, foreignAssetsCommaSeparatedString, appAccountsCommaSeparatedString, - extraPages + extraPages, + boxesCommaSeparatedString ) { if (operationString === 'create') { this.currentApplicationIndex = 0; @@ -4330,6 +4364,11 @@ module.exports = function getSteps(options) { if (appAccountsCommaSeparatedString !== '') { appAccounts = appAccountsCommaSeparatedString.split(','); } + // split and process box references + let boxes; + if (boxesCommaSeparatedString !== '') { + boxes = splitAndProcessBoxReferences(boxesCommaSeparatedString); + } const sp = await this.v2Client.getTransactionParams().do(); if (sp.firstRound === 0) sp.firstRound = 1; const o = { @@ -4348,6 +4387,7 @@ module.exports = function getSteps(options) { appAccounts, appForeignApps: foreignApps, appForeignAssets: foreignAssets, + boxes, extraPages, }; this.txn = new algosdk.Transaction(o); From 7ba69132e034b8bd114c7915881393cd4dbb6b0e Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Wed, 1 Jun 2022 14:59:49 -0400 Subject: [PATCH 06/22] Revert pretty print --- src/boxStorage.ts | 4 +--- src/transaction.ts | 9 ++++++++- tests/cucumber/steps/steps.js | 12 +++++------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/boxStorage.ts b/src/boxStorage.ts index 7de981c53..0b6ea86bb 100644 --- a/src/boxStorage.ts +++ b/src/boxStorage.ts @@ -15,9 +15,7 @@ function translateBoxReference( if (referenceId === 0 || referenceId === appIndex) { index = 0; } else { - throw new Error( - `Box ref with appId ${referenceId} not in foreign-apps ${appIndex}` - ); + throw new Error(`Box ref with appId ${referenceId} not in foreign-apps`); } } return { i: index, n: referenceName }; diff --git a/src/transaction.ts b/src/transaction.ts index 7e1f9e2d0..8e0998ead 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -428,7 +428,9 @@ export class Transaction implements TransactionStorageStructure { !Number.isSafeInteger(box.appIndex) || box.name.constructor !== Uint8Array ) - throw Error('each element of boxes must be a Uint8Array.'); + throw Error( + 'box app index must be a number and name must be an Uint8Array.' + ); }); } else { txn.boxes = []; @@ -844,6 +846,11 @@ export class Transaction implements TransactionStorageStructure { if (!txn.apan) delete txn.apan; if (!txn.apfa || !txn.apfa.length) delete txn.apfa; if (!txn.apas || !txn.apas.length) delete txn.apas; + for (const box of txn.apbx) { + if (!box.i) delete box.i; + if (!box.n) delete box.n; + } + if (!txn.apbx || !txn.apbx.length) delete txn.apbx; if (!txn.apat || !txn.apat.length) delete txn.apat; if (!txn.apep) delete txn.apep; if (txn.grp === undefined) delete txn.grp; diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index d31ed1494..b7b3afa36 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -4028,14 +4028,12 @@ module.exports = function getSteps(options) { for (let i = 0; i < splitRefs.length; i++) { if (i % 2 === 0) { - appIndex = splitRefs[i]; + appIndex = parseInt(splitRefs[i]); } else { - boxRefArray.push( - algosdk.BoxReference( - appIndex, - makeUint8Array(Buffer.from(splitRefs[1])) - ) - ); + boxRefArray.push({ + appIndex, + name: makeUint8Array(Buffer.from(splitRefs[i])), + }); } } return boxRefArray; From 5aa0264f73a31c079ad0d525873bb960af75562d Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Wed, 1 Jun 2022 17:06:30 -0400 Subject: [PATCH 07/22] Fix decoding for txn boxes --- src/boxStorage.ts | 2 +- src/composer.ts | 4 +++- src/makeTxn.ts | 1 - src/transaction.ts | 4 +--- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/boxStorage.ts b/src/boxStorage.ts index 0b6ea86bb..96a466d47 100644 --- a/src/boxStorage.ts +++ b/src/boxStorage.ts @@ -11,7 +11,6 @@ function translateBoxReference( let index = foreignApps.indexOf(referenceId) + 1; if (index === 0) { // Check if the app referenced is itself after checking the foreign apps array - if (referenceId === 0 || referenceId === appIndex) { index = 0; } else { @@ -30,6 +29,7 @@ export function translateBoxReferences( foreignApps: number[], appIndex: number ) { + if (!references) return []; return references.map((bx) => translateBoxReference(bx, foreignApps, appIndex) ); diff --git a/src/composer.ts b/src/composer.ts index 54bb61d8a..92bb8b10b 100644 --- a/src/composer.ts +++ b/src/composer.ts @@ -317,6 +317,8 @@ export class AtomicTransactionComposer { const refArgTypes: ABIReferenceType[] = []; const refArgValues: ABIValue[] = []; const refArgIndexToBasicArgIndex: Map = new Map(); + // TODO: Box encoding for ABI + const boxReferences: BoxReference[] = !boxes ? [] : boxes; for (let i = 0; i < methodArgs.length; i++) { let argType = method.args[i].type; @@ -437,6 +439,7 @@ export class AtomicTransactionComposer { accounts: foreignAccounts, foreignApps, foreignAssets, + boxes: boxReferences, onComplete: onComplete == null ? OnApplicationComplete.NoOpOC : onComplete, approvalProgram, @@ -446,7 +449,6 @@ export class AtomicTransactionComposer { numLocalInts, numLocalByteSlices, extraPages, - boxes, lease, note, rekeyTo, diff --git a/src/makeTxn.ts b/src/makeTxn.ts index 325b1064d..db59e94b4 100644 --- a/src/makeTxn.ts +++ b/src/makeTxn.ts @@ -1821,7 +1821,6 @@ export function makeApplicationCallTxnFromObject( | 'lease' | 'rekeyTo' | 'extraPages' - | 'boxes' > & Partial< Pick< diff --git a/src/transaction.ts b/src/transaction.ts index 8e0998ead..299ed9969 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -195,13 +195,13 @@ export class Transaction implements TransactionStorageStructure { appAccounts?: Address[]; appForeignApps?: number[]; appForeignAssets?: number[]; + boxes?: BoxReference[]; type?: TransactionType; flatFee: boolean; reKeyTo?: Address; nonParticipation?: boolean; group?: Buffer; extraPages?: number; - boxes?: BoxReference[]; constructor({ ...transaction }: AnyTransaction) { // Populate defaults @@ -432,8 +432,6 @@ export class Transaction implements TransactionStorageStructure { 'box app index must be a number and name must be an Uint8Array.' ); }); - } else { - txn.boxes = []; } if ( txn.assetMetadataHash !== undefined && From a2addcb71c4c0ff241c729d50fc217e052b8e81e Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Thu, 2 Jun 2022 14:40:17 -0400 Subject: [PATCH 08/22] Revise tests to make box args similar to app args --- tests/cucumber/steps/steps.js | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index b7b3afa36..f36abfc3d 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -3993,6 +3993,21 @@ module.exports = function getSteps(options) { return makeUint8Array(data); } + function processAppArgs(subArg) { + switch (subArg[0]) { + case 'str': + return makeUint8Array(Buffer.from(subArg[1])); + case 'int': + return makeUint8Array([parseInt(subArg[1])]); + case 'addr': + return algosdk.decodeAddress(subArg[1]).publicKey; + case 'b64': + return makeUint8Array(Buffer.from(subArg[1], 'base64')); + default: + throw Error(`did not recognize app arg of type${subArg[0]}`); + } + } + function splitAndProcessAppArgs(inArgs) { const splitArgs = inArgs.split(','); const subArgs = []; @@ -4001,22 +4016,7 @@ module.exports = function getSteps(options) { }); const appArgs = []; subArgs.forEach((subArg) => { - switch (subArg[0]) { - case 'str': - appArgs.push(makeUint8Array(Buffer.from(subArg[1]))); - break; - case 'int': - appArgs.push(makeUint8Array([parseInt(subArg[1])])); - break; - case 'addr': - appArgs.push(algosdk.decodeAddress(subArg[1]).publicKey); - break; - case 'b64': - appArgs.push(Buffer.from(subArg[1], 'base64')); - break; - default: - throw Error(`did not recognize app arg of type${subArg[0]}`); - } + appArgs.push(processAppArgs(subArg)); }); return appArgs; } @@ -4030,9 +4030,10 @@ module.exports = function getSteps(options) { if (i % 2 === 0) { appIndex = parseInt(splitRefs[i]); } else { + const refArg = splitRefs[i].split(':'); boxRefArray.push({ appIndex, - name: makeUint8Array(Buffer.from(splitRefs[i])), + name: processAppArgs(refArg), }); } } From 18fbd8b0e930c305596a0ab11772a260f37173be Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Thu, 9 Jun 2022 19:55:37 -0400 Subject: [PATCH 09/22] Clean up some code and add a blank test case --- src/boxStorage.ts | 12 ++++++------ tests/5.Transaction.js | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/boxStorage.ts b/src/boxStorage.ts index 96a466d47..49b8a4e91 100644 --- a/src/boxStorage.ts +++ b/src/boxStorage.ts @@ -8,16 +8,16 @@ function translateBoxReference( const referenceId = reference.appIndex; const referenceName = reference.name; // Foreign apps start from index 1; index 0 is its own app ID. - let index = foreignApps.indexOf(referenceId) + 1; - if (index === 0) { + try { + const index = foreignApps.indexOf(referenceId) + 1; // Check if the app referenced is itself after checking the foreign apps array - if (referenceId === 0 || referenceId === appIndex) { - index = 0; - } else { + if (referenceId !== 0 && referenceId !== appIndex) { throw new Error(`Box ref with appId ${referenceId} not in foreign-apps`); } + return { i: index, n: referenceName }; + } catch (TypeError) { + throw new Error(`Box ref with appId ${referenceId} not in foreign-apps`); } - return { i: index, n: referenceName }; } /** diff --git a/tests/5.Transaction.js b/tests/5.Transaction.js index 9dfe7e781..4dc83d95d 100644 --- a/tests/5.Transaction.js +++ b/tests/5.Transaction.js @@ -1528,6 +1528,7 @@ describe('Sign', () => { 9999, [{ i: 1, n: [0, 1, 2, 3] }], ], + [[], [], 9999, []], [ [ { appIndex: 0, name: [0, 1, 2, 3] }, From 0c3c4e1a6daec714a65e2f62b3670f3151410bb2 Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Thu, 9 Jun 2022 20:07:33 -0400 Subject: [PATCH 10/22] Fix err case --- src/boxStorage.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/boxStorage.ts b/src/boxStorage.ts index 49b8a4e91..0493cf566 100644 --- a/src/boxStorage.ts +++ b/src/boxStorage.ts @@ -7,17 +7,18 @@ function translateBoxReference( ) { const referenceId = reference.appIndex; const referenceName = reference.name; + let index = 0; // Foreign apps start from index 1; index 0 is its own app ID. try { - const index = foreignApps.indexOf(referenceId) + 1; - // Check if the app referenced is itself after checking the foreign apps array - if (referenceId !== 0 && referenceId !== appIndex) { - throw new Error(`Box ref with appId ${referenceId} not in foreign-apps`); - } - return { i: index, n: referenceName }; - } catch (TypeError) { + index = foreignApps.indexOf(referenceId) + 1; + } catch (err) { throw new Error(`Box ref with appId ${referenceId} not in foreign-apps`); } + // Check if the app referenced is itself after checking the foreign apps array + if (index === 0 && referenceId !== 0 && referenceId !== appIndex) { + throw new Error(`Box ref with appId ${referenceId} not in foreign-apps`); + } + return { i: index, n: referenceName }; } /** From 7b15d3ab49232f7da9741695f29ec68702e230db Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Thu, 9 Jun 2022 20:31:12 -0400 Subject: [PATCH 11/22] Fix box ref catch --- src/boxStorage.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/boxStorage.ts b/src/boxStorage.ts index 0493cf566..24211639b 100644 --- a/src/boxStorage.ts +++ b/src/boxStorage.ts @@ -12,9 +12,12 @@ function translateBoxReference( try { index = foreignApps.indexOf(referenceId) + 1; } catch (err) { - throw new Error(`Box ref with appId ${referenceId} not in foreign-apps`); + // Foreign app array cannot be empty unless the reference ID is itself. + if (referenceId !== 0 && referenceId !== appIndex) { + throw new Error(`Box ref with appId ${referenceId} not in foreign-apps`); + } } - // Check if the app referenced is itself after checking the foreign apps array + // Check if the app referenced is itself after checking the foreign apps array. if (index === 0 && referenceId !== 0 && referenceId !== appIndex) { throw new Error(`Box ref with appId ${referenceId} not in foreign-apps`); } From 90b21c1aca984e91e8c45ba3e48bd87183c85076 Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Thu, 9 Jun 2022 20:47:30 -0400 Subject: [PATCH 12/22] Delete box name field for length 0 array --- src/transaction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transaction.ts b/src/transaction.ts index 299ed9969..276852349 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -846,7 +846,7 @@ export class Transaction implements TransactionStorageStructure { if (!txn.apas || !txn.apas.length) delete txn.apas; for (const box of txn.apbx) { if (!box.i) delete box.i; - if (!box.n) delete box.n; + if (!box.n || !box.n.length) delete box.n; } if (!txn.apbx || !txn.apbx.length) delete txn.apbx; if (!txn.apat || !txn.apat.length) delete txn.apat; From 57836dcd9936ac3e17d5c236abb435abc35f11b0 Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Wed, 15 Jun 2022 15:30:10 -0400 Subject: [PATCH 13/22] Revise guard --- src/boxStorage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boxStorage.ts b/src/boxStorage.ts index 24211639b..9a992a0b5 100644 --- a/src/boxStorage.ts +++ b/src/boxStorage.ts @@ -33,7 +33,7 @@ export function translateBoxReferences( foreignApps: number[], appIndex: number ) { - if (!references) return []; + if (references == null || !Array.isArray(references)) return []; return references.map((bx) => translateBoxReference(bx, foreignApps, appIndex) ); From 94435183daf362325dd07f248f24b62bd02e57bf Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Wed, 22 Jun 2022 18:25:31 -0400 Subject: [PATCH 14/22] Address some PR feedback --- src/boxStorage.ts | 3 ++- src/makeTxn.ts | 2 +- src/transaction.ts | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/boxStorage.ts b/src/boxStorage.ts index 9a992a0b5..44a27c6fb 100644 --- a/src/boxStorage.ts +++ b/src/boxStorage.ts @@ -1,10 +1,11 @@ +import { EncodedBoxReference } from './types'; import { BoxReference } from './types/transactions/base'; function translateBoxReference( reference: BoxReference, foreignApps: number[], appIndex: number -) { +): EncodedBoxReference { const referenceId = reference.appIndex; const referenceName = reference.name; let index = 0; diff --git a/src/makeTxn.ts b/src/makeTxn.ts index db59e94b4..9b468cf1a 100644 --- a/src/makeTxn.ts +++ b/src/makeTxn.ts @@ -1296,8 +1296,8 @@ export function makeApplicationUpdateTxnFromObject( | 'accounts' | 'foreignApps' | 'foreignAssets' - | 'note' | 'boxes' + | 'note' | 'lease' | 'rekeyTo' > diff --git a/src/transaction.ts b/src/transaction.ts index 276852349..3d4947857 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -3,6 +3,7 @@ import * as address from './encoding/address'; import * as encoding from './encoding/encoding'; import * as nacl from './nacl/naclWrappers'; import * as utils from './utils/utils'; +import { translateBoxReferences } from './boxStorage'; import { OnApplicationComplete, TransactionParams, @@ -19,7 +20,6 @@ import AnyTransaction, { EncodedLogicSig, } from './types/transactions'; import { Address } from './types/address'; -import { translateBoxReferences } from './boxStorage'; const ALGORAND_TRANSACTION_LENGTH = 52; export const ALGORAND_MIN_TX_FEE = 1000; // version v5 @@ -1025,7 +1025,7 @@ export class Transaction implements TransactionStorageStructure { txn.boxes = txnForEnc.apbx.map((box) => ({ // Translate foreign app index to app ID appIndex: - box.i !== 0 || box.i !== txn.appIndex + box.i !== 0 && box.i !== txn.appIndex ? txn.appForeignApps[box.i - 1] : txn.appIndex, name: box.n, From b9a4e8c143ece9a38c670720fef3eedd8b208bb4 Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Wed, 22 Jun 2022 18:26:18 -0400 Subject: [PATCH 15/22] Fix box translation condition from index to id --- src/transaction.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/transaction.ts b/src/transaction.ts index 3d4947857..1d9ce87b7 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -1025,9 +1025,9 @@ export class Transaction implements TransactionStorageStructure { txn.boxes = txnForEnc.apbx.map((box) => ({ // Translate foreign app index to app ID appIndex: - box.i !== 0 && box.i !== txn.appIndex - ? txn.appForeignApps[box.i - 1] - : txn.appIndex, + box.i === 0 || box.i === txn.appIndex + ? txn.appIndex + : txn.appForeignApps[box.i - 1], name: box.n, })); } From 645f48f06cdbab87d038789dc545bf6ee8f21adf Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Wed, 22 Jun 2022 18:30:20 -0400 Subject: [PATCH 16/22] Alias own reference condition --- src/boxStorage.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/boxStorage.ts b/src/boxStorage.ts index 44a27c6fb..ead9edd57 100644 --- a/src/boxStorage.ts +++ b/src/boxStorage.ts @@ -8,18 +8,19 @@ function translateBoxReference( ): EncodedBoxReference { const referenceId = reference.appIndex; const referenceName = reference.name; + const isOwnReference = referenceId !== 0 && referenceId !== appIndex; let index = 0; // Foreign apps start from index 1; index 0 is its own app ID. try { index = foreignApps.indexOf(referenceId) + 1; } catch (err) { // Foreign app array cannot be empty unless the reference ID is itself. - if (referenceId !== 0 && referenceId !== appIndex) { + if (isOwnReference) { throw new Error(`Box ref with appId ${referenceId} not in foreign-apps`); } } // Check if the app referenced is itself after checking the foreign apps array. - if (index === 0 && referenceId !== 0 && referenceId !== appIndex) { + if (index === 0 && isOwnReference) { throw new Error(`Box ref with appId ${referenceId} not in foreign-apps`); } return { i: index, n: referenceName }; From 52a6549b4a58cdf44fb2dc33e189bdbb99109b24 Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Wed, 22 Jun 2022 18:42:43 -0400 Subject: [PATCH 17/22] Fix unit tests and add more comments --- src/boxStorage.ts | 5 ++++- tests/5.Transaction.js | 5 +---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/boxStorage.ts b/src/boxStorage.ts index ead9edd57..e7f274b8a 100644 --- a/src/boxStorage.ts +++ b/src/boxStorage.ts @@ -20,7 +20,10 @@ function translateBoxReference( } } // Check if the app referenced is itself after checking the foreign apps array. + // If index is zero, then the app ID was not found in the foreign apps array. if (index === 0 && isOwnReference) { + // Error if the app is trying to reference a foreign app that was not in + // its own foreign apps array. throw new Error(`Box ref with appId ${referenceId} not in foreign-apps`); } return { i: index, n: referenceName }; @@ -34,7 +37,7 @@ export function translateBoxReferences( references: BoxReference[], foreignApps: number[], appIndex: number -) { +): EncodedBoxReference[] { if (references == null || !Array.isArray(references)) return []; return references.map((bx) => translateBoxReference(bx, foreignApps, appIndex) diff --git a/tests/5.Transaction.js b/tests/5.Transaction.js index 4dc83d95d..cf8eeba07 100644 --- a/tests/5.Transaction.js +++ b/tests/5.Transaction.js @@ -1567,10 +1567,7 @@ describe('Sign', () => { testCase[1], testCase[2] ); - assert.deepStrictEqual(expected.length, actual.length); - for (let i = 0; i < expected.length; i++) { - assert.deepStrictEqual(expected[i], actual[i]); - } + assert.deepStrictEqual(expected, actual); } }); }); From 6b32fd3c9b14529e2b7f116b4886420f3718ae9c Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Thu, 23 Jun 2022 10:51:35 -0400 Subject: [PATCH 18/22] Add guard and change reference condition for better readability --- src/boxStorage.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/boxStorage.ts b/src/boxStorage.ts index e7f274b8a..8106b9b5f 100644 --- a/src/boxStorage.ts +++ b/src/boxStorage.ts @@ -8,20 +8,18 @@ function translateBoxReference( ): EncodedBoxReference { const referenceId = reference.appIndex; const referenceName = reference.name; - const isOwnReference = referenceId !== 0 && referenceId !== appIndex; + const isOwnReference = referenceId === 0 || referenceId === appIndex; let index = 0; - // Foreign apps start from index 1; index 0 is its own app ID. - try { - index = foreignApps.indexOf(referenceId) + 1; - } catch (err) { + + if (foreignApps == null && !isOwnReference) { // Foreign app array cannot be empty unless the reference ID is itself. - if (isOwnReference) { - throw new Error(`Box ref with appId ${referenceId} not in foreign-apps`); - } + throw new Error(`Box ref with appId ${referenceId} not in foreign-apps`); } + // Foreign apps start from index 1; index 0 is its own app ID. + index = foreignApps.indexOf(referenceId) + 1; // Check if the app referenced is itself after checking the foreign apps array. // If index is zero, then the app ID was not found in the foreign apps array. - if (index === 0 && isOwnReference) { + if (index === 0 && !isOwnReference) { // Error if the app is trying to reference a foreign app that was not in // its own foreign apps array. throw new Error(`Box ref with appId ${referenceId} not in foreign-apps`); From 4bae434e9ef3327763890ea7ca7d73ad75634686 Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Thu, 23 Jun 2022 11:26:30 -0400 Subject: [PATCH 19/22] Refactor box reference condition --- src/boxStorage.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/boxStorage.ts b/src/boxStorage.ts index 8106b9b5f..4ce8ba0a7 100644 --- a/src/boxStorage.ts +++ b/src/boxStorage.ts @@ -11,14 +11,13 @@ function translateBoxReference( const isOwnReference = referenceId === 0 || referenceId === appIndex; let index = 0; - if (foreignApps == null && !isOwnReference) { - // Foreign app array cannot be empty unless the reference ID is itself. - throw new Error(`Box ref with appId ${referenceId} not in foreign-apps`); + if (foreignApps != null) { + // Foreign apps start from index 1; index 0 is its own app ID. + index = foreignApps.indexOf(referenceId) + 1; } - // Foreign apps start from index 1; index 0 is its own app ID. - index = foreignApps.indexOf(referenceId) + 1; // Check if the app referenced is itself after checking the foreign apps array. - // If index is zero, then the app ID was not found in the foreign apps array. + // If index is zero, then the app ID was not found in the foreign apps array + // or the foreign apps array was null. if (index === 0 && !isOwnReference) { // Error if the app is trying to reference a foreign app that was not in // its own foreign apps array. From 027ddbca25caa39fb257c035e403bcbb3ed709e0 Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Thu, 23 Jun 2022 15:07:08 -0400 Subject: [PATCH 20/22] Export box reference type in index.ts and change SDK testing branch --- src/types/transactions/index.ts | 7 ++++++- tests/cucumber/docker/run_docker.sh | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/types/transactions/index.ts b/src/types/transactions/index.ts index 320225953..1ad181ffb 100644 --- a/src/types/transactions/index.ts +++ b/src/types/transactions/index.ts @@ -18,7 +18,12 @@ import { } from './application'; // Utilities -export { TransactionParams, TransactionType, SuggestedParams } from './base'; +export { + TransactionParams, + TransactionType, + SuggestedParams, + BoxReference, +} from './base'; export { MustHaveSuggestedParams, MustHaveSuggestedParamsInline, diff --git a/tests/cucumber/docker/run_docker.sh b/tests/cucumber/docker/run_docker.sh index 5db4bb447..84f7bf0cf 100755 --- a/tests/cucumber/docker/run_docker.sh +++ b/tests/cucumber/docker/run_docker.sh @@ -7,7 +7,7 @@ rm -rf test-harness rm -rf tests/cucumber/features # clone test harness -git clone --single-branch --branch box-reference https://github.com/algorand/algorand-sdk-testing.git test-harness +git clone --single-branch --branch feature/box-storage https://github.com/algorand/algorand-sdk-testing.git test-harness # move feature files and example files to destination mv test-harness/features tests/cucumber/features From 6d38a33756f6a5d2fbdfb09e0efae4c4f4a2d030 Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Thu, 23 Jun 2022 15:30:56 -0400 Subject: [PATCH 21/22] Revert back test branch to older version --- tests/cucumber/docker/run_docker.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cucumber/docker/run_docker.sh b/tests/cucumber/docker/run_docker.sh index 84f7bf0cf..5db4bb447 100755 --- a/tests/cucumber/docker/run_docker.sh +++ b/tests/cucumber/docker/run_docker.sh @@ -7,7 +7,7 @@ rm -rf test-harness rm -rf tests/cucumber/features # clone test harness -git clone --single-branch --branch feature/box-storage https://github.com/algorand/algorand-sdk-testing.git test-harness +git clone --single-branch --branch box-reference https://github.com/algorand/algorand-sdk-testing.git test-harness # move feature files and example files to destination mv test-harness/features tests/cucumber/features From d6366dd54a714a511808e24301fe00b46db37f0c Mon Sep 17 00:00:00 2001 From: algochoi <86622919+algochoi@users.noreply.github.com> Date: Wed, 29 Jun 2022 15:32:24 -0400 Subject: [PATCH 22/22] Remove redundant null check on array --- src/boxStorage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boxStorage.ts b/src/boxStorage.ts index 4ce8ba0a7..6d07014ac 100644 --- a/src/boxStorage.ts +++ b/src/boxStorage.ts @@ -35,7 +35,7 @@ export function translateBoxReferences( foreignApps: number[], appIndex: number ): EncodedBoxReference[] { - if (references == null || !Array.isArray(references)) return []; + if (!Array.isArray(references)) return []; return references.map((bx) => translateBoxReference(bx, foreignApps, appIndex) );