Documentazione SolanaSviluppo di programmi

FAQ

Posta le tue domande su StackExchange.

Berkeley Packet Filter (BPF)

I programmi onchain di Solana sono compilati tramite l'infrastruttura del compilatore LLVM in un formato eseguibile e collegabile (ELF) contenente una variazione del Berkeley Packet Filter (BPF) bytecode.

Poiché Solana utilizza l'infrastruttura del compilatore LLVM, un programma può essere scritto in qualsiasi linguaggio di programmazione che possa rivolgersi al backend BPF di LLVM.

BPF fornisce un efficiente set di istruzioni che può essere eseguito in una macchina virtuale interpretata o come efficienti istruzioni native compilate just-in-time.

Mappa della memoria

La mappa della memoria degli indirizzi virtuali utilizzata dai programmi SBF di Solana è fissa e strutturata come segue

  • Il codice del programma inizia a 0x100000000
  • I dati dello stack iniziano a 0x200000000
  • I dati dell'heap iniziano a 0x300000000
  • I parametri di input del programma iniziano a 0x400000000

Gli indirizzi virtuali sopra indicati sono indirizzi iniziali, ma ai programmi viene concesso l'accesso a un sottoinsieme della mappa della memoria. Il programma andrà in panic se tenta di leggere o scrivere su un indirizzo virtuale a cui non ha ricevuto accesso, e verrà restituito un AccessViolation errore che contiene l'indirizzo e la dimensione della violazione tentata.

InvalidAccountData

Questo errore del programma può verificarsi per molte ragioni. Di solito è causato dal passaggio di un account al programma che il programma non si aspetta, sia nella posizione errata nell'istruzione o un account non compatibile con l'istruzione che viene eseguita.

Un'implementazione di un programma potrebbe anche causare questo errore quando esegue un'istruzione cross-program e dimentica di fornire l'account per il programma che si sta chiamando.

InvalidInstructionData

Questo errore del programma può verificarsi durante il tentativo di deserializzare l'istruzione, verifica che la struttura passata corrisponda esattamente all'istruzione. Potrebbero esserci alcuni riempimenti tra i campi. Se il programma implementa il trait Rust Pack allora prova a impacchettare e spacchettare il tipo di istruzione T per determinare l'esatta codifica che il programma si aspetta.

MissingRequiredSignature

Alcune istruzioni richiedono che l'account sia un firmatario; questo errore viene restituito se ci si aspetta che un account sia firmato ma non lo è.

Un'implementazione di un programma potrebbe anche causare questo errore quando esegue una invocazione cross-program che richiede un indirizzo di programma firmato, ma i seed del firmatario passati a invoke_signed non corrispondono ai seed del firmatario utilizzati per creare l'indirizzo del programma create_program_address.

Stack

SBF utilizza frame di stack invece di un puntatore di stack variabile. Ogni frame di stack ha una dimensione di 4KB.

Se un programma viola la dimensione del frame di stack, il compilatore segnalerà il superamento come un avviso.

Per esempio:

Error: Function _ZN16curve25519_dalek7edwards21EdwardsBasepointTable6create17h178b3d2411f7f082E Stack offset of -30728 exceeded max offset of -4096 by 26632 bytes, please minimize large stack variables

Il messaggio identifica quale simbolo sta superando il suo frame di stack, ma il nome potrebbe essere offuscato.

Per decodificare un simbolo Rust usa rustfilt.

L'avviso sopra proviene da un programma Rust, quindi il nome del simbolo decodificato è:

rustfilt _ZN16curve25519_dalek7edwards21EdwardsBasepointTable6create17h178b3d2411f7f082E
curve25519_dalek::edwards::EdwardsBasepointTable::create

Il motivo per cui viene segnalato un avviso anziché un errore è perché alcuni crate dipendenti possono includere funzionalità che violano le restrizioni del frame di stack anche se il programma non utilizza quella funzionalità. Se il programma viola la dimensione dello stack durante l'esecuzione, verrà segnalato un errore AccessViolation.

I frame di stack SBF occupano un intervallo di indirizzi virtuali che inizia da 0x200000000.

Dimensione dell'heap

I programmi hanno accesso a un heap di runtime tramite le API Rust alloc. Per facilitare allocazioni veloci, viene utilizzato un semplice heap bump di 32KB. L'heap non supporta free o realloc.

Internamente, i programmi hanno accesso alla regione di memoria di 32KB che inizia all'indirizzo virtuale 0x300000000 e possono implementare un heap personalizzato in base alle esigenze specifiche del programma.

I programmi Rust implementano l'heap direttamente definendo un global_allocator personalizzato

Loader

I programmi vengono distribuiti ed eseguiti tramite loader di runtime, attualmente ci sono due loader supportati BPF Loader e BPF loader deprecated

I loader possono supportare diverse interfacce binarie di applicazione, quindi gli sviluppatori devono scrivere i loro programmi per lo stesso loader su cui li distribuiranno. Se un programma scritto per un loader viene distribuito su un altro, il risultato è solitamente un errore AccessViolation dovuto alla deserializzazione non corrispondente dei parametri di input del programma.

Per tutti gli scopi pratici, i programmi dovrebbero sempre essere scritti per utilizzare l'ultimo BPF loader, e il loader più recente è quello predefinito per l'interfaccia a riga di comando e le API javascript.

Distribuzione

La distribuzione di un programma SBF è il processo di caricamento di un oggetto condiviso BPF nei dati di un account di programma e di marcatura dell'account come eseguibile. Un client suddivide l'oggetto condiviso SBF in pezzi più piccoli e li invia come dati di istruzione di Write al loader, dove il loader scrive quei dati nei dati dell'account del programma. Una volta ricevuti tutti i pezzi, il client invia un'istruzione Finalize al loader, il loader quindi verifica che i dati SBF siano validi e marca l'account del programma come eseguibile. Una volta che l'account del programma è marcato come eseguibile, le transazioni successive possono emettere istruzioni per quel programma da elaborare.

Quando un'istruzione è diretta a un programma SBF eseguibile, il loader configura l'ambiente di esecuzione del programma, serializza i parametri di input del programma, chiama il punto di ingresso del programma e segnala eventuali errori riscontrati.

Per ulteriori informazioni, consulta deploying programs.

Serializzazione dei parametri di input

I loader SBF serializzano i parametri di input del programma in un array di byte che viene poi passato al punto di ingresso del programma, dove il programma è responsabile della deserializzazione on-chain. Uno dei cambiamenti tra il loader deprecato e quello attuale è che i parametri di input sono serializzati in modo che i vari parametri cadano su offset allineati all'interno dell'array di byte allineato. Questo consente alle implementazioni di deserializzazione di fare riferimento direttamente all'array di byte e fornire puntatori allineati al programma.

Il loader più recente serializza i parametri di input del programma come segue (tutte le codifiche sono in little endian):

  • 8 byte numero non firmato di account
  • Per ogni account
    • 1 byte che indica se questo è un account duplicato, se non è un duplicato allora il valore è 0xff, altrimenti il valore è l'indice dell'account di cui è un duplicato.
    • Se duplicato: 7 byte di padding
    • Se non duplicato:
      • 1 byte booleano, vero se l'account è un firmatario
      • 1 byte booleano, vero se l'account è scrivibile
      • 1 byte booleano, vero se l'account è eseguibile
      • 4 byte di padding
      • 32 byte della chiave pubblica dell'account
      • 32 byte della chiave pubblica del proprietario dell'account
      • 8 byte numero non firmato di lamport posseduti dall'account
      • 8 byte numero non firmato di byte di dati dell'account
      • x byte di dati dell'account
      • 10k byte di padding, usati per realloc
      • padding sufficiente per allineare l'offset a 8 byte.
      • 8 byte epoch di rent
  • 8 byte di numero non firmato di instruction data
  • x byte di instruction data
  • 32 byte del program id

Is this page helpful?

Indice

Modifica Pagina