Skip to content

Commit bfc897d

Browse files
authored
Merge pull request #28 from algorandfoundation/feat-v11
feat: add stubs for v11 op code and functions
2 parents 0853b82 + 3f4d4ce commit bfc897d

19 files changed

+899
-460
lines changed

src/context-helpers/internal-context.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Account, BaseContract, internal } from '@algorandfoundation/algorand-ty
22
import { AccountData } from '../impl/account'
33
import { ApplicationData } from '../impl/application'
44
import { AssetData } from '../impl/asset'
5+
import { VoterData } from '../impl/voter-params'
56
import { TransactionGroup } from '../subcontexts/transaction-context'
67
import { TestExecutionContext } from '../test-execution-context'
78

@@ -69,6 +70,14 @@ class InternalContext {
6970
}
7071
return data
7172
}
73+
74+
getVoterData(account: Account): VoterData {
75+
const data = this.ledger.voterDataMap.get(account)
76+
if (!data) {
77+
throw internal.errors.internalError('Unknown voter, check correct testing context is active')
78+
}
79+
return data
80+
}
7281
}
7382

7483
export const lazyContext = new InternalContext()

src/impl/account.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ export class AssetHolding {
2020
export class AccountData {
2121
optedAssets = new Uint64Map<AssetHolding>()
2222
optedApplications = new Uint64Map<Application>()
23+
incentiveEligible = false
24+
lastProposed?: uint64
25+
lastHeartbeat?: uint64
2326
account: Mutable<Omit<Account, 'bytes' | 'isOptedIn'>>
2427

2528
constructor() {

src/impl/acct-params.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Account, Application, gtxn, internal, uint64 } from '@algorandfoundatio
22
import { lazyContext } from '../context-helpers/internal-context'
33
import { asMaybeUint64Cls } from '../util'
44
import { getApp } from './app-params'
5+
import { Global } from './global'
56

67
export const getAccount = (acct: Account | internal.primitives.StubUint64Compat): Account => {
78
const acctId = asMaybeUint64Cls(acct)
@@ -84,14 +85,19 @@ export const AcctParams: internal.opTypes.AcctParamsType = {
8485
const acct = getAccount(a)
8586
return [acct.totalBoxBytes, acct.balance !== 0]
8687
},
87-
// TODO: implement v11 methods
88-
acctIncentiveEligible: function (_a: Account | uint64): readonly [boolean, boolean] {
89-
throw new Error('Function not implemented.')
88+
acctIncentiveEligible: function (a: Account | internal.primitives.StubUint64Compat): readonly [boolean, boolean] {
89+
const acct = getAccount(a)
90+
const accountData = lazyContext.ledger.accountDataMap.get(acct)
91+
return [accountData?.incentiveEligible ?? false, acct.balance !== 0]
9092
},
91-
acctLastProposed: function (_a: Account | uint64): readonly [uint64, boolean] {
92-
throw new Error('Function not implemented.')
93+
acctLastProposed: function (a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
94+
const acct = getAccount(a)
95+
const accountData = lazyContext.ledger.accountDataMap.get(acct)
96+
return [accountData?.lastProposed ?? Global.round, acct.balance !== 0]
9397
},
94-
acctLastHeartbeat: function (_a: Account | uint64): readonly [uint64, boolean] {
95-
throw new Error('Function not implemented.')
98+
acctLastHeartbeat: function (a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
99+
const acct = getAccount(a)
100+
const accountData = lazyContext.ledger.accountDataMap.get(acct)
101+
return [accountData?.lastHeartbeat ?? Global.round, acct.balance !== 0]
96102
},
97103
}

src/impl/block.ts

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,62 @@
1-
import { Account, bytes, internal, uint64 } from '@algorandfoundation/algorand-typescript'
1+
import { Account, bytes, internal, Uint64, uint64 } from '@algorandfoundation/algorand-typescript'
22
import { lazyContext } from '../context-helpers/internal-context'
3-
import { asUint64 } from '../util'
4-
import { itob } from './pure'
3+
import { asUint64, getRandomBytes } from '../util'
4+
5+
export class BlockData {
6+
seed: bytes
7+
timestamp: uint64
8+
proposer: Account
9+
feesCollected: uint64
10+
bonus: uint64
11+
branch: bytes
12+
feeSink: Account
13+
protocol: bytes
14+
txnCounter: uint64
15+
proposerPayout: uint64
16+
17+
constructor() {
18+
this.seed = getRandomBytes(32).asAlgoTs()
19+
this.timestamp = asUint64(Date.now())
20+
this.proposer = Account()
21+
this.feesCollected = Uint64(0)
22+
this.bonus = Uint64(0)
23+
this.branch = getRandomBytes(32).asAlgoTs()
24+
this.feeSink = Account()
25+
this.protocol = getRandomBytes(32).asAlgoTs()
26+
this.txnCounter = Uint64(0)
27+
this.proposerPayout = Uint64(0)
28+
}
29+
}
530

631
export const Block: internal.opTypes.BlockType = {
732
blkSeed: function (a: internal.primitives.StubUint64Compat): bytes {
8-
return itob(lazyContext.ledger.getBlockContent(a).seed)
33+
return lazyContext.ledger.getBlockData(a).seed
934
},
1035
blkTimestamp: function (a: internal.primitives.StubUint64Compat): uint64 {
11-
return asUint64(lazyContext.ledger.getBlockContent(a).timestamp)
36+
return lazyContext.ledger.getBlockData(a).timestamp
1237
},
13-
// TODO: implement v11 methods
14-
blkProposer: function (_a: uint64): Account {
15-
throw new Error('Function not implemented.')
38+
blkProposer: function (a: uint64): Account {
39+
return lazyContext.ledger.getBlockData(a).proposer
1640
},
17-
blkFeesCollected: function (_a: uint64): uint64 {
18-
throw new Error('Function not implemented.')
41+
blkFeesCollected: function (a: uint64): uint64 {
42+
return lazyContext.ledger.getBlockData(a).feesCollected
1943
},
20-
blkBonus: function (_a: uint64): uint64 {
21-
throw new Error('Function not implemented.')
44+
blkBonus: function (a: uint64): uint64 {
45+
return lazyContext.ledger.getBlockData(a).bonus
2246
},
23-
blkBranch: function (_a: uint64): bytes {
24-
throw new Error('Function not implemented.')
47+
blkBranch: function (a: uint64): bytes {
48+
return lazyContext.ledger.getBlockData(a).branch
2549
},
26-
blkFeeSink: function (_a: uint64): Account {
27-
throw new Error('Function not implemented.')
50+
blkFeeSink: function (a: uint64): Account {
51+
return lazyContext.ledger.getBlockData(a).feeSink
2852
},
29-
blkProtocol: function (_a: uint64): bytes {
30-
throw new Error('Function not implemented.')
53+
blkProtocol: function (a: uint64): bytes {
54+
return lazyContext.ledger.getBlockData(a).protocol
3155
},
32-
blkTxnCounter: function (_a: uint64): uint64 {
33-
throw new Error('Function not implemented.')
56+
blkTxnCounter: function (a: uint64): uint64 {
57+
return lazyContext.ledger.getBlockData(a).txnCounter
3458
},
35-
blkProposerPayout: function (_a: uint64): uint64 {
36-
throw new Error('Function not implemented.')
59+
blkProposerPayout: function (a: uint64): uint64 {
60+
return lazyContext.ledger.getBlockData(a).proposerPayout
3761
},
3862
}

src/impl/global.ts

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ export class GlobalData {
2525
assetOptInMinBalance: uint64
2626
genesisHash: bytes
2727
opcodeBudget?: uint64
28+
payoutsEnabled: boolean
29+
payoutsGoOnlineFee: uint64
30+
payoutsPercent: uint64
31+
payoutsMinBalance: uint64
2832

2933
constructor() {
3034
this.minTxnFee = Uint64(MIN_TXN_FEE)
@@ -35,6 +39,10 @@ export class GlobalData {
3539
this.assetCreateMinBalance = Uint64(DEFAULT_ASSET_CREATE_MIN_BALANCE)
3640
this.assetOptInMinBalance = Uint64(DEFAULT_ASSET_OPT_IN_MIN_BALANCE)
3741
this.genesisHash = DEFAULT_GLOBAL_GENESIS_HASH
42+
this.payoutsEnabled = false
43+
this.payoutsGoOnlineFee = Uint64(0)
44+
this.payoutsPercent = Uint64(0)
45+
this.payoutsMinBalance = Uint64(0)
3846
}
3947
}
4048
const getGlobalData = (): GlobalData => {
@@ -184,10 +192,44 @@ export const Global: internal.opTypes.GlobalType = {
184192
get genesisHash(): bytes {
185193
return getGlobalData().genesisHash
186194
},
187-
payoutsEnabled: false,
188-
// TODO: implement v11 fields
189-
payoutsGoOnlineFee: 0,
190-
payoutsPercent: 0,
191-
payoutsMinBalance: 0,
192-
payoutsMaxBalance: 0,
195+
196+
/**
197+
* Whether block proposal payouts are enabled.
198+
* Min AVM version: 11
199+
*/
200+
get payoutsEnabled(): boolean {
201+
return getGlobalData().payoutsEnabled
202+
},
203+
204+
/**
205+
* The fee required in a keyreg transaction to make an account incentive eligible.
206+
* Min AVM version: 11
207+
*/
208+
get payoutsGoOnlineFee(): uint64 {
209+
return getGlobalData().payoutsGoOnlineFee
210+
},
211+
212+
/**
213+
* The percentage of transaction fees in a block that can be paid to the block proposer.
214+
* Min AVM version: 11
215+
*/
216+
get payoutsPercent(): uint64 {
217+
return getGlobalData().payoutsPercent
218+
},
219+
220+
/**
221+
* The minimum algo balance an account must have in the agreement round to receive block payouts in the proposal round.
222+
* Min AVM version: 11
223+
*/
224+
get payoutsMinBalance(): uint64 {
225+
return getGlobalData().payoutsMinBalance
226+
},
227+
228+
/**
229+
* The maximum algo balance an account can have in the agreement round to receive block payouts in the proposal round.
230+
* Min AVM version: 11
231+
*/
232+
get payoutsMaxBalance(): uint64 {
233+
return getGlobalData().payoutsMinBalance
234+
},
193235
}

src/impl/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ export { Global } from './global'
1111
export { GTxn } from './gtxn'
1212
export { GITxn, ITxn, ITxnCreate } from './itxn'
1313
export { arg } from './logicSigArg'
14+
export { onlineStake } from './online-stake'
1415
export * from './pure'
1516
export { gloadBytes, gloadUint64, Scratch } from './scratch'
1617
export { gaid, Txn } from './txn'
18+
export { VoterParams } from './voter-params'

src/impl/online-stake.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { internal } from '@algorandfoundation/algorand-typescript'
2+
import { lazyContext } from '../context-helpers/internal-context'
3+
4+
export const onlineStake: internal.opTypes.OnlineStakeType = () => {
5+
return lazyContext.ledger.onlineStake
6+
}

src/impl/transactions.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ export class KeyRegistrationTransaction extends TransactionBase implements gtxn.
117117
this.voteKeyDilution = fields.voteKeyDilution ?? Uint64(0)
118118
this.nonparticipation = fields.nonparticipation ?? false
119119
this.stateProofKey = fields.stateProofKey ?? Bytes()
120+
const globalData = lazyContext.ledger.globalData
121+
if (this.fee >= globalData.payoutsGoOnlineFee && globalData.payoutsEnabled) {
122+
lazyContext.ledger.patchAccountData(this.sender, {
123+
incentiveEligible: true,
124+
})
125+
}
120126
}
121127

122128
readonly voteKey: bytes

src/impl/voter-params.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Account, internal, uint64 } from '@algorandfoundation/algorand-typescript'
2+
import { lazyContext } from '../context-helpers/internal-context'
3+
import { getAccount } from './acct-params'
4+
5+
export class VoterData {
6+
balance: uint64
7+
incentiveEligible: boolean
8+
9+
constructor() {
10+
this.balance = 0
11+
this.incentiveEligible = false
12+
}
13+
}
14+
15+
const getVoterData = (a: Account | internal.primitives.StubUint64Compat): VoterData => {
16+
const acct = getAccount(a)
17+
return lazyContext.getVoterData(acct)
18+
}
19+
20+
export const VoterParams: internal.opTypes.VoterParamsType = {
21+
voterBalance: function (a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
22+
const data = getVoterData(a)
23+
return [data.balance, data.balance !== 0]
24+
},
25+
voterIncentiveEligible: function (a: Account | internal.primitives.StubUint64Compat): readonly [boolean, boolean] {
26+
const data = getVoterData(a)
27+
return [data.incentiveEligible, data.balance !== 0]
28+
},
29+
}

src/subcontexts/ledger-context.ts

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,23 @@ import { MAX_UINT64 } from '../constants'
44
import { AccountData, AssetHolding } from '../impl/account'
55
import { ApplicationData } from '../impl/application'
66
import { AssetData } from '../impl/asset'
7+
import { BlockData } from '../impl/block'
78
import { GlobalData } from '../impl/global'
89
import { GlobalStateCls } from '../impl/state'
10+
import { VoterData } from '../impl/voter-params'
911
import { asBigInt, asMaybeBytesCls, asMaybeUint64Cls, asUint64, asUint64Cls, iterBigInt } from '../util'
1012

11-
interface BlockData {
12-
seed: bigint
13-
timestamp: bigint
14-
}
15-
1613
export class LedgerContext {
1714
appIdIter = iterBigInt(1001n, MAX_UINT64)
1815
assetIdIter = iterBigInt(1001n, MAX_UINT64)
1916
applicationDataMap = new Uint64Map<ApplicationData>()
2017
appIdContractMap = new Uint64Map<BaseContract>()
2118
accountDataMap = new AccountMap<AccountData>()
2219
assetDataMap = new Uint64Map<AssetData>()
20+
voterDataMap = new AccountMap<VoterData>()
2321
blocks = new Uint64Map<BlockData>()
2422
globalData = new GlobalData()
23+
onlineStake = 0
2524

2625
/* @internal */
2726
addAppIdContractMap(appId: internal.primitives.StubUint64Compat, contract: BaseContract): void {
@@ -112,19 +111,36 @@ export class LedgerContext {
112111
}
113112
}
114113

115-
setBlock(
116-
index: internal.primitives.StubUint64Compat,
117-
seed: internal.primitives.StubUint64Compat,
118-
timestamp: internal.primitives.StubUint64Compat,
119-
): void {
120-
const i = asBigInt(index)
121-
const s = asBigInt(seed)
122-
const t = asBigInt(timestamp)
114+
patchAccountData(account: Account, data: Partial<AccountData>) {
115+
const accountData = this.accountDataMap.get(account) ?? new AccountData()
116+
this.accountDataMap.set(account, {
117+
...accountData,
118+
...data,
119+
account: {
120+
...accountData?.account,
121+
...data.account,
122+
},
123+
})
124+
}
123125

124-
this.blocks.set(i, { seed: s, timestamp: t })
126+
patchVoterData(account: Account, data: Partial<VoterData>) {
127+
const voterData = this.voterDataMap.get(account) ?? new VoterData()
128+
this.voterDataMap.set(account, {
129+
...voterData,
130+
...data,
131+
})
132+
}
133+
134+
patchBlockData(index: internal.primitives.StubUint64Compat, data: Partial<BlockData>): void {
135+
const i = asUint64(index)
136+
const blockData = this.blocks.get(i) ?? new BlockData()
137+
this.blocks.set(i, {
138+
...blockData,
139+
...data,
140+
})
125141
}
126142

127-
getBlockContent(index: internal.primitives.StubUint64Compat): BlockData {
143+
getBlockData(index: internal.primitives.StubUint64Compat): BlockData {
128144
const i = asBigInt(index)
129145
if (this.blocks.has(i)) {
130146
return this.blocks.get(i)!

0 commit comments

Comments
 (0)