|
| 1 | +import { TokenId, ZkappCommand } from '../mina/v1/account-update.js'; |
| 2 | +import { Account, newAccount } from '../mina/v1/account.js'; |
| 3 | +import { NetworkValue } from '../mina/v1/precondition.js'; |
| 4 | +import { Account as AccountV2 } from '../mina/v2/account.js'; |
| 5 | +import { InitialApplyState, checkAndApplyAccountUpdate } from '../mina/v2/zkapp-logic.js'; |
| 6 | +import { PublicKey } from '../provable/crypto/signature.js'; |
| 7 | +import { UInt64 } from '../provable/int.js'; |
| 8 | +import { Field } from '../provable/wrapped.js'; |
| 9 | +export const DefaultTokenId = 1n; |
| 10 | + |
| 11 | +export type AccountId = { publicKey: PublicKey; tokenId?: Field }; |
| 12 | + |
| 13 | +export class Ledger { |
| 14 | + _nextLocation: bigint; |
| 15 | + _accounts: Map<bigint, Account>; |
| 16 | + _locations: Map<AccountId, bigint>; |
| 17 | + |
| 18 | + constructor() { |
| 19 | + this._nextLocation = 1n; |
| 20 | + this._accounts = new Map<bigint, Account>(); |
| 21 | + this._locations = new Map<AccountId, bigint>(); |
| 22 | + } |
| 23 | + |
| 24 | + static create(): Ledger { |
| 25 | + return new Ledger(); |
| 26 | + } |
| 27 | + |
| 28 | + addAccount(publicKey: PublicKey, balance: bigint | number | string): void { |
| 29 | + const accountId = { publicKey, tokenId: TokenId.default }; |
| 30 | + const location = (() => { |
| 31 | + const existing = this._locations.get(accountId); |
| 32 | + if (existing !== undefined) { |
| 33 | + throw new Error('account with public key already exists'); |
| 34 | + } |
| 35 | + |
| 36 | + const newLocation = this._nextLocation; |
| 37 | + this._nextLocation += 1n; |
| 38 | + |
| 39 | + return newLocation; |
| 40 | + })(); |
| 41 | + |
| 42 | + const account = newAccount(accountId); |
| 43 | + account.balance = UInt64.from(balance); |
| 44 | + |
| 45 | + this._locations.set(accountId, location); |
| 46 | + this._accounts.set(location, account); |
| 47 | + } |
| 48 | + |
| 49 | + getAccount(publicKey: PublicKey, tokenId: Field = TokenId.default): Account | undefined { |
| 50 | + const accountId = { publicKey, tokenId }; |
| 51 | + const location = this._locations.get(accountId); |
| 52 | + if (location === undefined) { |
| 53 | + return undefined; |
| 54 | + } |
| 55 | + |
| 56 | + return undefined; |
| 57 | + } |
| 58 | + |
| 59 | + applyTransaction(transaction: ZkappCommand, networkState: NetworkValue): void { |
| 60 | + for (const update of transaction.accountUpdates) { |
| 61 | + const { body, authorization } = update; |
| 62 | + |
| 63 | + if (body.authorizationKind.isProved && !authorization.proof) { |
| 64 | + throw Error( |
| 65 | + `The actual authorization does not match the expected authorization kind. Did you forget to invoke \`await tx.prove()\`?` |
| 66 | + ); |
| 67 | + } |
| 68 | + |
| 69 | + const accountId: AccountId = { publicKey: body.publicKey, tokenId: body.tokenId }; |
| 70 | + const account = (() => { |
| 71 | + const location = this._locations.get(accountId); |
| 72 | + if (location === undefined) { |
| 73 | + throw new Error('account not found'); |
| 74 | + } |
| 75 | + |
| 76 | + return this._accounts.get(location); |
| 77 | + })(); |
| 78 | + if (account === undefined) { |
| 79 | + throw new Error('account not found'); |
| 80 | + } |
| 81 | + |
| 82 | + const accountV2 = AccountV2.fromGeneric(account); |
| 83 | + |
| 84 | + |
| 85 | + /// WIP here |
| 86 | + |
| 87 | + checkAndApplyAccountUpdate(networkState, accountV2, update, InitialApplyState); |
| 88 | + } |
| 89 | + } |
| 90 | +} |
0 commit comments