Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GetApplicationBoxByName API #586

Merged
merged 35 commits into from
Jul 7, 2022
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
f7ec88d
Initial work to define box types in appl calls
algochoi May 26, 2022
6a68e65
More changes to boxes field
algochoi May 26, 2022
3996d72
Change boxes to array types, fix transactions, and tests
algochoi May 27, 2022
67dcb16
Write box translation tests
algochoi May 31, 2022
720ba89
Add cucumber test steps
algochoi May 31, 2022
7ba6913
Revert pretty print
algochoi Jun 1, 2022
5aa0264
Fix decoding for txn boxes
algochoi Jun 1, 2022
a2addcb
Revise tests to make box args similar to app args
algochoi Jun 2, 2022
18fbd8b
Clean up some code and add a blank test case
algochoi Jun 9, 2022
ec24770
Merge branch 'develop' into box-reference
algochoi Jun 9, 2022
0c3c4e1
Fix err case
algochoi Jun 10, 2022
7b15d3a
Fix box ref catch
algochoi Jun 10, 2022
90b21c1
Delete box name field for length 0 array
algochoi Jun 10, 2022
0f41758
Add box by name endpoint on algod
algochoi Jun 13, 2022
406b073
Add encoding tests for uri
algochoi Jun 13, 2022
57827b9
Move scope for cucumber test helper functions
algochoi Jun 13, 2022
57836dc
Revise guard
algochoi Jun 15, 2022
d88dff6
Merge branch 'develop' into box-reference
algochoi Jun 21, 2022
65551db
Merge branch 'feature/box-storage' into box-reference
algochoi Jun 22, 2022
1266b49
Merge branch 'feature/box-storage' into box-api
algochoi Jun 22, 2022
9443518
Address some PR feedback
algochoi Jun 22, 2022
b9a4e8c
Fix box translation condition from index to id
algochoi Jun 22, 2022
645f48f
Alias own reference condition
algochoi Jun 22, 2022
52a6549
Fix unit tests and add more comments
algochoi Jun 22, 2022
6b32fd3
Add guard and change reference condition for better readability
algochoi Jun 23, 2022
4bae434
Refactor box reference condition
algochoi Jun 23, 2022
c32e663
Merge branch 'box-reference' into box-api
algochoi Jun 23, 2022
60b3207
Refactor URI encoding
algochoi Jun 23, 2022
ad03eed
Add box integration test
algochoi Jun 23, 2022
a36f924
Change box name encoding scheme as query param in API
algochoi Jun 30, 2022
ef2d6d4
Merge branch 'feature/box-storage' into box-api
algochoi Jul 5, 2022
735d80f
Fix tests by changing how names are ingested
algochoi Jul 5, 2022
67c3608
Delete unused encoding URI functions
algochoi Jul 6, 2022
8f509f6
Change app funding step from then to given
algochoi Jul 6, 2022
cad581b
Revise link in Box API comments and add type hint to json request
algochoi Jul 7, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions src/boxStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { BoxReference } from './types/transactions/base';

function translateBoxReference(
reference: BoxReference,
foreignApps: number[],
appIndex: number
) {
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 {
index = foreignApps.indexOf(referenceId) + 1;
} catch (err) {
// 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.
if (index === 0 && referenceId !== 0 && referenceId !== appIndex) {
throw new Error(`Box ref with appId ${referenceId} not in foreign-apps`);
}
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
) {
if (!references) return [];
return references.map((bx) =>
translateBoxReference(bx, foreignApps, appIndex)
);
}
26 changes: 26 additions & 0 deletions src/client/v2/algod/algod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Compile from './compile';
import Dryrun from './dryrun';
import GetAssetByID from './getAssetByID';
import GetApplicationByID from './getApplicationByID';
import GetApplicationBoxByName from './getApplicationBoxByName';
import HealthCheck from './healthCheck';
import PendingTransactionInformation from './pendingTransactionInformation';
import PendingTransactions from './pendingTransactions';
Expand Down Expand Up @@ -429,6 +430,31 @@ export default class AlgodClient extends ServiceClient {
return new GetApplicationByID(this.c, this.intDecoding, index);
}

/**
* Given an application ID and the box name (key), return the value stored in the box.
*
* #### Example
* ```typescript
* const index = 60553466;
* const boxName = Buffer.from("foo");
* const app = await algodClient.getApplicationBoxByName(index, boxName).do();
* ```
*
* TODO: Change below
* [Response data schema details](Fill me in!)
* @param index - The application ID to look up.
* @param boxName - The box name or key to look up.
* @category GET
*/
getApplicationBoxByName(index: number, boxName: Uint8Array) {
return new GetApplicationBoxByName(
this.c,
this.intDecoding,
index,
boxName
);
}

/**
* Returns the entire genesis file.
*
Expand Down
22 changes: 22 additions & 0 deletions src/client/v2/algod/getApplicationBoxByName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import JSONRequest from '../jsonrequest';
import HTTPClient from '../../client';
import IntDecoding from '../../../types/intDecoding';
import { encodeURLFromBytes } from '../../../encoding/uri';

export default class GetApplicationBoxByName extends JSONRequest {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: consider typing the response with JSONRequest<types.Box>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

constructor(
c: HTTPClient,
intDecoding: IntDecoding,
private index: number,
private boxName: Uint8Array
) {
super(c, intDecoding);
this.index = index;
this.boxName = boxName;
}

path() {
const encodedBoxName = encodeURLFromBytes(this.boxName);
return `/v2/applications/${this.index}/boxes/${encodedBoxName}`;
}
}
37 changes: 37 additions & 0 deletions src/client/v2/algod/models/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,43 @@ export class BlockResponse extends BaseModel {
}
}

/**
* Box name and its content.
*/
export class Box extends BaseModel {
/**
* (name) box name, base64 encoded
*/
public name: Uint8Array;

/**
* (value) box value, base64 encoded.
*/
public value: Uint8Array;

/**
* Creates a new `Box` object.
* @param name - (name) box name, base64 encoded
* @param value - (value) box value, base64 encoded.
*/
constructor(name: string | Uint8Array, value: string | Uint8Array) {
super();
this.name =
typeof name === 'string'
? new Uint8Array(Buffer.from(name, 'base64'))
: name;
this.value =
typeof value === 'string'
? new Uint8Array(Buffer.from(value, 'base64'))
: value;

this.attribute_map = {
name: 'name',
value: 'value',
};
}
}

export class BuildVersion extends BaseModel {
public branch: string;

Expand Down
7 changes: 7 additions & 0 deletions src/composer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
isTransactionWithSigner,
} from './signer';
import {
BoxReference,
OnApplicationComplete,
SuggestedParams,
} from './types/transactions/base';
Expand Down Expand Up @@ -201,6 +202,7 @@ export class AtomicTransactionComposer {
numLocalInts,
numLocalByteSlices,
extraPages,
boxes,
note,
lease,
rekeyTo,
Expand Down Expand Up @@ -232,6 +234,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 */
Expand Down Expand Up @@ -317,6 +321,8 @@ export class AtomicTransactionComposer {
const refArgTypes: ABIReferenceType[] = [];
const refArgValues: ABIValue[] = [];
const refArgIndexToBasicArgIndex: Map<number, number> = 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;
Expand Down Expand Up @@ -437,6 +443,7 @@ export class AtomicTransactionComposer {
accounts: foreignAccounts,
foreignApps,
foreignAssets,
boxes: boxReferences,
onComplete:
onComplete == null ? OnApplicationComplete.NoOpOC : onComplete,
approvalProgram,
Expand Down
29 changes: 29 additions & 0 deletions src/encoding/uri.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* This file contains URI encoding schemes for Uint8Arrays and Buffers.
*
* If there are non UTF-8 characters in the box name, the name will be converted
* to its corresponding hex value and escaped in the URL.
* */

const isUrlSafe = (char) => {
const urlSafePattern = /[a-zA-Z0-9\-_~.]+/;
return urlSafePattern.test(char);
};

export function encodeURLFromBytes(buf: Uint8Array) {
if (!(buf instanceof Uint8Array)) {
throw TypeError(`Argument ${buf} must be in bytes`);
}
let encoded = '';
for (let i = 0; i < buf.length; i++) {
const charBuf = Buffer.from('00', 'hex');
charBuf.writeUInt8(buf[i]);
const char = charBuf.toString();
if (isUrlSafe(char)) {
encoded += char;
} else {
encoded += `%${charBuf.toString('hex').toUpperCase()}`;
}
}
return encoded;
}
Loading