@@ -105,7 +105,7 @@ static const fd_cluster_version_t FD_RUNTIME_CLUSTER_VERSION = {
105105/* The bpf loader's serialization footprint is bounded in the worst case
106106 by 64 unique writable accounts which are each 10MiB in size (bounded
107107 by the amount of transaction accounts). We can also have up to
108- FD_INSTR_ACCT_MAX (256 ) referenced accounts in an instruction.
108+ FD_BPF_INSTR_ACCT_MAX (255 ) referenced accounts in an instruction.
109109
110110 - 8 bytes for the account count
111111 For each account:
@@ -147,8 +147,67 @@ static const fd_cluster_version_t FD_RUNTIME_CLUSTER_VERSION = {
147147#define FD_ACCOUNT_REC_ALIGN (8UL)
148148/* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/ebpf.rs#L37-L38 */
149149#define FD_RUNTIME_EBPF_HOST_ALIGN (16UL)
150- #define FD_INSTR_ACCT_MAX (256)
151150
151+ /* FD_INSTR_ACCT_MAX is the maximum number of accounts that can
152+ be referenced by a single instruction.
153+
154+ This is different from FD_BPF_INSTR_ACCT_MAX, which is enforced by the
155+ BPF serializer. It is possible to pass in more than FD_BPF_INSTR_ACCT_MAX
156+ instruction accounts in a transaction (for example mainnet transaction)
157+ 3eDdfZE6HswPxFKrtnQPsEmTkyL1iP57gRPEXwaqNGAqF1paGXCYYMwh7z4uQDUMgFor742sikVSQZW1gFRDhPNh).
158+
159+ A transaction like this will be loaded and sanitized, but will fail in the
160+ bpf serialization stage. It is also possible to invoke a native program with
161+ more than FD_BPF_INSTR_ACCT_MAX instruction accounts that will execute successfully.
162+
163+ Therefore we need to derive a bound from a worst-case transaction: one that
164+ has the maximum possible number of instruction accounts at the expense of
165+ everything else. This is a V0 transaction with only two account addresses
166+ (a program and a fee payer), a single signature, a single instruction with
167+ empty data, as many instruction accounts as possible, and no address table
168+ lookups.
169+
170+ Therefore, the maximum number of instruction accounts is:
171+ (MTU - fixed overhead) / (size of instruction account)
172+ = (MTU
173+ - signature count (1 byte, value=1)
174+ - signature (64 bytes)
175+ - version (1 byte)
176+ - signature count in header (1 byte)
177+ - readonly signed count (1 byte)
178+ - readonly unsigned count (1 byte)
179+ - account count (1 byte, compact-u16 value=2)
180+ - 2 account addresses (32 + 32 bytes)
181+ - recent blockhash (32 bytes)
182+ - instruction count (1 byte, compact-u16 value=1)
183+ - program id index (1 byte)
184+ - instruction account count (2 bytes)
185+ - data len (1 byte, value=0)
186+ - address lookup table count (1 byte, value=0) )
187+ / (1 byte per account index)
188+ = 1232 - 1 - 64 - 1 - 1 - 1 - 1 - 1 - 64 - 32 - 1 - 1 - 2 - 1 - 1
189+ = 1060
190+
191+ TODO: SIMD-406 (https://github.com/solana-foundation/solana-improvement-documents/pull/406)
192+ limits the number of instruction accounts to 255 in transaction sanitization.
193+
194+ Once the corresponding feature gate has been activated, we can reduce
195+ FD_INSTR_ACCT_MAX to 255. We cannot reduce this before as this would cause
196+ the result of the get_processed_sibling_instruction syscall to diverge from
197+ Agave. */
198+ #define FD_INSTR_ACCT_MAX (1060UL)
199+
200+ /* FD_BPF_INSTR_ACCT_MAX is the maximum number of accounts that
201+ an instruction that goes through the bpf loader serializer can reference.
202+
203+ The BPF loader has a lower limit for the number of instruction accounts
204+ than is enforced in transaction sanitization.
205+
206+ TODO: remove this limit once SIMD-406 is activated, as we can then use the
207+ same limit everywhere.
208+
209+ https://github.com/anza-xyz/agave/blob/v3.1.4/transaction-context/src/lib.rs#L30-L32 */
210+ #define FD_BPF_INSTR_ACCT_MAX (255UL)
152211
153212#define FD_BPF_LOADER_UNIQUE_ACCOUNT_FOOTPRINT (direct_mapping ) \
154213 (1UL /* dup byte */ + \
@@ -168,7 +227,7 @@ static const fd_cluster_version_t FD_RUNTIME_CLUSTER_VERSION = {
168227#define FD_BPF_LOADER_INPUT_REGION_FOOTPRINT (account_lock_limit , direct_mapping ) \
169228 (FD_ULONG_ALIGN_UP( (sizeof(ulong) /* acct_cnt */ + \
170229 account_lock_limit * FD_BPF_LOADER_UNIQUE_ACCOUNT_FOOTPRINT (direct_mapping ) + \
171- (FD_INSTR_ACCT_MAX - account_lock_limit )* FD_BPF_LOADER_DUPLICATE_ACCOUNT_FOOTPRINT + \
230+ (FD_BPF_INSTR_ACCT_MAX - account_lock_limit )* FD_BPF_LOADER_DUPLICATE_ACCOUNT_FOOTPRINT + \
172231 sizeof (ulong ) /* instr data len */ + \
173232 FD_TXN_MTU /* No instr data */ + \
174233 sizeof (fd_pubkey_t )), /* program id */ \
0 commit comments