Skip to content

Commit

Permalink
Merge pull request #28 from algorandfoundation/feat-v11
Browse files Browse the repository at this point in the history
feat: add stubs for v11 op code and functions
  • Loading branch information
boblat authored Jan 14, 2025
2 parents 0853b82 + 3f4d4ce commit bfc897d
Show file tree
Hide file tree
Showing 19 changed files with 899 additions and 460 deletions.
9 changes: 9 additions & 0 deletions src/context-helpers/internal-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Account, BaseContract, internal } from '@algorandfoundation/algorand-ty
import { AccountData } from '../impl/account'
import { ApplicationData } from '../impl/application'
import { AssetData } from '../impl/asset'
import { VoterData } from '../impl/voter-params'
import { TransactionGroup } from '../subcontexts/transaction-context'
import { TestExecutionContext } from '../test-execution-context'

Expand Down Expand Up @@ -69,6 +70,14 @@ class InternalContext {
}
return data
}

getVoterData(account: Account): VoterData {
const data = this.ledger.voterDataMap.get(account)
if (!data) {
throw internal.errors.internalError('Unknown voter, check correct testing context is active')
}
return data
}
}

export const lazyContext = new InternalContext()
3 changes: 3 additions & 0 deletions src/impl/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ export class AssetHolding {
export class AccountData {
optedAssets = new Uint64Map<AssetHolding>()
optedApplications = new Uint64Map<Application>()
incentiveEligible = false
lastProposed?: uint64
lastHeartbeat?: uint64
account: Mutable<Omit<Account, 'bytes' | 'isOptedIn'>>

constructor() {
Expand Down
20 changes: 13 additions & 7 deletions src/impl/acct-params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Account, Application, gtxn, internal, uint64 } from '@algorandfoundatio
import { lazyContext } from '../context-helpers/internal-context'
import { asMaybeUint64Cls } from '../util'
import { getApp } from './app-params'
import { Global } from './global'

export const getAccount = (acct: Account | internal.primitives.StubUint64Compat): Account => {
const acctId = asMaybeUint64Cls(acct)
Expand Down Expand Up @@ -84,14 +85,19 @@ export const AcctParams: internal.opTypes.AcctParamsType = {
const acct = getAccount(a)
return [acct.totalBoxBytes, acct.balance !== 0]
},
// TODO: implement v11 methods
acctIncentiveEligible: function (_a: Account | uint64): readonly [boolean, boolean] {
throw new Error('Function not implemented.')
acctIncentiveEligible: function (a: Account | internal.primitives.StubUint64Compat): readonly [boolean, boolean] {
const acct = getAccount(a)
const accountData = lazyContext.ledger.accountDataMap.get(acct)
return [accountData?.incentiveEligible ?? false, acct.balance !== 0]
},
acctLastProposed: function (_a: Account | uint64): readonly [uint64, boolean] {
throw new Error('Function not implemented.')
acctLastProposed: function (a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
const acct = getAccount(a)
const accountData = lazyContext.ledger.accountDataMap.get(acct)
return [accountData?.lastProposed ?? Global.round, acct.balance !== 0]
},
acctLastHeartbeat: function (_a: Account | uint64): readonly [uint64, boolean] {
throw new Error('Function not implemented.')
acctLastHeartbeat: function (a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
const acct = getAccount(a)
const accountData = lazyContext.ledger.accountDataMap.get(acct)
return [accountData?.lastHeartbeat ?? Global.round, acct.balance !== 0]
},
}
68 changes: 46 additions & 22 deletions src/impl/block.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,62 @@
import { Account, bytes, internal, uint64 } from '@algorandfoundation/algorand-typescript'
import { Account, bytes, internal, Uint64, uint64 } from '@algorandfoundation/algorand-typescript'
import { lazyContext } from '../context-helpers/internal-context'
import { asUint64 } from '../util'
import { itob } from './pure'
import { asUint64, getRandomBytes } from '../util'

export class BlockData {
seed: bytes
timestamp: uint64
proposer: Account
feesCollected: uint64
bonus: uint64
branch: bytes
feeSink: Account
protocol: bytes
txnCounter: uint64
proposerPayout: uint64

constructor() {
this.seed = getRandomBytes(32).asAlgoTs()
this.timestamp = asUint64(Date.now())
this.proposer = Account()
this.feesCollected = Uint64(0)
this.bonus = Uint64(0)
this.branch = getRandomBytes(32).asAlgoTs()
this.feeSink = Account()
this.protocol = getRandomBytes(32).asAlgoTs()
this.txnCounter = Uint64(0)
this.proposerPayout = Uint64(0)
}
}

export const Block: internal.opTypes.BlockType = {
blkSeed: function (a: internal.primitives.StubUint64Compat): bytes {
return itob(lazyContext.ledger.getBlockContent(a).seed)
return lazyContext.ledger.getBlockData(a).seed
},
blkTimestamp: function (a: internal.primitives.StubUint64Compat): uint64 {
return asUint64(lazyContext.ledger.getBlockContent(a).timestamp)
return lazyContext.ledger.getBlockData(a).timestamp
},
// TODO: implement v11 methods
blkProposer: function (_a: uint64): Account {
throw new Error('Function not implemented.')
blkProposer: function (a: uint64): Account {
return lazyContext.ledger.getBlockData(a).proposer
},
blkFeesCollected: function (_a: uint64): uint64 {
throw new Error('Function not implemented.')
blkFeesCollected: function (a: uint64): uint64 {
return lazyContext.ledger.getBlockData(a).feesCollected
},
blkBonus: function (_a: uint64): uint64 {
throw new Error('Function not implemented.')
blkBonus: function (a: uint64): uint64 {
return lazyContext.ledger.getBlockData(a).bonus
},
blkBranch: function (_a: uint64): bytes {
throw new Error('Function not implemented.')
blkBranch: function (a: uint64): bytes {
return lazyContext.ledger.getBlockData(a).branch
},
blkFeeSink: function (_a: uint64): Account {
throw new Error('Function not implemented.')
blkFeeSink: function (a: uint64): Account {
return lazyContext.ledger.getBlockData(a).feeSink
},
blkProtocol: function (_a: uint64): bytes {
throw new Error('Function not implemented.')
blkProtocol: function (a: uint64): bytes {
return lazyContext.ledger.getBlockData(a).protocol
},
blkTxnCounter: function (_a: uint64): uint64 {
throw new Error('Function not implemented.')
blkTxnCounter: function (a: uint64): uint64 {
return lazyContext.ledger.getBlockData(a).txnCounter
},
blkProposerPayout: function (_a: uint64): uint64 {
throw new Error('Function not implemented.')
blkProposerPayout: function (a: uint64): uint64 {
return lazyContext.ledger.getBlockData(a).proposerPayout
},
}
54 changes: 48 additions & 6 deletions src/impl/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ export class GlobalData {
assetOptInMinBalance: uint64
genesisHash: bytes
opcodeBudget?: uint64
payoutsEnabled: boolean
payoutsGoOnlineFee: uint64
payoutsPercent: uint64
payoutsMinBalance: uint64

constructor() {
this.minTxnFee = Uint64(MIN_TXN_FEE)
Expand All @@ -35,6 +39,10 @@ export class GlobalData {
this.assetCreateMinBalance = Uint64(DEFAULT_ASSET_CREATE_MIN_BALANCE)
this.assetOptInMinBalance = Uint64(DEFAULT_ASSET_OPT_IN_MIN_BALANCE)
this.genesisHash = DEFAULT_GLOBAL_GENESIS_HASH
this.payoutsEnabled = false
this.payoutsGoOnlineFee = Uint64(0)
this.payoutsPercent = Uint64(0)
this.payoutsMinBalance = Uint64(0)
}
}
const getGlobalData = (): GlobalData => {
Expand Down Expand Up @@ -184,10 +192,44 @@ export const Global: internal.opTypes.GlobalType = {
get genesisHash(): bytes {
return getGlobalData().genesisHash
},
payoutsEnabled: false,
// TODO: implement v11 fields
payoutsGoOnlineFee: 0,
payoutsPercent: 0,
payoutsMinBalance: 0,
payoutsMaxBalance: 0,

/**
* Whether block proposal payouts are enabled.
* Min AVM version: 11
*/
get payoutsEnabled(): boolean {
return getGlobalData().payoutsEnabled
},

/**
* The fee required in a keyreg transaction to make an account incentive eligible.
* Min AVM version: 11
*/
get payoutsGoOnlineFee(): uint64 {
return getGlobalData().payoutsGoOnlineFee
},

/**
* The percentage of transaction fees in a block that can be paid to the block proposer.
* Min AVM version: 11
*/
get payoutsPercent(): uint64 {
return getGlobalData().payoutsPercent
},

/**
* The minimum algo balance an account must have in the agreement round to receive block payouts in the proposal round.
* Min AVM version: 11
*/
get payoutsMinBalance(): uint64 {
return getGlobalData().payoutsMinBalance
},

/**
* The maximum algo balance an account can have in the agreement round to receive block payouts in the proposal round.
* Min AVM version: 11
*/
get payoutsMaxBalance(): uint64 {
return getGlobalData().payoutsMinBalance
},
}
2 changes: 2 additions & 0 deletions src/impl/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export { Global } from './global'
export { GTxn } from './gtxn'
export { GITxn, ITxn, ITxnCreate } from './itxn'
export { arg } from './logicSigArg'
export { onlineStake } from './online-stake'
export * from './pure'
export { gloadBytes, gloadUint64, Scratch } from './scratch'
export { gaid, Txn } from './txn'
export { VoterParams } from './voter-params'
6 changes: 6 additions & 0 deletions src/impl/online-stake.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { internal } from '@algorandfoundation/algorand-typescript'
import { lazyContext } from '../context-helpers/internal-context'

export const onlineStake: internal.opTypes.OnlineStakeType = () => {
return lazyContext.ledger.onlineStake
}
6 changes: 6 additions & 0 deletions src/impl/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ export class KeyRegistrationTransaction extends TransactionBase implements gtxn.
this.voteKeyDilution = fields.voteKeyDilution ?? Uint64(0)
this.nonparticipation = fields.nonparticipation ?? false
this.stateProofKey = fields.stateProofKey ?? Bytes()
const globalData = lazyContext.ledger.globalData
if (this.fee >= globalData.payoutsGoOnlineFee && globalData.payoutsEnabled) {
lazyContext.ledger.patchAccountData(this.sender, {
incentiveEligible: true,
})
}
}

readonly voteKey: bytes
Expand Down
29 changes: 29 additions & 0 deletions src/impl/voter-params.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Account, internal, uint64 } from '@algorandfoundation/algorand-typescript'
import { lazyContext } from '../context-helpers/internal-context'
import { getAccount } from './acct-params'

export class VoterData {
balance: uint64
incentiveEligible: boolean

constructor() {
this.balance = 0
this.incentiveEligible = false
}
}

const getVoterData = (a: Account | internal.primitives.StubUint64Compat): VoterData => {
const acct = getAccount(a)
return lazyContext.getVoterData(acct)
}

export const VoterParams: internal.opTypes.VoterParamsType = {
voterBalance: function (a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
const data = getVoterData(a)
return [data.balance, data.balance !== 0]
},
voterIncentiveEligible: function (a: Account | internal.primitives.StubUint64Compat): readonly [boolean, boolean] {
const data = getVoterData(a)
return [data.incentiveEligible, data.balance !== 0]
},
}
46 changes: 31 additions & 15 deletions src/subcontexts/ledger-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,23 @@ import { MAX_UINT64 } from '../constants'
import { AccountData, AssetHolding } from '../impl/account'
import { ApplicationData } from '../impl/application'
import { AssetData } from '../impl/asset'
import { BlockData } from '../impl/block'
import { GlobalData } from '../impl/global'
import { GlobalStateCls } from '../impl/state'
import { VoterData } from '../impl/voter-params'
import { asBigInt, asMaybeBytesCls, asMaybeUint64Cls, asUint64, asUint64Cls, iterBigInt } from '../util'

interface BlockData {
seed: bigint
timestamp: bigint
}

export class LedgerContext {
appIdIter = iterBigInt(1001n, MAX_UINT64)
assetIdIter = iterBigInt(1001n, MAX_UINT64)
applicationDataMap = new Uint64Map<ApplicationData>()
appIdContractMap = new Uint64Map<BaseContract>()
accountDataMap = new AccountMap<AccountData>()
assetDataMap = new Uint64Map<AssetData>()
voterDataMap = new AccountMap<VoterData>()
blocks = new Uint64Map<BlockData>()
globalData = new GlobalData()
onlineStake = 0

/* @internal */
addAppIdContractMap(appId: internal.primitives.StubUint64Compat, contract: BaseContract): void {
Expand Down Expand Up @@ -112,19 +111,36 @@ export class LedgerContext {
}
}

setBlock(
index: internal.primitives.StubUint64Compat,
seed: internal.primitives.StubUint64Compat,
timestamp: internal.primitives.StubUint64Compat,
): void {
const i = asBigInt(index)
const s = asBigInt(seed)
const t = asBigInt(timestamp)
patchAccountData(account: Account, data: Partial<AccountData>) {
const accountData = this.accountDataMap.get(account) ?? new AccountData()
this.accountDataMap.set(account, {
...accountData,
...data,
account: {
...accountData?.account,
...data.account,
},
})
}

this.blocks.set(i, { seed: s, timestamp: t })
patchVoterData(account: Account, data: Partial<VoterData>) {
const voterData = this.voterDataMap.get(account) ?? new VoterData()
this.voterDataMap.set(account, {
...voterData,
...data,
})
}

patchBlockData(index: internal.primitives.StubUint64Compat, data: Partial<BlockData>): void {
const i = asUint64(index)
const blockData = this.blocks.get(i) ?? new BlockData()
this.blocks.set(i, {
...blockData,
...data,
})
}

getBlockContent(index: internal.primitives.StubUint64Compat): BlockData {
getBlockData(index: internal.primitives.StubUint64Compat): BlockData {
const i = asBigInt(index)
if (this.blocks.has(i)) {
return this.blocks.get(i)!
Expand Down
4 changes: 3 additions & 1 deletion src/value-generators/avm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { asBigInt, asUint64Cls, getRandomBigInt, getRandomBytes } from '../util'

type AccountContextData = Partial<AccountData['account']> & {
address?: Account
incentiveEligible?: boolean
optedAssetBalances?: Map<internal.primitives.StubUint64Compat, internal.primitives.StubUint64Compat>
optedApplications?: Application[]
}
Expand Down Expand Up @@ -56,7 +57,8 @@ export class AvmValueGenerator {
}

const data = new AccountData()
const { address, optedAssetBalances, optedApplications, ...accountData } = input ?? {}
const { address, optedAssetBalances, optedApplications, incentiveEligible, ...accountData } = input ?? {}
data.incentiveEligible = incentiveEligible ?? false
data.account = {
...data.account,
...accountData,
Expand Down
Loading

0 comments on commit bfc897d

Please sign in to comment.