Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

13. Protocol-owned programs and sysvars

13.1 Identity and deployment model

Protocol-owned behavior is selected by public key plus bank state, not by a human-readable program name. A program can be a native builtin, a Core-BPF deployment under an SBF loader, a stateless precompile, or feature-disabled. Migration can retain a public program identifier while changing its execution owner and code source.

[SOL-PRG-0001] Protocol-owned dispatch MUST use the exact program address and form listed in Section 13.2 for epoch E.

[SOL-PRG-0002] For every Core-BPF protocol program, replay MUST resolve and authenticate from the bank the loader, the ProgramData account when applicable, the executable bytes, the deployment slot, and the upgrade-authority state, then execute those bytes under Chapter 12.

[SOL-PRG-0003] A validator MUST select dispatch from the program account and active feature state visible in the bank and MUST NOT substitute a builtin just because the public key is historically associated with one.

[SOL-PRG-0004] A builtin-to-Core-BPF migration MUST update the program and ProgramData accounts, capitalization, program resolution, and execution-cost classification exactly at the activating bank. The migrated bytes MUST have deployment slot d and effective slot d+1, where d is the activating bank’s slot.

13.2 Known protocol identifiers

Normative table.

SurfaceProgram IDForm in epoch E
System program11111111111111111111111111111111native builtin
Vote programVote111111111111111111111111111111111111111native builtin
Stake programStake11111111111111111111111111111111111111Core BPF
Config programConfig1111111111111111111111111111111111111Core BPF
Feature programFeature111111111111111111111111111111111111Core BPF
Address lookup tableAddressLookupTab1e1111111111111111111111111Core BPF
Compute budgetComputeBudget111111111111111111111111111111native preflight interpreter
Native loaderNativeLoader1111111111111111111111111111111reserved builtin owner/dispatcher
Deprecated BPF loaderBPFLoader1111111111111111111111111111111111compatibility execution loader
BPF loaderBPFLoader2111111111111111111111111111111111compatibility execution loader
Upgradeable BPF loaderBPFLoaderUpgradeab1e11111111111111111111111native loader builtin
Loader-v4 addressLoaderV411111111111111111111111111111111111reserved and inactive; retained in default-CU and reserved-account classification only
ZK ElGamal proofZkE1Gama1Proof11111111111111111111111111111native builtin
secp256k1 precompileKeccakSecp256k11111111111111111111111111111precompile
Ed25519 precompileEd25519SigVerify111111111111111111111111111precompile
secp256r1 precompileSecp256r1SigVerify1111111111111111111111111precompile
Incinerator1nc1nerator11111111111111111111111111111111freeze-time account hook

[SOL-PRG-0005] A program identifier absent from the epoch-E catalog MUST NOT acquire protocol-owned dispatch.

[SOL-PRG-0006] A native builtin MUST follow its normative native transition, while a Core-BPF program MUST follow the authenticated deployed bytes selected by ResolveProgram. A public identifier, historical native behavior, or prose summary MUST NOT override the bank-selected form.

13.3 Protocol program state

System, vote, stake, config, address-lookup-table, loader, feature, and proof-program accounts contain consensus state. Their state tags, byte layouts, authorities, signer/writable requirements, arithmetic, and error codes form part of consensus even when the program is implemented natively.

[SOL-PRG-0007] An active protocol program MUST decode instruction and account-state bytes with the exact bank-selected format and MUST deterministically reject unknown discriminants, truncation, invalid lengths, invalid authorities, missing signatures, and prohibited writable or owner combinations.

[SOL-PRG-0008] A protocol program MUST apply checked or saturating arithmetic exactly where its specified transition requires it and MUST return the specified error without committing partial program changes when a precondition fails.

[SOL-PRG-0009] Address-lookup-table resolution MUST use only lookup-table state produced by the active address-lookup-table program and visible under the bank-slot and SlotHashes rules in Chapter 7.

[SOL-PRG-0010] System durable-nonce initialization, authorization, advancement, withdrawal, and deallocation MUST preserve the nonce state and replay conditions used by Chapters 7 and 11.

13.3.1 System program

System-program instruction data is a Section 5.3 fixed-integer enum. It begins with the u32 little-endian discriminant below. A string is length:u64 || length UTF-8 bytes, has no terminator, and is limited to 32 bytes for a derivation seed. Public keys are 32 raw bytes. The decoder permits trailing instruction-data bytes after a complete variant.

Normative table.

Disc.InstructionExact payload after discriminantRequired instruction accounts
0CreateAccountlamports:u64, space:u64, owner[32]0 funding writable signer; 1 new writable signer
1Assignowner[32]0 assigned writable signer
2Transferlamports:u640 funding writable signer; 1 recipient writable
3CreateAccountWithSeedbase[32], seed:string, lamports:u64, space:u64, owner[32]0 funding writable signer; 1 created writable; the named base must be an instruction signer
4AdvanceNonceAccountempty0 nonce writable; 1 RecentBlockhashes; 2 nonce-authority signer
5WithdrawNonceAccountlamports:u640 nonce writable; 1 recipient writable; 2 RecentBlockhashes; 3 Rent; 4 authority signer
6InitializeNonceAccountauthority[32]0 nonce writable; 1 RecentBlockhashes; 2 Rent
7AuthorizeNonceAccountnew_authority[32]0 nonce writable; 1 current-authority signer
8Allocatespace:u640 allocated writable signer
9AllocateWithSeedbase[32], seed:string, space:u64, owner[32]0 allocated writable; the named base must be an instruction signer
10AssignWithSeedbase[32], seed:string, owner[32]0 assigned writable; the named base must be an instruction signer
11TransferWithSeedlamports:u64, from_seed:string, from_owner[32]0 funding writable; 1 base signer; 2 recipient writable
12UpgradeNonceAccountempty0 nonce writable
13CreateAccountAllowPrefundlamports:u64, space:u64, owner[32]0 new writable signer; if lamports is nonzero, 1 funding writable signer

For a seed-derived address, compute SHA256(base[32] || seed_bytes || owner[32]); the result must equal the address being allocated, assigned, created, or debited. Allocation requires a system-owned account with empty data and space <= 10,485,760; assignment requires empty data; a lamport transfer may debit only a system-owned account with empty data. CreateAccount additionally requires the destination balance to be zero. CreateAccountAllowPrefund omits only that zero-balance condition; all allocation, signer, derivation, funding, and assignment checks remain.

Normative table. System Program custom-error domain.

ValueExact custom errorFirst applicable cause
0AccountAlreadyInUsea create destination is already allocated or otherwise in use
1ResultWithNegativeLamportsa checked debit would make the source balance negative
2InvalidProgramIdassignment/debit ownership or derived owner is not permitted
3InvalidAccountDataLengthallocation length or existing data length violates the selected instruction
4MaxSeedLengthExceededa seed exceeds 32 bytes
5AddressWithSeedMismatchSHA256(base || seed || owner) differs from the addressed account
6NonceNoRecentBlockhashesnonce initialization/advance lacks the required recent-blockhash state
7NonceBlockhashNotExpiredthe stored durable nonce is still the bank’s next durable nonce
8NonceUnexpectedBlockhashValuethe supplied/stored nonce does not match the transaction marker

Instruction decode precedes account-list lookup. Thereafter the System Program checks account count and signer privilege in listed-account order, address/seed derivation, ownership and state, data length/allocation, then balances. A named custom error takes precedence at its predicate; otherwise the exact standard InstructionError in Section 12.7 applies.

[SOL-PRG-0011] The System program MUST decode the table above, enforce the listed signer and writable privileges, use checked lamport arithmetic, and perform allocation, assignment, transfer, and durable-nonce transitions atomically. An invalid derivation, owner, data length, balance, signature, or account list MUST fail the instruction without a partial System-program transition.

13.3.2 Upgradeable BPF loader

Upgradeable-loader state begins with a u32 little-endian discriminant. The fixed metadata offsets, rather than the serialized length of an absent option, locate following program bytes.

Normative table.

State disc.StateExact metadata and following bytes
0Uninitializedfour-byte discriminant LE(0,4) followed by zero or more uninterpreted capacity bytes; a Close transition truncates the account to the four-byte discriminant
1Bufferbyte 4 authority option (0 absent, 1 present); bytes [5,37) hold the authority when present and are ignored otherwise; program bytes begin at 37
2Programbytes [4,36) ProgramData address; metadata length 36
3ProgramData[4,12) last-deployment slot; byte 12 upgrade-authority option; [13,45) authority when present and ignored otherwise; SBF bytes begin at 45

Management instruction data is another Section 5.3 fixed-integer enum:

Normative table.

Disc.InstructionExact payload after discriminant
0InitializeBufferempty
1Writeoffset:u32, byte_count:u64, bytes[byte_count]
2DeployWithMaxDataLenmax_data_len:u64
3Upgradeempty
4SetAuthorityempty; optional new-authority account selects Some or None
5Closeempty
6ExtendProgramadditional_bytes:u32
7SetAuthorityCheckedempty

Normative table. Upgradeable-loader ordered accounts and transitions.

InstructionOrdered instruction accountsExact transition
InitializeBuffer0 Buffer W; 1 authorityRequire Loader-owned Uninitialized state; store Buffer with Some(key1). Epoch-E processing requires account 1 even though older construction APIs described it as optional.
Write0 Buffer W; 1 authority SRequire matching present Buffer authority; copy payload to [37+offset,37+offset+byte_count) without resizing.
DeployWithMaxDataLen0 payer W,S; 1 ProgramData W; 2 Program W; 3 Buffer W; 4 Rent; 5 Clock; 6 System; 7 authority SDerive account 1 from account 2, first transfer every Buffer lamport to payer, then create/fund ProgramData to 45+max_data_len; validate/copy the Buffer ELF, store ProgramData with current slot and authority, store Program pointing to ProgramData, set Program executable, and truncate Buffer to its 37-byte metadata.
Upgrade0 ProgramData W; 1 Program W; 2 Buffer W; 3 spill W; 4 Rent; 5 Clock; 6 authority SRequire matching Program/ProgramData, a deployment slot older than Clock slot, and matching Buffer and ProgramData authority; verify the Buffer ELF, replace ProgramData bytes and slot, zero the unused suffix, retain exactly its rent-exempt lamports, send the excess combined ProgramData/Buffer lamports to spill, and truncate/drain Buffer.
SetAuthority0 Buffer or ProgramData W; 1 current authority S; optional 2 new authorityRequire the stored present authority to match account 1. Buffer requires account 2; ProgramData may omit it to become immutable. Store the selected authority without changing deployment slot or bytes.
Close0 Loader-owned state W; 1 recipient W; optional 2 authority S; optional 3 associated Program WRequire accounts 0 and 1 distinct. Uninitialized closes without authority. Buffer requires matching authority. ProgramData additionally requires account 3 to point to account 0, a deployment slot older than Clock slot, and matching authority; close ProgramData and make the associated Program unavailable to ResolveProgram. Transfer all lamports to account 1 and logically delete account 0 by setting its lamports to zero.
ExtendProgram0 ProgramData W; 1 Program W; optional 2 System; optional 3 payer W,SRequire matching Program/ProgramData and present upgrade authority. Increase ProgramData length by the payload amount, subject to the 10-KiB/minimum and 10-MiB limits; if its balance is short, transfer the exact rent-exemption shortfall from payer through System.
SetAuthorityChecked0 Buffer or ProgramData W; 1 current authority S; 2 new authority SRequire both signatures and the matching present authority; store Some(account2) without changing slot or bytes.

ProgramData is derived from the Program address by the Section 5.5 PDA algorithm with the Program address as the sole seed and the Upgradeable BPF Loader as program ID. Write ranges must remain within the Buffer data after byte 37. Deploy and upgrade validate the resulting ELF under Section 12.8.5, copy program bytes into ProgramData after byte 45, set the deployment slot, and make the Program account executable. An upgrade or authority change requires the current authority; SetAuthorityChecked also requires the new authority. ExtendProgram adds at least 10,240 bytes unless fewer than 10,240 bytes remain before the 10-MiB account limit, and cannot exceed that limit.

[SOL-PRG-0012] The Upgradeable BPF Loader MUST preserve the state tags, metadata offsets, PDA relationship, authority checks, minimum extension, deployment slot, executable transition, and bank-visible program bytes above. A successful deploy or upgrade at slot d MUST set effective slot d+1; the new bytes are unavailable for invocation in d and become resolvable in the child slot.

13.3.3 Vote program

Vote-account data is a Section 5.3 fixed-integer enum followed by unused account capacity. Its first four bytes are a little-endian state discriminant: 0 Uninitialized, 1 V1_14_11, 2 V3, or 3 V4. The decoder permits unused trailing bytes. Serialization overwrites the encoded prefix but preserves any remaining account bytes, which therefore remain part of the account hash.

The component encodings used below are:

Normative table.

ComponentExact encoding
optional valuetag byte 0, or tag byte 1 followed by the value
Lockoutslot:u64, confirmation_count:u32
LandedVotelatency:u8, then Lockout
authorized-voter mapcount:u64, then count records epoch:u64, voter[32] in increasing epoch order
prior-voter ring32 records voter[32], first_epoch:u64, last_epoch:u64, then next_index:u64, then is_empty:u8
epoch creditscount:u64, then count records epoch:u64, credits:u64, previous_credits:u64
block timestampslot:u64, unix_timestamp:i64

Legacy states remain decodable because an untouched account can retain them. Their exact field sequences after the four-byte state discriminant are:

Normative table.

StateAccount sizeExact field sequence
V1_14_11 (1)3,731 bytesnode[32], withdrawer[32], commission:u8, vote_count:u64, vote_count Lockout records, optional root_slot, authorized-voter map, prior-voter ring, epoch credits, block timestamp
V3 (2)3,762 bytesnode[32], withdrawer[32], commission:u8, vote_count:u64, vote_count LandedVote records, optional root_slot, authorized-voter map, prior-voter ring, epoch credits, block timestamp

Every successful epoch-E Vote-program mutation stores V4 and grows a shorter legacy account to 3,762 bytes only if its balance is rent-exempt at that length. The V4 prefix after discriminant 3 is:

Normative table.

OrderV4 fieldExact encoding
1node identity32 bytes
2authorized withdrawer32 bytes
3inflation-rewards collector32 bytes
4block-revenue collector32 bytes
5inflation commissionu16 basis points, 0..=10000
6block-revenue commissionu16 basis points, 0..=10000
7pending delegator rewardsu64
8BLS voter keyoption tag and, when present, 48 compressed BLS12-381 G1 bytes
9landed votescount:u64, then that many LandedVote records, oldest first; at most 31
10root slotoptional u64
11authorized votersauthorized-voter map
12epoch creditsepoch-credits sequence, at most 64 records
13last timestampblock timestamp

Vote instruction data also begins with a u32 little-endian discriminant and permits trailing bytes. Only the following forms execute in epoch E; any other Vote-instruction discriminant is invalid instruction data.

Normative table.

Disc.Active instructionExact payload after discriminant
0InitializeAccountnode[32], authorized_voter[32], authorized_withdrawer[32], commission:u8
1Authorizenew_authority[32], then authority-kind
3Withdrawlamports:u64
4UpdateValidatorIdentityempty
5UpdateCommissioncommission_percent:u8
7AuthorizeCheckedauthority-kind; new authority is an instruction account signer
10AuthorizeWithSeedauthority-kind, derived_owner[32], seed:string, new_authority[32]
11AuthorizeCheckedWithSeedauthority-kind, derived_owner[32], seed:string; new authority is an instruction account signer
14TowerSynccompact tower shown below
15TowerSyncSwitchcompact tower, then switch_proof_hash[32]
18UpdateCommissionBpscommission_bps:u16, commission_kind:u32; kind 0 is inflation rewards, and other kinds are unavailable in epoch E

Authority-kind is LE(0,4) for a voter, LE(1,4) for a withdrawer, or LE(2,4) || bls_key[48] || proof_of_possession[96] for a BLS voter. A seed string uses length:u64 || UTF-8 bytes and is at most 32 bytes.

Normative table.

Compact-tower orderExact encoding
1root u64; u64::MAX means no root
2lockout count as canonical compact-u16, at most 31
3for each lockout: canonical unsigned-LEB128 u64 offset from the root (or zero when no root) for the first, then from the preceding vote; followed by confirmation_count:u8
4voted-bank hash [32]
5timestamp option tag and, when present, unix_timestamp:i64
6terminal FEC root [32]

Offsets are accumulated with checked u64 addition and must produce strictly increasing vote slots. The voted-bank hash must equal SlotHashes for the last vote, and the block identifier field is the Chapter 14 terminal FEC root for the same slot. Vote lockouts, roots, authorization, timestamp monotonicity, credit accrual, withdrawals, collector fields, and commission updates are applied to the V4 fields as specified by Chapters 15 and 16.

[SOL-PRG-0013] The Vote program MUST accept only the epoch-E instruction forms above, decode legacy account states exactly, store every successful mutation as V4, and enforce the named authority, tower, SlotHashes, timestamp, rent, credit, withdrawal, and commission rules without partially updating the vote account on failure.

Normative table. Vote Program custom-error domain.

ValueExact custom errorValueExact custom error
0VoteTooOld11ConfirmationTooLarge
1SlotsMismatch12RootRollBack
2SlotHashMismatch13ConfirmationRollBack
3EmptySlots14SlotSmallerThanRoot
4TimestampTooOld15TooManyVotes
5TooSoonToReauthorize16VotesTooOldAllFiltered
6LockoutConflict17RootOnDifferentFork
7NewVoteStateLockoutMismatch18ActiveVoteAccountClose
8SlotsNotOrdered19CommissionUpdateTooLate
9ConfirmationsNotOrdered20AssertionFailed
10ZeroConfirmations

Vote instruction decode and account-count/signature checks precede these semantic errors. For a vote/tower update, test empty/ordered/bounded tower shape, old-slot filtering, SlotHashes membership and bank hash, proposed-root ancestry, lockout consistency, then timestamp monotonicity; the first failing predicate supplies the value above. Authority/owner/account-data failures use the exact standard InstructionError from Section 12.7.

13.3.4 Stake Program v5

A Stake account is exactly 200 bytes, owned by the Stake Program, and decoded with FIXINT-LE. Bytes not occupied by the selected state are retained as zero/unused capacity and remain committed by the account hash.

Normative table. Stake v5 state byte layout.

State tag / rangeFieldExact encoding
[0,4)stateu32: 0 Uninitialized, 1 Initialized, 2 Stake, 3 RewardsPool
[4,12)rent-exempt reserveu64 lamports; present for tags 1 and 2
[12,44), [44,76)staker, withdrawertwo public keys; tags 1 and 2
[76,84), [84,92), [92,124)lockupUnix timestamp i64 seconds, epoch u64, custodian [32]; tags 1 and 2
[124,156)delegated vote addresspublic key; tag 2
[156,164)delegated stakeu64 lamports; tag 2
[164,172), [172,180)activation/deactivation epochtwo u64; u64::MAX means bootstrap activation or no deactivation; tag 2
[180,188)reserved rateeight bytes retained for compatibility; tag 2
[188,196)credits observedvote credits u64; tag 2
[196,197)flagsbit 0 means fully activate before deactivation; bits 1–7 zero; tag 2
remaining through byte 200unused capacitypreserved/zero-filled by canonical state writes

Stake instructions use FIXINT-LE: a u32 discriminant followed by the payload below. Option<T> is tag byte 0, or 1||T; StakeAuthorize is u32 0 staker or 1 withdrawer. LockupArgs is optional i64, optional u64, optional key; the checked form omits the key and takes a signed new custodian account. A seed string is u64 length || UTF-8 and at most 32 bytes.

Normative table. Stake v5 instructions and ordered accounts.

Disc.Instruction / payloadOrdered instruction accounts and transition
0Initialize: Authorized, Lockupstake W; Rent. Require tag 0, 200 bytes, and rent exemption; store tag 1.
1Authorize: new key, kindstake W; Clock; current authority S; optional lockup custodian S. Replace selected authority; an in-force withdrawer lockup additionally requires custodian.
2DelegateStakestake W; vote; Clock; StakeHistory; unused StakeConfig; staker S. Require tag 1 or eligible tag 2, valid vote state, and delegation at least 1,000,000,000 lamports after reserve; set voter, stake, activation epoch, no-deactivation, and observed credits.
3Split: u64 lamportssource stake W; uninitialized destination W; staker S. Preserve Meta/delegation proportionally; each nonzero delegated remainder satisfies minimum delegation and each account preserves its rent reserve.
4Withdraw: u64 lamportsstake W; recipient W; Clock; StakeHistory; withdrawer S; optional custodian S. Withdraw only lamports not effective/activating stake and not required reserve unless closing; enforce lockup.
5Deactivatestake W; Clock; staker S. Require delegated state and no prior deactivation; set deactivation to current epoch, subject to flag.
6SetLockup: LockupArgsstake W; authority S. While lockup is in force require current custodian; otherwise require withdrawer; replace each present field.
7Mergedestination W; source W; Clock; StakeHistory; staker S. Require identical authorities/lockup and compatible inactive, activating, or active delegations; active voter and credits must match; add balances/delegation checked and drain source.
8AuthorizeWithSeedstake W; base S; Clock; optional custodian S. Derive current authority by Section 5.5 seed form, then apply disc. 1.
9InitializeCheckedstake W; Rent; staker; withdrawer S. Store tag 1 with no lockup.
10AuthorizeChecked: kindstake W; Clock; current authority S; new authority S; optional custodian S. Apply disc. 1 using account 3 as new key.
11AuthorizeCheckedWithSeedstake W; base S; Clock; new authority S; optional custodian S. Seed payload supplies kind/seed/owner; apply checked authorization.
12SetLockupCheckedstake W; current authority S; optional new custodian S. Apply disc. 6 and take a present custodian from account 2.
13GetMinimumDelegationnone. Set return data to LE(1,000,000,000,8).
14DeactivateDelinquentstake W; delinquent vote; reference vote. Require stake voter match, the reference voted within the last five epochs, and delinquent did not; set current deactivation epoch.
15Redelegateinactive in E; return InvalidInstructionData.
16MoveStake: u64 lamportsactive source W; active or inactive destination W; staker S. Require identical Meta, fully active source, compatible voter/credits, minimum source remainder and destination delegation; move only active stake.
17MoveLamports: u64 lamportsfully active/inactive source W; mergeable destination W; staker S. Require identical Meta and move only lamports above delegated backing and rent reserve.

Effective, activating, and deactivating amounts are computed from the Delegation fields and StakeHistory by Section 16.4 before any withdraw, split, merge, delinquent deactivation, move, or reward operation. A lockup is in force when lockup.unix_timestamp > Clock.unix_timestamp or lockup.epoch > Clock.epoch, unless its custodian signed. Stake mutations other than reward redemption fail with Custom(16) while EpochRewards is active.

Normative table. Stake custom-error domain.

ValueNameValueName
0NoCreditsToRedeem9InsufficientReferenceVotes
1LockupInForce10VoteAddressMismatch
2AlreadyDeactivated11MinimumDelinquentEpochsForDeactivationNotMet
3TooSoonToRedelegate12InsufficientDelegation
4InsufficientStake13RedelegateTransientOrInactiveStake
5MergeTransientStake14RedelegateToSameVoteAccount
6MergeMismatch15RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted
7CustodianMissing16EpochRewardsActive
8CustodianSignatureMissing

All other failures use the exact standard InstructionError named by the failed predicate: owner, state bytes, account count, signature, authority, funds, rent, or checked arithmetic are tested in that order after instruction decode. Stake reward redemption uses the vote-credit and commission algorithm in Section 16.6. An ordinary zero-lamport reward does not advance credits_observed; only the activation-epoch and credits-rewind forced updates named there do so without a payout.

13.3.5 Config Program

A Config account is owned by the Config Program. Its data begins with a canonical ShortU16 key count N, then N records of public key [32] and Boolean signer byte, followed by opaque configuration bytes. The entire instruction data uses the same layout and replaces the account prefix; there is no instruction discriminant.

Normative table. Config transition and exact precedence.

StepRule / exact error
1Decode incoming key list; malformed input is InvalidInstructionData.
2Account 0 must be Config-owned (InvalidAccountOwner) and current key prefix must decode (InvalidAccountData).
3If the current signer list is empty, account 0 must sign (MissingRequiredSignature).
4Visit incoming signer records in order. For each signer other than account 0, consume the next instruction account and require the same key and signer privilege; account-0 records require account 0 to sign. On an initialized config, every incoming signer must occur in the current signer set. First failure is MissingRequiredSignature.
5Duplicate (key,is_signer) records are InvalidArgument. If the number of current signer records exceeds the number supplied, return MissingRequiredSignature.
6If account data length is smaller than instruction-data length, return InvalidInstructionData; otherwise copy the complete instruction bytes to offset zero and leave the remaining account bytes unchanged.

The fixed Core-BPF behavior consumes SBF compute under Chapter 12; the former native 450-CU dispatch constant is not applied. This transition is the only Config instruction and provides no interpretation of the opaque suffix.

13.3.6 Feature Program

The single authoritative Feature-account layout and decoder are in Section 17.1. A Feature-Program-created account has the nine-byte canonical length; the bank feature scan applies Section 17.1’s explicitly broader accepted domain for already-present account state.

Feature activation is not a Feature-Program instruction. It is an atomic transaction sequence that System-transfers the rent-exempt balance, allocates nine bytes, and assigns the signed feature address to the Feature Program. At the next new-bank feature scan, a pending account selected for activation is written as 1||LE(current_slot,8); the behavioral side effects in Appendix C and Section 17.4 are applied in that same bank before transactions.

The sole Feature instruction is byte 00, RevokePendingActivation, with feature account 0 W,S, incinerator account 1 W, and System Program account 2 readonly. Require Feature ownership and a decodable pending state; an active state returns Custom(0) (FeatureAlreadyActivated). On success transfer all lamports to the incinerator, resize the feature account to zero, and assign it to the System Program. Revocation after activation cannot undo a rule. A Core-BPF migration changes only execution form; it neither activates nor revokes any Feature account.

13.3.7 Address Lookup Table Program

Lookup-table instruction data is FIXINT-LE: u32 discriminants 0 Create (recent_slot:u64,bump:u8), 1 Freeze, 2 Extend (count:u64 then that many 32-byte addresses), 3 Deactivate, and 4 Close. State is u32 tag 0 Uninitialized or tag 1 LookupTable followed by the metadata below.

Normative table. Lookup-table account bytes.

RangeField
[0,4)state tag 1
[4,12)deactivation slot; u64::MAX means active
[12,20)last-extended slot
[20,21)start index of the addresses appended in that slot
[21,22)authority option tag
[22,54)authority when present, ignored/zero when absent
[54,56)zero u16 padding
[56,56+32*N)N, 0..=256, consecutive addresses

Normative table. Lookup-table lifecycle.

InstructionOrdered accounts and exact transition
Createtable W; authority; payer W,S; System. recent_slot must occur in SlotHashes and table must equal PDA(authority||LE(recent_slot,8), bump). If already ALT-owned, succeed idempotently. Otherwise fund to max(1,rent_minimum(56)), allocate 56, assign ALT owner, and store active metadata with authority.
Extendtable W; authority S; optional payer W,S and System when more rent is required. Require owner, present matching authority, active state, at least one new key, and total <=256. On the first extension in a slot, store the old N as start index; append keys and fund new rent minimum.
Freezetable W; authority S. Require present matching authority, active state, and N>0; set authority absent. A frozen table is immutable and cannot close.
Deactivatetable W; authority S. Require present matching authority and active state; set deactivation slot to current Clock slot.
Closetable W; authority S; recipient W. Require distinct table/recipient, present matching authority, and deactivation slot absent from SlotHashes and unequal current slot. Transfer all lamports, set table data length and lamports to zero.

An active/deactivating table remains usable while its deactivation slot is the current slot or occurs in SlotHashes. It becomes deactivated when that slot is absent. For lookup in current slot s, if s == last_extended_slot, only indices below last_extended_slot_start_index are visible; otherwise all N are visible. Invalid state/owner/index map to the four exact transaction errors in Section 7.8 during message loading. Program-instruction failures use InvalidAccountOwner, MissingRequiredSignature, Immutable, IncorrectAuthority, AccountAlreadyInitialized, InvalidArgument, or InvalidInstructionData in the predicate order shown above.

13.3.8 Native Loader and BPF loader execution

The Native Loader address is a reserved dispatch owner, not a general-purpose instruction program. Directly invoking it returns UnsupportedProgramId. A Native-Loader-owned program account dispatches only when its key is in the bank’s builtin registry; an unregistered key produces TransactionError::InvalidProgramForExecution during loading. The program’s own instruction accounts exclude the loader-owner account.

The deprecated and current BPF loaders may execute already-deployed program accounts using ABI v0 and v1 respectively, but direct management instructions return UnsupportedProgramId after the fixed loader charge in Section 12.8.8. The Upgradeable Loader alone accepts the management state machine in Section 13.3.2. CPI directly to any loader, and CPI to an Upgradeable-Loader Upgrade, SetAuthority, SetAuthorityChecked, or Close instruction, returns UnsupportedProgramId before the nested invocation begins.

13.3.9 ZK ElGamal Proof Program

Instruction byte zero is the u8 discriminant below. For a verify instruction, the remaining bytes are either the exact proof-data POD value or exactly four bytes proof_offset:u32; the latter reads that POD at the offset in readonly account 0. A verification with no context needs no more accounts. To store the verified statement, supply a writable context account followed by the context authority; when proof bytes came from account 0 those are accounts 1 and 2.

Normative table. ZK ElGamal proof instructions and complete POD envelopes.

Disc.InstructionContext POD, in byte orderContext bytesProof bytesInline proof-data bytesStored context-state bytesCU
0CloseContextStatenot applicable——exactly 1existing account length3,300
1VerifyZeroCiphertextpubkey[32], ciphertext[64]96961931296,000
2VerifyCiphertextCiphertextEqualityfirst_pubkey[32], second_pubkey[32], first_ciphertext[64], second_ciphertext[64]1922244172258,000
3VerifyCiphertextCommitmentEqualitypubkey[32], ciphertext[64], commitment[32]1281923211616,400
4VerifyPubkeyValiditypubkey[32]326497652,600
5VerifyPercentageWithCappercentage_commitment[32], delta_commitment[32], claimed_commitment[32], max_value_le[8]1042563611376,500
6VerifyBatchedRangeProofU64commitments[8][32], bit_lengths[8]264672937297111,000
7VerifyBatchedRangeProofU128same2647361,001297200,000
8VerifyBatchedRangeProofU256same2648001,065297368,000
9VerifyGroupedCiphertext2HandlesValiditypubkey0[32], pubkey1[32], grouped_ciphertext[96]1601603211936,400
10VerifyBatchedGroupedCiphertext2HandlesValiditypubkey0[32], pubkey1[32], grouped_ciphertext_lo[96], grouped_ciphertext_hi[96]25616041728913,000
11VerifyGroupedCiphertext3HandlesValiditypubkey0[32], pubkey1[32], pubkey2[32], grouped_ciphertext[128]2241924172578,100
12VerifyBatchedGroupedCiphertext3HandlesValiditypubkey0[32], pubkey1[32], pubkey2[32], grouped_ciphertext_lo[128], grouped_ciphertext_hi[128]35219254538516,400

Every inline proof envelope is exactly discriminant:u8 || context_POD || proof_POD, with no padding or trailing bytes. The five-byte account form is exactly discriminant:u8 || proof_offset:u32_le; its readonly proof account must contain context_POD || proof_POD at [proof_offset, proof_offset + context_bytes + proof_bytes). A stored context state is exactly authority[32] || proof_type:u8 || context_POD, which yields the lengths in the table.

All keys, ciphertext components, commitments, and proof points are canonical compressed Ristretto encodings; each proof scalar is canonical modulo the Ristretto scalar order. An ElGamal ciphertext is commitment[32] || decryption_handle[32]; a two- or three-handle grouped ciphertext is one commitment followed by two or three 32-byte handles. The proof relations are, respectively: encryption of zero; equality of the two ciphertext plaintexts; equality of a ciphertext plaintext and a Pedersen commitment opening; knowledge of the secret key corresponding to the supplied public key; either percentage == max_value or delta == claimed; membership of every committed value in its declared unsigned bit range; and validity of every grouped decryption handle for one shared commitment. Batched grouped proofs check both the low and high grouped ciphertext under the same supplied keys.

For a batched range context, selected commitments are the prefix before the first all-zero 32-byte commitment, so there are at most eight. The equally long prefix of bit_lengths supplies the selected unsigned u8 widths; its sum must be exactly 64, 128, or 256 for discriminants 6, 7, or 8. The remaining commitment and length slots are not selected, but their raw bytes remain bound into the transcript and are not required to be zero. Verification uses the named proof’s fixed Fiat–Shamir transcript and rejects a noncanonical scalar, invalid compressed point, identity commitment where prohibited, malformed range vector, or false relation. Any POD decode or verification failure is InvalidInstructionData after the row’s full CU is charged.

A context account is Proof-Program-owned and exactly authority[32] || proof_type:u8 || context_POD. It must contain proof type 0 Uninitialized and have exactly the resulting length; otherwise return AccountAlreadyInitialized or InvalidAccountData. Success replaces the entire bytes. Close takes context W, distinct destination W, authority S; it validates owner, initialized type, and exact authority in that order, transfers all lamports, truncates data, and assigns System ownership. Failures are respectively MissingRequiredSignature, InvalidInstructionData, InvalidAccountOwner, UninitializedAccount, or InvalidAccountOwner for an authority mismatch.

13.3.10 Core-BPF migration and upgrade side effects

At an activating bank, a protocol-owned migration first authenticates the source Upgradeable-Loader Buffer state, its configured address and authority, and any configured verified-build hash; authenticates the target’s expected builtin/stateless/Core-BPF form; derives the target ProgramData PDA; constructs the Program and ProgramData states from Section 13.3.2; and validates/deploys the source ELF under the bank’s Chapter 12 environment. No bank account is changed if any check fails.

On success it replaces the target Program/ProgramData accounts, logically deletes the source buffer by setting its lamports to zero, removes any native builtin registration, and adjusts capitalization by the following checked expression:

Normative algorithm.

new_program_lamports + new_programdata_lamports
    - old_target_lamports - old_programdata_lamports - buffer_lamports

A Core-BPF upgrade retains the Program account and authority, replaces only ProgramData, clears the buffer, updates the deployment slot, and adjusts the same capitalization components. For migration or upgrade slot d, the new bytes have effective slot d+1; ResolveProgram does not dispatch the old or new form during d. These are bank-creation transitions, not transaction instructions, so a configured authentication/ELF/hash failure is bank-fatal rather than a transaction error.

For a Core-BPF row, execution follows Chapter 12 using deployed account bytes in the bank. The public program identifier and migration feature do not substitute for the Program/ProgramData relationship or executable bytes.

13.4 Sysvar account identities

Sysvar accounts use owner Sysvar1111111111111111111111111111111111111 unless a specific protocol exception is specified. Known addresses are:

Normative table.

SysvarAddressLifetime/update role
ClockSysvarC1ock11111111111111111111111111111111updated for each child bank before transactions
EpochRewardsSysvarEpochRewards1111111111111111111111111initialized/updated during partitioned rewards
EpochScheduleSysvarEpochSchedu1e111111111111111111111111genesis-derived schedule
InstructionsSysvar1nstructions1111111111111111111111111transaction-local synthesized account
LastRestartSlotSysvarLastRestartS1ot1111111111111111111111updated before transactions when required
RecentBlockhashesSysvarRecentB1ockHashes11111111111111111111legacy queue view updated at block boundary
RentSysvarRent111111111111111111111111111111111rent-exempt minimum parameters
SlotHashesSysvarS1otHashes111111111111111111111111111parent-slot/hash ancestry updated before transactions
SlotHistorySysvarS1otHistory11111111111111111111111111updated during freeze
StakeHistorySysvarStakeHistory1111111111111111111111111updated at epoch transition

Sysvar account data uses the Chapter 5 fixed-width and sequence rules. There is no host-structure padding. The exact data grammars are:

Normative table.

SysvarExact serialized data
Clockslot:u64, epoch_start_timestamp:i64, epoch:u64, leader_schedule_epoch:u64, unix_timestamp:i64; exactly 40 bytes
EpochScheduleslots_per_epoch:u64, leader_schedule_slot_offset:u64, warmup:u8 Boolean, first_normal_epoch:u64, first_normal_slot:u64; exactly 33 bytes
EpochRewardsdistribution_starting_block_height:u64, num_partitions:u64, parent_blockhash[32], total_points:u128, total_rewards:u64, distributed_rewards:u64, active:u8 Boolean; exactly 81 bytes
LastRestartSlotlast_restart_slot:u64; exactly 8 bytes
RecentBlockhashesu64 count, at most 150 records of blockhash[32], lamports_per_signature:u64 in newest-to-oldest order
Rentlamports_per_byte:u64, eight threshold bytes, burn_percent:u8; exactly 17 bytes
SlotHashesu64 count, at most 512 records of slot:u64, hash[32] in descending slot order
StakeHistoryu64 count, at most 512 records of epoch:u64, effective:u64, activating:u64, deactivating:u64 in descending epoch order
SlotHistoryoption tag 1, block count LE(16384,8), 16,384 u64 little-endian blocks, bit length LE(1048576,8), then next_slot:u64; exactly 131,097 bytes

SlotHistory bit i is the least-significant-bit-first bit (i mod 64) in block floor(i/64). A slot s in [next_slot.saturating_sub(1048576), next_slot) is present exactly when bit s mod 1048576 is set; a slot at or above next_slot is future and an older slot is too old. Mainnet EpochSchedule bytes are (432000, 432000, false, 0, 0). Mainnet Rent bytes are LE(5080,8) || 00 00 00 00 00 00 f0 3f || 32; the final byte is decimal 50 (0x32) and the middle bytes are the little-endian IEEE-754 representation of 1.0 retained for compatibility.

[SOL-PRG-0014] A persistent sysvar account MUST have its canonical address, Sysvar owner, a false executable flag, a rent-exempt balance, and the exact serialized data for the child bank.

[SOL-PRG-0015] A sysvar update MUST replace the account data using the canonical serializer and MUST adjust capitalization for any protocol-funded rent-exempt balance change.

[SOL-PRG-0016] The sysvar cache supplied to builtins, SBF syscalls, and loaders MUST reflect the same values as the child bank’s canonical sysvar state at the invocation point.

13.5 Sysvar update phases

[SOL-PRG-0017] Before transaction execution, SlotHashes MUST incorporate the parent slot and parent bank hash according to its bounded history rule, and Clock MUST identify the child slot and epoch with the stake-weighted timestamp result below.

[SOL-PRG-0018] StakeHistory MUST be updated exactly when the child crosses an epoch boundary, and LastRestartSlot MUST reflect the greatest qualifying hard-fork or restart slot visible to the child.

[SOL-PRG-0019] EpochRewards MUST be initialized and updated in lockstep with the partitioned epoch-reward state transition, including its active/inactive status and distributed amount.

[SOL-PRG-0020] At the slot boundary, RecentBlockhashes MUST reflect the active recent-blockhash queue after terminal hash registration; during freeze, SlotHistory MUST record the child slot before bank hashing.

The bounded histories, timestamp estimator, reward fields, and deprecated-sysvar availability are selected by the bank feature state and the relevant protocol-program transition.

For child slot s, retain each vote account’s most recent timestamp only when its reported slot r <= s and s-r <= 432000. Advance that timestamp by the PoH duration from r+1 through s, group equal advanced estimates, and sum the epoch stake delegated to each group with saturating u128 arithmetic. The estimate is the smallest timestamp whose cumulative stake in ascending timestamp order is strictly greater than half the total included stake. If no stake is included, retain the parent’s timestamp. Relative to the epoch-start PoH estimate, clamp an estimate that is too far behind to 25% behind and one that is too far ahead to 150% ahead; finally, do not permit the child Unix timestamp to precede its parent’s. On an epoch boundary, the resulting value also becomes epoch_start_timestamp.

13.6 Instructions sysvar

The instructions sysvar is transaction-scoped. It contains a serialization of the top-level message instructions and a mutable current top-level instruction index used during execution; it is not loaded from the parent account database.

Normative table.

RegionExact encoding
headerinstruction count N:u16, followed by N little-endian u16 offsets from byte zero to each instruction record
instruction recordaccount count A:u16; A account-meta records; program ID [32]; data length D:u16; D data bytes
account-meta recordflags u8 then public key [32]; flag bit 0 is signer, bit 1 writable, and bits 2–7 are zero
final fieldcurrent top-level instruction index u16 in the last two data bytes

Every offset points at the corresponding record’s account count. Records are stored in top-level message order and account metas retain instruction order. All counts, offsets, and total size must fit u16.

[SOL-PRG-0021] When the instructions-sysvar key is present in a transaction, account loading MUST synthesize its data from that transaction’s complete top-level instruction list and MUST NOT use any persistent account value at the same address.

[SOL-PRG-0022] Immediately before each top-level instruction is invoked, the runtime MUST set the synthesized instructions sysvar’s current index to that instruction’s zero-based message index.

[SOL-PRG-0023] CPI MUST NOT append callee instructions to the synthesized top-level instruction list or change the current index away from the enclosing top-level instruction.

13.7 Reserved protocol accounts and incineration

The epoch-E set has no pending or conditionally writable entries. Every row is an always-readonly base entry; no feature adds or removes it in E.

Normative table. Complete reserved-account set.

Reserved surfaceExact addressFeature transitionWritability
Address Lookup Table ProgramAddressLookupTab1e1111111111111111111111111basealways readonly
BPF LoaderBPFLoader2111111111111111111111111111111111basealways readonly
deprecated BPF LoaderBPFLoader1111111111111111111111111111111111basealways readonly
Upgradeable BPF LoaderBPFLoaderUpgradeab1e11111111111111111111111basealways readonly
Compute Budget ProgramComputeBudget111111111111111111111111111111basealways readonly
Config ProgramConfig1111111111111111111111111111111111111basealways readonly
Ed25519 precompileEd25519SigVerify111111111111111111111111111basealways readonly
Feature ProgramFeature111111111111111111111111111111111111basealways readonly
abandoned Loader-v4 addressLoaderV411111111111111111111111111111111111base; retained permanentlyalways readonly
secp256k1 precompileKeccakSecp256k11111111111111111111111111111basealways readonly
secp256r1 precompileSecp256r1SigVerify1111111111111111111111111basealways readonly
Stake ConfigStakeConfig11111111111111111111111111111111basealways readonly
Stake ProgramStake11111111111111111111111111111111111111basealways readonly
System Program11111111111111111111111111111111basealways readonly
Vote ProgramVote111111111111111111111111111111111111111basealways readonly
ZK ElGamal Proof ProgramZkE1Gama1Proof11111111111111111111111111111basealways readonly
ZK Token Proof ProgramZkTokenProof1111111111111111111111111111111basealways readonly
Clock sysvarSysvarC1ock11111111111111111111111111111111basealways readonly
EpochRewards sysvarSysvarEpochRewards1111111111111111111111111basealways readonly
EpochSchedule sysvarSysvarEpochSchedu1e111111111111111111111111basealways readonly
Fees sysvarSysvarFees111111111111111111111111111111111base, deprecatedalways readonly
Instructions sysvarSysvar1nstructions1111111111111111111111111basealways readonly
LastRestartSlot sysvarSysvarLastRestartS1ot1111111111111111111111basealways readonly
RecentBlockhashes sysvarSysvarRecentB1ockHashes11111111111111111111base, deprecatedalways readonly
Rent sysvarSysvarRent111111111111111111111111111111111basealways readonly
Rewards sysvarSysvarRewards111111111111111111111111111111base, deprecatedalways readonly
SlotHashes sysvarSysvarS1otHashes111111111111111111111111111basealways readonly
SlotHistory sysvarSysvarS1otHistory11111111111111111111111111basealways readonly
StakeHistory sysvarSysvarStakeHistory1111111111111111111111111basealways readonly
Native LoaderNativeLoader1111111111111111111111111111111basealways readonly
Sysvar ownerSysvar1111111111111111111111111111111111111basealways readonly

[SOL-PRG-0024] Immediately after address resolution, sanitation MUST demote every row above to readonly. Demotion changes only the effective writable bitmap: the signed message bytes and its three header counts remain unchanged. A reserved address requested writable still contributes one requested-writable-account cost in Section 10.4.1, but it is not included in effective-writable account cost attribution and does not change fee calculation.

[SOL-PRG-0025] At freeze, a present incinerator account MUST be replaced with the default account and its lamports MUST be removed from capitalization before the accounts commitment and bank hash are finalized.

[SOL-PRG-0026] Lamports sent to the incinerator after its processing point MUST NOT be retroactively burned in an earlier bank.

[SOL-PRG-0027] A transaction that attempts a state change through a demoted reserved account MUST fail at instruction validation with the applicable ReadonlyLamportChange or ReadonlyDataModified; the message is not rejected merely for having requested that reserved key writable.

[SOL-PRG-0028] A Core-BPF program MUST execute the authenticated deployed bytes selected by ResolveProgram; prose in this chapter is not authority to substitute a native implementation. The Stake, Config, Feature, and Address Lookup Table state decoders and cross-program rules in Sections 13.3.4 through 13.3.10 remain normative where the rest of the protocol consumes those accounts. Any mismatch between the prose and authenticated execution is a defect in this specification, not an alternative state transition.

13.8 Core-BPF programs

The following subsections are an index, not duplicate definitions. Each program’s single authoritative state machine is the referenced Section 13.3 subsection; Chapter 12 supplies its SBF execution semantics and the bank’s authenticated Program/ProgramData bytes supply its code.

13.8.1 Stake Program v5

The complete active instruction set, 200-byte state, transitions, and custom error domain are defined once in Section 13.3.4.

13.8.2 Config Program

The key-prefix and opaque-suffix state, sole update transition, and exact error precedence are defined once in Section 13.3.5.

13.8.3 Feature Program

Pending/active state, activation, revocation, and migration behavior are defined once in Sections 13.3.6 and 17.1–17.4.

13.8.4 Address Lookup Table Program

Instruction bytes, 56-byte metadata, address visibility, and the complete lifecycle are defined once in Sections 13.3.7 and 7.4.

13.9 Deployment dependence

Core-BPF executable bytes and upgrade-authority state are ordinary bank state. An implementation restoring or replaying a bank authenticates those accounts through the state commitment and executes the version visible at that bank, as required by SOL-PRG-0002; this document does not embed mutable deployed bytes.