FAQ
Stel je vragen op StackExchange.
Berkeley Packet Filter (BPF)
Solana onchain programma's worden gecompileerd via de LLVM compiler infrastructure naar een Executable and Linkable Format (ELF) dat een variant bevat van de Berkeley Packet Filter (BPF) bytecode.
Omdat Solana gebruikmaakt van de LLVM compiler infrastructure, kan een programma worden geschreven in elke programmeertaal die de LLVM's BPF backend kan aanspreken.
BPF biedt een efficiënte instructieset die kan worden uitgevoerd in een geïnterpreteerde virtuele machine of als efficiënte just-in-time gecompileerde native instructies.
Geheugenindeling
De virtuele adresgeheugenindeling die door Solana SBF-programma's wordt gebruikt, is vast en als volgt ingedeeld
- Programmacode begint op 0x100000000
- Stackgegevens beginnen op 0x200000000
- Heapgegevens beginnen op 0x300000000
- Programma-invoerparameters beginnen op 0x400000000
De bovenstaande virtuele adressen zijn startadressen, maar programma's krijgen
toegang tot een subset van de geheugenindeling. Het programma zal crashen als
het probeert te lezen of te schrijven naar een virtueel adres waartoe het geen
toegang heeft gekregen, en een AccessViolation
fout zal worden teruggegeven
die het adres en de grootte van de poging tot schending bevat.
InvalidAccountData
Deze programmafout kan om veel redenen optreden. Meestal wordt het veroorzaakt door het doorgeven van een account aan het programma dat het programma niet verwacht, ofwel op de verkeerde positie in de instructie of een account dat niet compatibel is met de instructie die wordt uitgevoerd.
Een implementatie van een programma kan deze fout ook veroorzaken bij het uitvoeren van een cross-programma instructie en het vergeten om het account te verstrekken voor het programma dat je aanroept.
InvalidInstructionData
Deze programma-fout kan optreden tijdens het deserialiseren van de instructie.
Controleer of de doorgegeven structuur exact overeenkomt met de instructie. Er
kan sprake zijn van opvulling tussen velden. Als het programma de Rust Pack
trait implementeert, probeer dan het verpakken en uitpakken van het
instructietype T
om de exacte codering te bepalen die het programma verwacht.
MissingRequiredSignature
Sommige instructies vereisen dat het account een ondertekenaar is; deze fout wordt teruggegeven als een account naar verwachting ondertekend moet zijn, maar dat niet is.
Een implementatie van een programma kan deze fout ook veroorzaken bij het
uitvoeren van een cross-program invocation die een ondertekend
programma-adres vereist, maar waarbij de doorgegeven ondertekenaar seeds aan
invoke_signed
niet overeenkomen met de ondertekenaar seeds die zijn gebruikt
om het programma-adres te maken
create_program_address
.
Stack
SBF gebruikt stack frames in plaats van een variabele stack pointer. Elk stack frame is 4KB groot.
Als een programma de grootte van het stack frame overschrijdt, zal de compiler de overschrijding als een waarschuwing melden.
Bijvoorbeeld:
Error: Function _ZN16curve25519_dalek7edwards21EdwardsBasepointTable6create17h178b3d2411f7f082E Stack offset of -30728 exceeded max offset of -4096 by 26632 bytes, please minimize large stack variables
Het bericht identificeert welk symbool zijn stack frame overschrijdt, maar de naam kan vervormd zijn.
Om een Rust-symbool te ontwarren, gebruik rustfilt.
De bovenstaande waarschuwing kwam van een Rust-programma, dus de ontwarrede symboolnaam is:
rustfilt _ZN16curve25519_dalek7edwards21EdwardsBasepointTable6create17h178b3d2411f7f082Ecurve25519_dalek::edwards::EdwardsBasepointTable::create
De reden dat er een waarschuwing wordt gemeld in plaats van een fout is omdat
sommige afhankelijke crates functionaliteit kunnen bevatten die de stack
frame-beperkingen schendt, zelfs als het programma die functionaliteit niet
gebruikt. Als het programma de stack-grootte tijdens runtime overschrijdt, wordt
een AccessViolation
fout gemeld.
SBF stack frames bezetten een virtueel adresbereik dat begint bij 0x200000000
.
Heap-grootte
Programma's hebben toegang tot een runtime heap via de Rust alloc
API's. Om
snelle toewijzingen te faciliteren, wordt een eenvoudige 32KB bump heap
gebruikt. De heap ondersteunt geen free
of realloc
.
Intern hebben programma's toegang tot het 32KB geheugengebied dat begint op virtueel adres 0x300000000 en kunnen ze een aangepaste heap implementeren op basis van de specifieke behoeften van het programma.
Rust-programma's implementeren de heap direct door een aangepaste
global_allocator
te definiëren
Loaders
Programma's worden geïmplementeerd en uitgevoerd door runtime loaders. Momenteel zijn er twee ondersteunde loaders: BPF Loader en BPF loader deprecated
Loaders kunnen verschillende application binary interfaces ondersteunen, dus
ontwikkelaars moeten hun programma's schrijven voor en implementeren naar
dezelfde loader. Als een programma dat voor de ene loader is geschreven, wordt
geïmplementeerd naar een andere, is het resultaat meestal een AccessViolation
fout vanwege niet-overeenkomende deserialisatie van de invoerparameters van het
programma.
Voor alle praktische doeleinden moeten programma's altijd worden geschreven om de nieuwste BPF loader als doel te hebben, en de nieuwste loader is de standaard voor de opdrachtregelinterface en de JavaScript API's.
Implementatie
SBF-programma-implementatie is het proces van het uploaden van een BPF shared
object naar de data van een programma-account en het markeren van het account
als uitvoerbaar. Een client breekt het SBF shared object op in kleinere stukken
en verzendt deze als de instruction data van
Write
instructies naar de loader, waar de loader die gegevens in de
programma-accountdata schrijft. Zodra alle stukken zijn ontvangen, stuurt de
client een
Finalize
instructie naar de loader. De loader valideert vervolgens dat de SBF-data geldig
is en markeert het programma-account als executable. Zodra het
programma-account als uitvoerbaar is gemarkeerd, kunnen volgende transacties
instructies uitgeven die dat programma moet verwerken.
Wanneer een instructie naar een uitvoerbaar SBF-programma wordt gestuurd, configureert de loader de uitvoeringsomgeving van het programma, serialiseert de invoerparameters van het programma, roept het entrypoint van het programma aan en rapporteert eventuele fouten die zijn opgetreden.
Voor meer informatie, zie programma's implementeren.
Serialisatie van invoerparameters
SBF-loaders serialiseren de programma-invoerparameters naar een byte-array die vervolgens wordt doorgegeven aan het entrypoint van het programma, waar het programma verantwoordelijk is voor het deserialiseren ervan on-chain. Een van de veranderingen tussen de verouderde loader en de huidige loader is dat de invoerparameters worden geserialiseerd op een manier die resulteert in verschillende parameters die op uitgelijnde offsets binnen de uitgelijnde byte- array vallen. Dit stelt deserialisatie-implementaties in staat om direct naar de byte-array te verwijzen en uitgelijnde pointers aan het programma te leveren.
De nieuwste loader serialiseert de programma-invoerparameters als volgt (alle codering is little endian):
- 8 bytes unsigned aantal accounts
- Voor elk account
- 1 byte dat aangeeft of dit een duplicaat account is, zo niet dan is de waarde 0xff, anders is de waarde de index van het account waarvan het een duplicaat is.
- Indien duplicaat: 7 bytes padding
- Indien geen duplicaat:
- 1 byte boolean, true als account een ondertekenaar is
- 1 byte boolean, true als account schrijfbaar is
- 1 byte boolean, true als account uitvoerbaar is
- 4 bytes padding
- 32 bytes van de publieke sleutel van het account
- 32 bytes van de publieke sleutel van de eigenaar van het account
- 8 bytes unsigned aantal lamport in bezit van het account
- 8 bytes unsigned aantal bytes aan accountgegevens
- x bytes aan accountgegevens
- 10k bytes aan padding, gebruikt voor realloc
- voldoende padding om de offset uit te lijnen op 8 bytes.
- 8 bytes rent epoch
- 8 bytes unsigned aantal instruction data
- x bytes aan instruction data
- 32 bytes van de programma-id
Is this page helpful?