Samenvatting
CPI's doorlopen 11 runtime-stappen, waaronder privilege-controle, account-vertaling en data-synchronisatie. Maximale aanroepdiepte: 5 (9 met SIMD-0268). Privilege-regels voorkomen dat de aangeroepene verder escaleert dan wat de aanroeper heeft toegestaan.
Privilege-regels
CPI's breiden de account-privileges van de aanroeper uit naar de aangeroepene
met strikte handhaving. De runtime controleert deze regels in
prepare_next_instruction:
| Scenario | Toegestaan? | Handhavingspunt | Fout |
|---|---|---|---|
| Aanroeper geeft account door als schrijfbaar, aangeroepene markeert als schrijfbaar | Ja | -- | -- |
| Aanroeper geeft account door als alleen-lezen, aangeroepene markeert als schrijfbaar | Nee | prepare_next_instruction | PrivilegeEscalation |
| Aanroeper geeft account door als schrijfbaar, aangeroepene markeert als alleen-lezen | Ja | -- | -- |
| Aanroeper geeft account door als ondertekenaar, aangeroepene markeert als ondertekenaar | Ja | -- | -- |
| Aanroeper geeft account door als niet-ondertekenaar, aangeroepene markeert als ondertekenaar, account is een PDA afgeleid van seeds van aanroeper | Ja | prepare_next_instruction | -- |
| Aanroeper geeft account door als niet-ondertekenaar, aangeroepene markeert als ondertekenaar, account is GEEN PDA van aanroeper | Nee | prepare_next_instruction | PrivilegeEscalation |
| Aanroeper geeft account door als ondertekenaar, aangeroepene markeert als niet-ondertekenaar | Ja | -- | -- |
| Programma A roept zichzelf direct aan (A -> A) | Ja | push() | -- |
| Programma A roept B aan, dat A aanroept (indirecte reentrancy) | Nee | push() | ReentrancyNotAllowed |
| CPI naar native loader, bpf_loader, bpf_loader_deprecated of precompile | Nee | check_authorized_program | ProgramNotSupported |
| Account niet gevonden in transactie | Nee | prepare_next_instruction | MissingAccount |
De privilege-regels kunnen als volgt worden samengevat:
- Schrijfbaar privilege kan niet escaleren. Als de aanroeper een account als alleen-lezen markeert, kan de aangeroepene het niet als schrijfbaar markeren.
- Ondertekenaar privilege vereist autorisatie. Een account kan alleen een
ondertekenaar zijn in de aangeroepene als (a) het al een ondertekenaar was in
de aanroeper, OF (b) het een PDA is afgeleid van de seeds van het aanroepende
programma via
invoke_signed. - Privilege-vermindering is altijd toegestaan. De aangeroepene mag minder privileges gebruiken dan de aanroeper heeft verleend.
CPI uitvoeringsflow
Een CPI passeert verschillende runtime-lagen. Deze sectie documenteert de volledige pipeline vanaf de programma SDK-aanroep door de syscall-grens naar de runtime en terug. Elke stap verwijst naar het bronbestand dat het implementeert.
De maximale hoogte van de programma-instructie-aanroep wordt de
max_instruction_stack_depth
genoemd en is ingesteld op de
MAX_INSTRUCTION_STACK_DEPTH
constante van 5. Met MAX_INSTRUCTION_STACK_DEPTH_SIMD_0268 actief, neemt dit toe tot 9.
Stack-hoogte 1 is de initiële transactie-instructie. Elke CPI verhoogt de hoogte met 1. Een maximum van 5 betekent dat een programma CPI's tot 4 niveaus diep kan maken (8 niveaus diep met SIMD-0268).
Stap 1: Programma roept invoke of invoke_signed aan
Het programma roept
invoke
of
invoke_signed
aan. invoke is een dunne wrapper die invoke_signed aanroept met een
lege ondertekenaar seeds-array. De SDK-functie serialiseert de
Instruction, AccountInfo slice, en ondertekenaar seeds naar
VM-geheugen, en triggert vervolgens de syscall.
Stap 2: Syscall-entry
De SBF VM dispatcht naar de
sol_invoke_signed_rust
syscall-handler, die aanroept naar het gedeelde entry point:
cpi_common.
Stap 3: Invocatiekosten verbruiken
De eerste actie binnen cpi_common is om
de vaste invocatiekosten in rekening te brengen
van de gedeelde compute meter:
invoke_units
= 1.000 CU's (of 946 CU's met SIMD-0339).
Stap 4: Instructie vertalen vanuit VM-geheugen
De syscall handler vertaalt de instructie vanuit de adresruimte van de VM van
het programma naar host-side Rust types via
translate_instruction_rust,
die een StableInstruction struct leest, de datalengte valideert tegen
MAX_INSTRUCTION_DATA_LEN
(10.240 bytes), en vervolgens de
kosten voor dataserialisatie
in rekening brengt.
Stap 5: Signer seeds vertalen en PDA's afleiden
De handler roept
translate_signers_rust
aan. Voor elke set signer seeds doet de runtime het volgende:
- Controleert het aantal signer seed sets tegen
MAX_SIGNERS(16). - Controleert de lengte van elke seed set tegen
MAX_SEEDS(16 seeds per set). - Roept
Pubkey::create_program_addressaan met de seeds en de programma-ID van de aanroeper. Als de seeds geen geldige PDA opleveren, mislukt de CPI metBadSeeds. - Verzamelt de resulterende PDA pubkeys in een
signersvec.
Deze afgeleide PDA's worden behandeld als geldige signers voor de callee instructie.
Stap 6: Geautoriseerd programma controleren
Voordat wordt doorgegaan, roept de runtime
check_authorized_program
aan om te verifiëren dat het doelprogramma is toegestaan voor CPI. De volgende
programma's zijn geblokkeerd:
- De native loader
bpf_loaderenbpf_loader_deprecatedbpf_loader_upgradeable(behalve specifieke beheerinstructies:upgrade,set_authority,set_authority_checked(feature-gated),extend_program_checked(feature-gated),close)- Precompile programma's (ed25519, secp256k1, etc.)
Overtreding retourneert
ProgramNotSupported.
Stap 7: Privilege verificatie (prepare_next_instruction)
De runtime roept
prepare_next_instruction
aan die de InstructionAccount lijst van de callee opbouwt en privilege
regels afdwingt. Zie Privilege regels hieronder voor de
volledige beslissingstabel.
Stap 8: Vertaal accountinformatie
De handler roept translate_accounts aan die:
- Valideert het aantal accountinformatie
tegen
MAX_CPI_ACCOUNT_INFOS(128, of 255 met SIMD-0339). - Brengt kosten voor accountinformatievertaling in rekening
(alleen SIMD-0339):
(num_account_infos * 80) / 250CU's. - Voor elk niet-uitvoerbaar, niet-duplicaat account, bouwt een
CallerAccountdoor pointers te vertalen van VM-geheugen naar hostgeheugen. Dit omvat het in rekening brengen van dataserialisatiekosten per account:account_data_len / cpi_bytes_per_unitCU's.
Stap 9: Pre-CPI accountsynchronisatie (aanroeper naar aangeroepene)
Voordat de aangeroepene wordt uitgevoerd, synchroniseert de runtime de
accountwijzigingen van de aanroeper zodat de aangeroepene deze kan zien. De
functie
update_callee_account
wordt aangeroepen voor elk vertaald account, waarbij lamports, data en eigenaar
worden gekopieerd. Zie
Accountdatasynchronisatie
voor de gedetailleerde veldtoewijzing.
Stap 10: Push instructiecontext, voer aangeroepene uit en pop
De runtime roept
process_instruction
aan, die:
- Roept
push()aan om een nieuw frame toe te voegen aan de instructiestack.push()handhaaft de reentrancy-regel: een programma mag zichzelf alleen aanroepen als het de directe aanroeper is (d.w.z. programma A kan A aanroepen, maar A kan niet B aanroepen die A aanroept). Overtreding retourneertReentrancyNotAllowed. - Roept
process_executable_chainaan die het toegangspunt van het aangeroepen programma oplost en aanroept. De aangeroepene draait met dezelfde gedeelde compute meter. Al het CU-verbruik door de aangeroepene vermindert het resterende budget van de aanroeper. - Roept
pop()aan om het frame van de aangeroepene te verwijderen en te verifiëren dat lamport-saldi ongewijzigd zijn (UnbalancedInstructionindien niet).
Stap 11: Post-CPI accountsynchronisatie (aangeroepene naar aanroeper)
Nadat process_instruction terugkeert (wat de pop omvat), synchroniseert de
runtime wijzigingen terug naar de aanroeper via
update_caller_account
voor elk schrijfbaar account. Daarnaast werkt
update_caller_account_region
VM-geheugenregiotoewijzingen bij voor accounts waarvan de dataregio's zijn
gewijzigd. Zie
Accountdatasynchronisatie
voor de gedetailleerde veldtoewijzing.
De CPI syscall retourneert 0 (succes) naar het aanroepende programma.
Is this page helpful?