Documentation SolanaDéveloppement de programmes

FAQ

Postez vos questions sur StackExchange.

Berkeley Packet Filter (BPF)

Les programmes on-chain de Solana sont compilés via l'infrastructure du compilateur LLVM vers un format exécutable et linkable (ELF) contenant une variante du bytecode Berkeley Packet Filter (BPF).

Comme Solana utilise l'infrastructure du compilateur LLVM, un programme peut être écrit dans n'importe quel langage de programmation capable de cibler le backend BPF de LLVM.

BPF fournit un jeu d'instructions efficace qui peut être exécuté dans une machine virtuelle interprétée ou comme instructions natives compilées just-in-time efficaces.

Carte mémoire

La carte mémoire d'adresses virtuelles utilisée par les programmes SBF de Solana est fixe et structurée comme suit

  • Le code du programme commence à 0x100000000
  • Les données de pile commencent à 0x200000000
  • Les données de tas commencent à 0x300000000
  • Les paramètres d'entrée du programme commencent à 0x400000000

Les adresses virtuelles ci-dessus sont des adresses de départ, mais les programmes ont accès à un sous-ensemble de la carte mémoire. Le programme générera une erreur s'il tente de lire ou d'écrire à une adresse virtuelle à laquelle il n'a pas reçu d'accès, et une erreur AccessViolation sera renvoyée contenant l'adresse et la taille de la violation tentée.

InvalidAccountData

Cette erreur de programme peut survenir pour de nombreuses raisons. Généralement, elle est causée par la transmission d'un compte au programme que celui-ci n'attend pas, soit à une position incorrecte dans l'instruction, soit un compte incompatible avec l'instruction en cours d'exécution.

Une implémentation d'un programme peut également provoquer cette erreur lors de l'exécution d'une instruction inter-programmes en oubliant de fournir le compte pour le programme que vous appelez.

InvalidInstructionData

Cette erreur de programme peut survenir lors de la désérialisation de l'instruction. Vérifiez que la structure transmise correspond exactement à l'instruction. Il peut y avoir des espaces de remplissage entre les champs. Si le programme implémente le trait Rust Pack, essayez d'empaqueter et de désempaqueter le type d'instruction T pour déterminer l'encodage exact que le programme attend.

MissingRequiredSignature

Certaines instructions nécessitent que le compte soit un signataire ; cette erreur est renvoyée si un compte est censé être signé mais ne l'est pas.

Une implémentation d'un programme peut également provoquer cette erreur lors de l'exécution d'une invocation inter-programmes qui nécessite une adresse de programme signée, mais les graines de signataire transmises à invoke_signed ne correspondent pas aux graines de signataire utilisées pour créer l'adresse du programme create_program_address.

Stack

SBF utilise des cadres de pile au lieu d'un pointeur de pile variable. Chaque cadre de pile a une taille de 4 Ko.

Si un programme dépasse cette taille de cadre de pile, le compilateur signalera le dépassement sous forme d'avertissement.

Par exemple :

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

Le message identifie quel symbole dépasse son cadre de pile, mais le nom peut être déformé.

Pour démêler un symbole Rust, utilisez rustfilt.

L'avertissement ci-dessus provient d'un programme Rust, donc le nom de symbole démêlé est :

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

La raison pour laquelle un avertissement est signalé plutôt qu'une erreur est que certains crates dépendants peuvent inclure des fonctionnalités qui violent les restrictions de cadre de pile même si le programme n'utilise pas cette fonctionnalité. Si le programme viole la taille de pile à l'exécution, une erreur AccessViolation sera signalée.

Les cadres de pile SBF occupent une plage d'adresses virtuelles commençant à 0x200000000.

Taille du tas (heap)

Les programmes ont accès à un tas d'exécution via les API Rust alloc. Pour faciliter des allocations rapides, un simple tas bump de 32 Ko est utilisé. Le tas ne prend pas en charge free ou realloc.

En interne, les programmes ont accès à la région mémoire de 32 Ko commençant à l'adresse virtuelle 0x300000000 et peuvent implémenter un tas personnalisé basé sur les besoins spécifiques du programme.

Les programmes Rust implémentent le tas directement en définissant un global_allocator personnalisé

Chargeurs

Les programmes sont déployés et exécutés par des chargeurs d'exécution, actuellement il existe deux chargeurs pris en charge BPF Loader et BPF loader deprecated

Les chargeurs peuvent prendre en charge différentes interfaces binaires d'application, les développeurs doivent donc écrire leurs programmes pour et les déployer sur le même chargeur. Si un programme écrit pour un chargeur est déployé sur un autre, le résultat est généralement une erreur AccessViolation due à une désérialisation incompatible des paramètres d'entrée du programme.

À toutes fins pratiques, les programmes doivent toujours être écrits pour cibler le dernier chargeur BPF, et le dernier chargeur est celui par défaut pour l'interface en ligne de commande et les API JavaScript.

Déploiement

Le déploiement d'un programme SBF est le processus de téléchargement d'un objet partagé BPF dans les données d'un compte de programme et de marquer le compte comme exécutable. Un client divise l'objet partagé SBF en petits morceaux et les envoie comme données d'instruction des instructions Write au chargeur où le chargeur écrit ces données dans les données du compte du programme. Une fois tous les morceaux reçus, le client envoie une instruction Finalize au chargeur, le chargeur valide alors que les données SBF sont valides et marque le compte du programme comme exécutable. Une fois le compte du programme marqué exécutable, les transactions ultérieures peuvent émettre des instructions pour que ce programme les traite.

Lorsqu'une instruction est dirigée vers un programme SBF exécutable, le chargeur configure l'environnement d'exécution du programme, sérialise les paramètres d'entrée du programme, appelle le point d'entrée du programme et signale toute erreur rencontrée.

Pour plus d'informations, consultez déploiement des programmes.

Sérialisation des paramètres d'entrée

Les chargeurs SBF sérialisent les paramètres d'entrée du programme dans un tableau d'octets qui est ensuite transmis au point d'entrée du programme, où le programme est responsable de la désérialisation on-chain. L'un des changements entre le chargeur obsolète et le chargeur actuel est que les paramètres d'entrée sont sérialisés de manière à ce que les différents paramètres se retrouvent sur des décalages alignés dans le tableau d'octets aligné. Cela permet aux implémentations de désérialisation de référencer directement le tableau d'octets et de fournir des pointeurs alignés au programme.

Le dernier chargeur sérialise les paramètres d'entrée du programme comme suit (tout encodage est en little endian):

  • 8 octets nombre non signé de comptes
  • Pour chaque compte
    • 1 octet indiquant s'il s'agit d'un compte en double, si ce n'est pas un doublon alors la valeur est 0xff, sinon la valeur est l'index du compte dont il est un doublon.
    • Si doublon: 7 octets de remplissage
    • Si pas doublon:
      • 1 octet booléen, vrai si le compte est un signataire
      • 1 octet booléen, vrai si le compte est inscriptible
      • 1 octet booléen, vrai si le compte est exécutable
      • 4 octets de remplissage
      • 32 octets de la clé publique du compte
      • 32 octets de la clé publique du propriétaire du compte
      • 8 octets nombre non signé de lamports possédés par le compte
      • 8 octets nombre non signé d'octets de données du compte
      • x octets de données du compte
      • 10k octets de remplissage, utilisés pour realloc
      • suffisamment de remplissage pour aligner le décalage sur 8 octets.
      • 8 octets d'époque de loyer
  • 8 octets de nombre non signé de données d'instruction
  • x octets de données d'instruction
  • 32 octets de l'identifiant du programme

Is this page helpful?

Table des matières

Modifier la page