CPI-Kostenmodell und Datensynchronisation

Zusammenfassung

Jeder CPI kostet ~1.000 CUs Basis plus Serialisierungskosten. Kontodaten werden vor und nach der Ausführung des Aufgerufenen synchronisiert. PDA-Signierung verwendet die Programm-ID des Aufrufers. Rückgabedaten sind auf 1.024 Bytes begrenzt.

CPI-Kostenmodell

CPI-Kosten werden aus demselben Transaktions-Rechenbudget gezogen (gemeinsamer Zähler). Die vollständige Kostenformel für jeden invoke / invoke_signed Aufruf:

total_cpi_cost = invocation_cost
+ instruction_data_cost
+ account_meta_cost (SIMD-0339 only)
+ account_info_cost (SIMD-0339 only)
+ per_account_data_cost (for each non-executable account)
+ callee_execution_cost

Kostenaufschlüsselung

KostenkomponenteFormelQuelle
Aufruf (fest)invoke_units = 1.000 CUs (946 mit SIMD-0339)Berechnet beim CPI-Eintritt
Instruction-Data-Serialisierunginstruction_data_len / cpi_bytes_per_unitcpi_bytes_per_unit = 250. Berechnet in translate_instruction.
Account-Meta-Serialisierung (SIMD-0339)(num_account_metas * 34) / cpi_bytes_per_unitJedes AccountMeta ist 34 Bytes (32 Pubkey + 1 is_signer + 1 is_writable). Berechnet in translate_instruction.
Account-Info-Übersetzung (SIMD-0339)(num_account_infos * 80) / cpi_bytes_per_unitACCOUNT_INFO_BYTE_SIZE = 80 Bytes (32 Key + 32 Owner + 8 Lamports + 8 data_len). Berechnet in translate_account_infos.
Pro-Konto-Datenaccount_data_len / cpi_bytes_per_unitBerechnet pro Konto in CallerAccount::from_account_info und für ausführbare Konten.
Ausführung des AufgerufenenBeliebige CUs, die das aufgerufene Programm verbrauchtVom gemeinsamen Zähler während der Ausführung des Aufgerufenen abgezogen.

Beispiel für Kostenberechnung

Ein CPI mit 100 Bytes instruction data, 5 Account-Metas, 5 Account-Infos (jeweils mit 1.000 Bytes Daten), SIMD-0339 aktiv:

invocation_cost = 946
instruction_data_cost = 100 / 250 = 0 (integer division)
account_meta_cost = (5 * 34) / 250 = 0
account_info_cost = (5 * 80) / 250 = 1
per_account_data_cost = 5 * (1000 / 250) = 20
total (before callee) = 967 CUs

Synchronisierung von Kontodaten

Der Kontostatus wird zwischen Aufrufer und Aufgerufenem an zwei Punkten während eines CPI synchronisiert. Dies stellt sicher, dass beide Seiten eine konsistente Ansicht der Kontodaten sehen.

Pre-CPI-Synchronisierung (Aufrufer zu Aufgerufenem)

Bevor der Aufgerufene ausgeführt wird, kopiert update_callee_account die laufenden Änderungen des Aufrufers in die Kontoansicht des Aufgerufenen:

FeldRichtungWann
LamportsAufrufer -> AufgerufenerWenn sich der Wert von der aktuellen Ansicht des Aufgerufenen unterscheidet
DatenlängeAufrufer -> AufgerufenerWenn der Aufrufer die Größe des Kontos geändert hat. Darf original_data_len + MAX_PERMITTED_DATA_INCREASE (10 KiB) nicht überschreiten.
DateninhaltAufrufer -> AufgerufenerWenn die Daten des Kontos änderbar sind (can_data_be_changed erfolgreich)
EigentümerAufrufer -> AufgerufenerWird zuletzt gesetzt, damit Daten-/Lamport-Änderungen unter dem alten Eigentümer zulässig sind

Post-CPI-Synchronisierung (Aufgerufener zu Aufrufer)

Nachdem der Aufgerufene zurückkehrt, kopiert update_caller_account die Änderungen des Aufgerufenen zurück in die Ansicht des Aufrufers. Dies erfolgt nur für Konten, die als beschreibbar markiert sind:

FeldRichtungWann
LamportsAufgerufener -> AufruferWird immer zurückkopiert
EigentümerAufgerufener -> AufruferWird immer zurückkopiert
DatenlängeAufgerufener -> AufruferBei Änderung. Der VM-Daten-Slice-Zeiger und das serialisierte Längenfeld werden aktualisiert. Wenn das Konto verkleinert wurde, wird freigegebener Speicher auf null gesetzt. Wenn die neue Länge original_data_len + MAX_PERMITTED_DATA_INCREASE überschreitet, wird InvalidRealloc zurückgegeben.
DateninhaltAufgerufener -> AufruferWird vom Datenpuffer des Aufgerufenen in die serialisierte Datenregion des Aufrufers kopiert

Realloc-Limits

Kontendaten können während CPI bis zu MAX_PERMITTED_DATA_INCREASE = 10.240 Bytes (10 KiB) über die Datenlänge des Kontos zu Beginn der aktuellen Top-Level-Instruktion hinaus vergrößert werden. Dieses Limit wird sowohl in update_callee_account (vor CPI) als auch in update_caller_account (nach CPI) durchgesetzt. Bei Überschreitung wird InvalidRealloc zurückgegeben.

PDA-Signierung

Wenn invoke_signed aufgerufen wird, leitet die Runtime PDA-Adressen aus den bereitgestellten Seeds und der Programm-ID des Aufrufers ab. Dies geschieht in translate_signers_rust:

  1. Das Signer-Seed-Array wird validiert: maximal MAX_SIGNERS (16) Signer-Seed-Sets.
  2. Jedes Seed-Set wird validiert: maximal MAX_SEEDS (16) Seeds pro Set, jeder Seed maximal MAX_SEED_LEN (32) Bytes.
  3. Pubkey::create_program_address wird mit den Seeds und der Programm-ID des Aufrufers (nicht des Aufgerufenen) aufgerufen.
  4. Wenn die Seeds keine gültige PDA erzeugen (d. h. der resultierende Punkt liegt auf der ed25519-Kurve), schlägt der CPI mit BadSeeds fehl.
  5. Die abgeleiteten PDA-Pubkeys werden gesammelt und an prepare_next_instruction als gültige Signer übergeben. Während der Berechtigungsprüfung wird, wenn ein Callee-Konto als Signer markiert ist und sein Pubkey mit einer dieser abgeleiteten PDAs übereinstimmt, die Signer-Prüfung bestanden.

Die PDA wird unter Verwendung der Programm-ID des Aufrufers abgeleitet, nicht des Aufgerufenen. Das bedeutet, dass nur das Programm, das die PDA besitzt (dasjenige, dessen ID zur Ableitung verwendet wurde), in ihrem Namen signieren kann. Ein Programm kann nicht für PDAs signieren, die von anderen Programmen abgeleitet wurden.

Rückgabedaten

Programme können Daten mithilfe des Rückgabedaten-Mechanismus an Aufrufer zurückgeben. Dieser verwendet zwei Syscalls:

  • sol_set_return_data: Legt bis zu MAX_RETURN_DATA (1.024) Bytes an Rückgabedaten für die aktuelle Instruktion fest. Die Kosten betragen data_len / cpi_bytes_per_unit + syscall_base_cost CUs.
  • sol_get_return_data: Liest die Rückgabedaten, die von der zuletzt ausgeführten Instruktion festgelegt wurden. Gibt die Daten zusammen mit der Programm-ID zurück, die sie festgelegt hat. Die Kosten betragen (data_len + 32) / cpi_bytes_per_unit + syscall_base_cost CUs (32 Bytes für die Programm-ID).

Rückgabedaten werden pro Transaktion gespeichert und von jeder Anweisung überschrieben, die sol_set_return_data aufruft. Zu Beginn jedes Programmaufrufs setzt die Runtime die Rückgabedaten auf leer zurück. Nachdem ein CPI zurückkehrt, kann der Aufrufer alle Rückgabedaten lesen, die der Aufgerufene (oder ein vom Aufgerufenen aufgerufenes Programm) zuletzt gesetzt hat.

Rückgabedaten sind auf 1.024 Bytes begrenzt. Nur das letzte Programm, das sol_set_return_data in der Aufrufkette aufruft, bestimmt, was der Aufrufer sieht. Wenn ein Aufgerufener weitere CPIs durchführt, die Rückgabedaten setzen, werden die eigenen Rückgabedaten des Aufgerufenen überschrieben.

Is this page helpful?

Inhaltsverzeichnis

Seite bearbeiten

Verwaltet von

© 2026 Solana Foundation.
Alle Rechte vorbehalten.
Verbinden Sie sich