Skip to content

Commit

Permalink
fix: Several bugs in the box proxy api
Browse files Browse the repository at this point in the history
  • Loading branch information
tristanmenzel committed Dec 10, 2024
1 parent d7a08c9 commit 85a25b3
Show file tree
Hide file tree
Showing 13 changed files with 2,605 additions and 1,017 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"dev:examples": "tsx src/cli.ts build examples --output-awst --output-awst-json",
"dev:approvals": "rimraf tests/approvals/out && tsx src/cli.ts build tests/approvals --dry-run",
"dev:expected-output": "tsx src/cli.ts build tests/expected-output --dry-run",
"dev:testing": "tsx src/cli.ts build tests/approvals/casting.algo.ts --output-awst --output-awst-json --output-ssa-ir --log-level=info --out-dir out/[name] --optimization-level=0",
"dev:testing": "tsx src/cli.ts build tests/approvals/box-proxies.algo.ts --output-awst --output-awst-json --output-ssa-ir --log-level=info --out-dir out/[name] --optimization-level=0",
"audit": "better-npm-audit audit",
"format": "prettier --write .",
"lint": "eslint \"src/**/*.ts\"",
Expand Down
32 changes: 31 additions & 1 deletion src/awst_build/eb/storage/box/base.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { nodeFactory } from '../../../../awst/node-factory'
import { type BoxValueExpression, BytesConstant, type Expression } from '../../../../awst/nodes'
import type { SourceLocation } from '../../../../awst/source-location'
import { wtypes } from '../../../../awst/wtypes'
import { codeInvariant } from '../../../../util'
import { AppStorageDeclaration } from '../../../models/app-storage-declaration'
import type { BoxPType, BoxRefPType } from '../../../ptypes'
import { BoxMapPType, type ContractClassPType, type PType } from '../../../ptypes'
import { boolPType, BoxMapPType, type ContractClassPType, type PType, uint64PType } from '../../../ptypes'
import { instanceEb } from '../../../type-registry'
import { InstanceExpressionBuilder } from '../../index'
import { ValueProxy } from '../value-proxy'

Expand Down Expand Up @@ -61,3 +63,31 @@ export function boxValue({
existsAssertionMessage: 'Box must have value',
})
}

export function boxLength(box: BoxValueExpression, sourceLocation: SourceLocation) {
return instanceEb(
nodeFactory.checkedMaybe({
expr: nodeFactory.intrinsicCall({
opCode: 'box_len',
stackArgs: [box.key],
wtype: new wtypes.WTuple({ types: [wtypes.uint64WType, wtypes.boolWType], immutable: true }),
immediates: [],
sourceLocation,
}),
comment: box.existsAssertionMessage ?? 'Box exists',
}),
uint64PType,
)
}

export function boxExists(box: BoxValueExpression, sourceLocation: SourceLocation) {
return instanceEb(
nodeFactory.stateExists({
field: box,
sourceLocation,
wtype: wtypes.boolWType,
}),

boolPType,
)
}
27 changes: 4 additions & 23 deletions src/awst_build/eb/storage/box/box-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@ import { wtypes } from '../../../../awst/wtypes'

import { invariant } from '../../../../util'
import type { PType } from '../../../ptypes'
import { boolPType, BoxMapPType, bytesPType, stringPType, TuplePType, uint64PType } from '../../../ptypes'
import { boolPType, BoxMapPType, bytesPType, stringPType, TuplePType } from '../../../ptypes'
import { instanceEb } from '../../../type-registry'
import { BooleanExpressionBuilder } from '../../boolean-expression-builder'
import { FunctionBuilder, type NodeBuilder } from '../../index'
import { parseFunctionArgs } from '../../util/arg-parsing'
import { VoidExpressionBuilder } from '../../void-expression-builder'
import { extractKey } from '../util'
import { BoxProxyExpressionBuilder, BoxValueExpressionBuilder } from './base'
import { boxExists, boxLength, BoxProxyExpressionBuilder, BoxValueExpressionBuilder } from './base'

export class BoxMapFunctionBuilder extends FunctionBuilder {
call(args: ReadonlyArray<NodeBuilder>, typeArgs: ReadonlyArray<PType>, sourceLocation: SourceLocation): NodeBuilder {
Expand Down Expand Up @@ -99,13 +98,7 @@ class BoxMapHasFunctionBuilder extends BoxMapFunctionBuilderBase {
genericTypeArgs: 0,
argSpec: (a) => [a.required(this.keyType)],
})
return new BooleanExpressionBuilder(
nodeFactory.stateExists({
wtype: wtypes.boolWType,
field: this.boxValueExpression(key.resolve()),
sourceLocation,
}),
)
return boxExists(this.boxValueExpression(key.resolve()), sourceLocation)
}
}

Expand Down Expand Up @@ -197,19 +190,7 @@ class BoxMapLengthFunctionBuilder extends BoxMapFunctionBuilderBase {
argSpec: (a) => [a.required(this.keyType)],
})

return instanceEb(
nodeFactory.checkedMaybe({
expr: nodeFactory.intrinsicCall({
opCode: 'box_len',
stackArgs: [this.boxValueExpression(key.resolve())],
wtype: new wtypes.WTuple({ types: [wtypes.uint64WType, wtypes.boolWType], immutable: true }),
immediates: [],
sourceLocation,
}),
comment: 'Box must exist',
}),
uint64PType,
)
return boxLength(this.boxValueExpression(key.resolve()), sourceLocation)
}
}
class BoxMapDeleteFunctionBuilder extends BoxMapFunctionBuilderBase {
Expand Down
41 changes: 9 additions & 32 deletions src/awst_build/eb/storage/box/box-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { instanceEb } from '../../../type-registry'
import { FunctionBuilder, type NodeBuilder } from '../../index'
import { parseFunctionArgs } from '../../util/arg-parsing'
import { extractKey } from '../util'
import { BoxProxyExpressionBuilder, boxValue, BoxValueExpressionBuilder } from './base'
import { boxExists, boxLength, BoxProxyExpressionBuilder, boxValue, BoxValueExpressionBuilder } from './base'

export class BoxRefFunctionBuilder extends FunctionBuilder {
call(args: ReadonlyArray<NodeBuilder>, typeArgs: ReadonlyArray<PType>, sourceLocation: SourceLocation): NodeBuilder {
Expand Down Expand Up @@ -55,32 +55,9 @@ export class BoxRefExpressionBuilder extends BoxProxyExpressionBuilder<BoxRefPTy
case 'replace':
return new BoxRefReplaceFunctionBuilder(boxValueExpr)
case 'exists':
return boxExists(boxValueExpr, sourceLocation)
case 'length': {
const boxLength = nodeFactory.intrinsicCall({
opCode: 'box_len',
stackArgs: [boxValueExpr],
wtype: new wtypes.WTuple({ types: [wtypes.uint64WType, wtypes.boolWType], immutable: true }),
immediates: [],
sourceLocation,
})
if (name === 'exists') {
return instanceEb(
nodeFactory.tupleItemExpression({
base: boxLength,
sourceLocation,
index: 1n,
}),
boolPType,
)
} else {
return instanceEb(
nodeFactory.checkedMaybe({
expr: boxLength,
comment: 'Box must exist',
}),
uint64PType,
)
}
return boxLength(boxValueExpr, sourceLocation)
}
case 'value':
return new BoxValueExpressionBuilder(boxValueExpr, this.ptype.contentType)
Expand Down Expand Up @@ -110,7 +87,7 @@ export class BoxRefCreateFunctionBuilder extends BoxRefBaseFunctionBuilder {
return instanceEb(
nodeFactory.intrinsicCall({
opCode: 'box_create',
stackArgs: [this.boxValue, size.resolve()],
stackArgs: [this.boxValue.key, size.resolve()],
wtype: wtypes.boolWType,
immediates: [],
sourceLocation,
Expand All @@ -134,7 +111,7 @@ export class BoxRefResizeFunctionBuilder extends BoxRefBaseFunctionBuilder {
return instanceEb(
nodeFactory.intrinsicCall({
opCode: 'box_resize',
stackArgs: [this.boxValue, size.resolve()],
stackArgs: [this.boxValue.key, size.resolve()],
wtype: wtypes.voidWType,
immediates: [],
sourceLocation,
Expand All @@ -158,7 +135,7 @@ export class BoxRefExtractFunctionBuilder extends BoxRefBaseFunctionBuilder {
return instanceEb(
nodeFactory.intrinsicCall({
opCode: 'box_extract',
stackArgs: [this.boxValue, start.resolve(), length.resolve()],
stackArgs: [this.boxValue.key, start.resolve(), length.resolve()],
wtype: wtypes.bytesWType,
immediates: [],
sourceLocation,
Expand All @@ -182,7 +159,7 @@ export class BoxRefReplaceFunctionBuilder extends BoxRefBaseFunctionBuilder {
return instanceEb(
nodeFactory.intrinsicCall({
opCode: 'box_replace',
stackArgs: [this.boxValue, start.resolve(), value.resolve()],
stackArgs: [this.boxValue.key, start.resolve(), value.resolve()],
wtype: wtypes.voidWType,
immediates: [],
sourceLocation,
Expand All @@ -207,7 +184,7 @@ export class BoxRefPutFunctionBuilder extends BoxRefBaseFunctionBuilder {
return instanceEb(
nodeFactory.intrinsicCall({
opCode: 'box_put',
stackArgs: [this.boxValue, value.resolve()],
stackArgs: [this.boxValue.key, value.resolve()],
wtype: wtypes.voidWType,
immediates: [],
sourceLocation,
Expand All @@ -231,7 +208,7 @@ export class BoxRefSpliceFunctionBuilder extends BoxRefBaseFunctionBuilder {
return instanceEb(
nodeFactory.intrinsicCall({
opCode: 'box_splice',
stackArgs: [this.boxValue, start.resolve(), stop.resolve(), value.resolve()],
stackArgs: [this.boxValue.key, start.resolve(), stop.resolve(), value.resolve()],
wtype: wtypes.voidWType,
immediates: [],
sourceLocation,
Expand Down
27 changes: 4 additions & 23 deletions src/awst_build/eb/storage/box/box.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ import type { SourceLocation } from '../../../../awst/source-location'
import { wtypes } from '../../../../awst/wtypes'
import { invariant } from '../../../../util'
import type { PType } from '../../../ptypes'
import { boolPType, BoxPType, bytesPType, stringPType, TuplePType, uint64PType } from '../../../ptypes'
import { boolPType, BoxPType, bytesPType, stringPType, TuplePType } from '../../../ptypes'
import { instanceEb } from '../../../type-registry'
import { BooleanExpressionBuilder } from '../../boolean-expression-builder'
import { FunctionBuilder, type NodeBuilder, ParameterlessFunctionBuilder } from '../../index'
import { parseFunctionArgs } from '../../util/arg-parsing'
import { VoidExpressionBuilder } from '../../void-expression-builder'
import { extractKey } from '../util'
import { BoxProxyExpressionBuilder, boxValue, BoxValueExpressionBuilder } from './base'
import { boxExists, boxLength, BoxProxyExpressionBuilder, boxValue, BoxValueExpressionBuilder } from './base'

export class BoxFunctionBuilder extends FunctionBuilder {
call(args: ReadonlyArray<NodeBuilder>, typeArgs: ReadonlyArray<PType>, sourceLocation: SourceLocation): NodeBuilder {
Expand Down Expand Up @@ -48,27 +47,9 @@ export class BoxExpressionBuilder extends BoxProxyExpressionBuilder<BoxPType> {
case 'value':
return new BoxValueExpressionBuilder(boxValueExpr, this.ptype.contentType)
case 'exists':
return new BooleanExpressionBuilder(
nodeFactory.stateExists({
field: boxValueExpr,
sourceLocation,
wtype: wtypes.boolWType,
}),
)
return boxExists(boxValueExpr, sourceLocation)
case 'length':
return instanceEb(
nodeFactory.checkedMaybe({
expr: nodeFactory.intrinsicCall({
opCode: 'box_len',
stackArgs: [boxValueExpr],
wtype: new wtypes.WTuple({ types: [wtypes.uint64WType, wtypes.boolWType], immutable: true }),
immediates: [],
sourceLocation,
}),
comment: 'Box must exist',
}),
uint64PType,
)
return boxLength(boxValueExpr, sourceLocation)
case 'delete':
return new BoxDeleteFunctionBuilder(boxValueExpr, sourceLocation)
case 'get':
Expand Down
31 changes: 27 additions & 4 deletions tests/approvals/box-proxies.algo.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { bytes, uint64 } from '@algorandfoundation/algorand-typescript'
import { assert, Box, BoxMap, BoxRef, Bytes } from '@algorandfoundation/algorand-typescript'
import { assert, BaseContract, Box, BoxMap, BoxRef, Bytes, Txn } from '@algorandfoundation/algorand-typescript'
import { itob } from '@algorandfoundation/algorand-typescript/op'

const boxA = Box<string>({ key: Bytes('A') })
function testBox(box: Box<string>, value: string) {
Expand Down Expand Up @@ -51,10 +52,32 @@ function testBoxRef(box: BoxRef, length: uint64) {
} else if (boxRef.length !== length) {
boxRef.resize(length)
}
if (box.exists) {
box.resize(4)
} else {
box.create({ size: 4 })
}
const someBytes = Bytes.fromHex('FFFFFFFF')
box.put(someBytes)
boxRef.put(someBytes)
box.splice(1, 2, Bytes.fromHex('00'))
boxRef.splice(1, 2, Bytes.fromHex('00'))

assert(box.value === Bytes.fromHex('FFFFFFFF'))
box.splice(1, 1, Bytes.fromHex('00'))
assert(box.value === Bytes.fromHex('FF00FFFF'))
}

export class BoxContract extends BaseContract {
boxOne = Box<string>({ key: 'one' })
boxMapTwo = BoxMap<string, bytes>({ keyPrefix: 'two' })
boxRefThree = BoxRef({ key: 'three' })

approvalProgram(): boolean {
if (Txn.applicationId.id !== 0) {
testBox(this.boxOne, 'aaaaaargh')

testBoxMap(this.boxMapTwo, 'what?', itob(256456))

testBoxRef(this.boxRefThree, 99)
}
return true
}
}
Loading

0 comments on commit 85a25b3

Please sign in to comment.