Versie 2.0 is de eerste stabiele release van het Python-pakket. De 2.0-migratiegids behandelt de ondertekeningsvormen die het deelt met Rust en TypeScript.
Installatie
Installeer het basispakket, plus één extra per backend die een provider- afhankelijkheid nodig heeft:
pip install solana-keychain # memory, vault and parapip install 'solana-keychain[aws-kms]' # AWS KMS (boto3)pip install 'solana-keychain[gcp-kms]' # GCP KMS (google-cloud-kms)pip install 'solana-keychain[privy]' # Privypip install 'solana-keychain[turnkey]' # Turnkeypip install 'solana-keychain[fireblocks]' # Fireblockspip install 'solana-keychain[cdp]' # Coinbase Developer Platformpip install 'solana-keychain[crossmint]' # Crossmintpip install 'solana-keychain[dfns]' # Dfnspip install 'solana-keychain[openfort]' # Openfortpip install 'solana-keychain[utila]' # Utilapip install 'solana-keychain[fordefi]' # Fordefi# Several at oncepip install 'solana-keychain[aws-kms,privy]'
Vereist Python 3.10 of nieuwer. Transacties zijn
solders VersionedTransaction-objecten;
legacy-, v0- en v1-berichten worden allemaal geaccepteerd, behalve door Fireblocks met
use_program_call=True, dat alleen legacy en v0 accepteert.
Memory, Vault en Para kunnen worden geïmporteerd vanuit de pakketroot. Elke andere backend
wordt geïmporteerd vanuit een eigen submodule (bijvoorbeeld solana_keychain.aws_kms), en
het importeren van een backend zonder bijbehorende extra veroorzaakt een ImportError die de te
installeren extra noemt. Ledger is alleen beschikbaar voor Rust.
Basisgebruik
Elke backend heeft een config-dataclass en een asynchrone create_<backend>_signer-
factory die een kant-en-klare signer teruggeeft. Alle ondertekeningsmethoden zijn coroutines:
import asynciofrom solana_keychain import MemorySignerfrom solders.transaction import VersionedTransactionasync def sign(transaction: VersionedTransaction) -> str:signer = MemorySigner.from_private_key_file("/path/to/id.json")print("Signing with:", signer.pubkey)# Check availability (useful for remote signers)if not await signer.is_available():raise RuntimeError("Signer offline")result = await signer.sign_transaction(transaction)print("Signature:", result.signature)print("Fully signed:", result.is_complete)return result.encoded_transaction # base64 wire transaction
sign_transaction geeft een SignedTransaction terug met encoded_transaction,
signature, is_complete en transaction. transaction is de gezaghebbende
ondertekende transactie: ga daarmee verder, vooral bij een modifying signer, waarvan het
resultaat een ander object is dan het object dat je hebt doorgegeven.
Uniforme factory
create_keychain_signer neemt een backendnaam en de config-dataclass van die backend,
en importeert de backend lazy, zodat alleen de extras die je gebruikt hoeven te worden
geïnstalleerd:
from solana_keychain import create_keychain_signerfrom solana_keychain.privy import PrivySignerConfigsigner = await create_keychain_signer("privy",PrivySignerConfig(app_id="your-app-id",app_secret="your-app-secret",wallet_id="your-wallet-id",),)
De namen staan in SUPPORTED_BACKENDS: aws-kms, cdp, crossmint,
dfns, fireblocks, fordefi, gcp-kms, memory, openfort, para,
privy, turnkey, utila en vault. Een onbekende naam, of een config van de
verkeerde klasse, veroorzaakt een SignerError met SignerErrorCode.CONFIG_ERROR. De
factory is getypeerd als teruggevend de basis-SolanaSigner, dus beperk het type met
isinstance voordat je ondertekent (zie Signer-mogelijkheden).
Externe HTTP-clients
Externe HTTP-backends accepteren een optionele http_client (een httpx.AsyncClient)
voor aangepaste TLS of proxy's. Als deze niet is ingesteld, gebruikt elk verzoek een eenmalige client
met een time-out van 60 seconden, afgedwongen HTTPS op de basis-URL, en worden redirects
afgewezen.
Signer-mogelijkheden
Elke backend is een subklasse van de SolanaSigner-ABC (pubkey, sign_message,
is_available) plus precies één capability-klasse:
TransactionSigner:sign_transaction(tx)ondertekent je transactie ter plekke en geeft eenSignedTransactionterug. Jij verzendt de transactie.ModifyingSigner:modify_and_sign_transaction(tx)laat de provider de transactie herschrijven en ondertekent die vervolgens zonder te verzenden. Je object blijft ongewijzigd; ga verder met de teruggegevenSignedTransaction.transaction.SendingSigner:sign_and_send_transaction(tx)ondertekent en verzendt via de provider en geeft deSignatureterug van de transactie die is verwerkt. Je transactie wordt nooit gewijzigd.
| Backend | Capability-klasse | sign_message |
|---|---|---|
| memory, vault, privy, turnkey, aws-kms, fireblocks, gcp-kms, dfns, para, openfort | TransactionSigner | Ja |
| cdp | TransactionSigner | Alleen UTF-8-payloads, anders SERIALIZATION_ERROR |
| utila | TransactionSigner | Veroorzaakt SIGNING_FAILED |
fordefi black box (FordefiBlackBoxSigner) | TransactionSigner | Ja |
fordefi native manual (FordefiNativeManualSigner) | ModifyingSigner | Ja |
fordefi native auto (FordefiNativeAutoSigner) | SendingSigner | Ja |
| crossmint | SendingSigner | Veroorzaakt SIGNING_FAILED |
Beperk het type met isinstance. Alle capability-klassen worden geëxporteerd vanuit de
pakketroot:
from solana_keychain import ModifyingSigner, SendingSigner, TransactionSignerif isinstance(signer, SendingSigner):signature = await signer.sign_and_send_transaction(transaction)elif isinstance(signer, ModifyingSigner):result = await signer.modify_and_sign_transaction(transaction)elif isinstance(signer, TransactionSigner):result = await signer.sign_transaction(transaction)
Initialisatie
De create_*-factory's en create_keychain_signer doen alle vereiste setup voor
jou. Als je de Privy-, Fireblocks-, Dfns-, Crossmint-, Para-, Openfort- of
Utila-signerklasse rechtstreeks aanmaakt, voer dan await signer.init() uit vóór gebruik. Tot die tijd
veroorzaken pubkey en elke ondertekenaanroep SignerErrorCode.NOT_INITIALIZED.
Backendconfiguratie
Memory
Bouw op basis van een Solana CLI-keypair-bestand, een base58- of "[1,2,...]"-string, ruwe
bytes, of een solders Keypair:
from solana_keychain import MemorySigner, MemorySignerConfig, create_memory_signerfrom solders.keypair import Keypairsigner = MemorySigner.from_private_key_file("/path/to/id.json")signer = MemorySigner.from_private_key_string("base58_private_key")signer = MemorySigner.from_bytes(secret_key_bytes) # 64 or 32 bytessigner = await create_memory_signer(MemorySignerConfig(keypair=Keypair()))
De sleutel staat in het procesgeheugen, en Python kan deze niet op nul zetten. Gebruik deze backend voor ontwikkeling en testen.
HashiCorp Vault
De sleutel moet een ed25519-sleutel in de transit-engine zijn. Gewone HTTP wordt alleen
accepteerd voor een loopback-adres, voor vault server -dev:
from solana_keychain import VaultSignerConfig, create_vault_signersigner = await create_vault_signer(VaultSignerConfig(api_base_url="https://vault.example.com:8200",token="hvs.xxxxx",key_name="my-solana-key",public_key="base58_public_key",))
AWS KMS
key_id is een sleutel-ID, ARN of alias voor een ECC_NIST_EDWARDS25519-sleutel. Geef een
vooraf gebouwde boto3 KMS-client mee voor aangepaste inloggegevens of endpoints; region wordt
dan genegeerd:
from solana_keychain.aws_kms import AwsKmsSignerConfig, create_aws_kms_signersigner = await create_aws_kms_signer(AwsKmsSignerConfig(key_id="alias/my-solana-key",public_key="base58_public_key",region="us-east-1", # optional, defaults to the AWS config chain))
GCP KMS
De sleutelversie moet een EC_SIGN_ED25519-sleutel zijn. client accepteert optioneel een
vooraf gebouwde KeyManagementServiceAsyncClient:
from solana_keychain.gcp_kms import GcpKmsSignerConfig, create_gcp_kms_signersigner = await create_gcp_kms_signer(GcpKmsSignerConfig(key_name="projects/my-project/locations/us-east1/keyRings/my-ring/cryptoKeys/my-key/cryptoKeyVersions/1",public_key="base58_public_key",))
Privy
from solana_keychain.privy import PrivySignerConfig, create_privy_signersigner = await create_privy_signer(PrivySignerConfig(app_id="app_id",app_secret="app_secret",wallet_id="wallet_id",))
Wallets die door een autorisatiesleutel worden beschermd, nemen een PrivyAuthorizationConfig als
authorization_context.
Turnkey
from solana_keychain.turnkey import TurnkeySignerConfig, create_turnkey_signersigner = await create_turnkey_signer(TurnkeySignerConfig(api_public_key="api_public_key", # hex P-256api_private_key="api_private_key", # hex P-256organization_id="org_id",private_key_id="private_key_id",public_key="base58_public_key",))
Fireblocks
from solana_keychain.fireblocks import FireblocksSignerConfig, create_fireblocks_signersigner = await create_fireblocks_signer(FireblocksSignerConfig(api_key="api_key",private_key_pem="-----BEGIN RSA PRIVATE KEY-----\n...",vault_account_id="0",asset_id="SOL", # or "SOL_TEST" for devnet))
use_program_call=True ondertekent met de PROGRAM_CALL-operatie in plaats van RAW. De transactie
wordt alleen ter ondertekening verzonden, dus Fireblocks herschrijft of verzendt de
transactie niet, en jij verzendt zoals gebruikelijk. Het vereist een hot wallet en moet
door Fireblocks voor je workspace worden ingeschakeld.
CDP (Coinbase Developer Platform)
from solana_keychain.cdp import CdpSignerConfig, create_cdp_signersigner = await create_cdp_signer(CdpSignerConfig(api_key_id="api_key_id",api_key_secret="api_key_secret",wallet_secret="wallet_secret",address="base58_address",network="solana", # or "solana-devnet"; required for address lookup tables))
Crossmint
from solana_keychain.crossmint import CrossmintSignerConfig, create_crossmint_signersigner = await create_crossmint_signer(CrossmintSignerConfig(api_key="api_key",wallet_locator="wallet_locator",))signature = await signer.sign_and_send_transaction(transaction)
Crossmint is alleen verzendend. De API voert een goedgekeurde transactie altijd
aan de serverzijde uit en sponsort gas, dus CrossmintSigner is een SendingSigner en
sign_message veroorzaakt SIGNING_FAILED. De teruggegeven handtekening identificeert de
transactie die Crossmint heeft verwerkt, en die kan afwijken van die van jou. Het instellen van
signer_secret (een xmsk1_... delegated-signer-secret) keurt transacties
automatisch goed, waarmee de keuze wat wordt goedgekeurd aan Crossmint wordt gedelegeerd.
Dfns
from solana_keychain.dfns import DfnsSignerConfig, create_dfns_signersigner = await create_dfns_signer(DfnsSignerConfig(auth_token="auth_token",cred_id="cred_id",private_key_pem="-----BEGIN EC PRIVATE KEY-----\n...",wallet_id="wallet_id",))
Openfort
from solana_keychain.openfort import OpenfortSignerConfig, create_openfort_signersigner = await create_openfort_signer(OpenfortSignerConfig(secret_key="sk_...",account_id="acc_...",wallet_secret="wallet_secret",))
Para
from solana_keychain import ParaSignerConfig, create_para_signersigner = await create_para_signer(ParaSignerConfig(api_key="sk_...",wallet_id="wallet_uuid",))
Utila
Utila ondertekent alleen transacties; sign_message veroorzaakt SIGNING_FAILED.
from solana_keychain.utila import UtilaSignerConfig, create_utila_signersigner = await create_utila_signer(UtilaSignerConfig(service_account_email="sa@example.com",service_account_private_key_pem="-----BEGIN PRIVATE KEY-----\n...",vault_id="vault_id",wallet_id="wallet_id",network="networks/solana-devnet",))
Fordefi
Fordefi heeft drie ondertekeningsmodi, vastgelegd bij het aanmaken door chain en
push_mode. create_fordefi_signer kiest de klasse, elke klasse weigert een
config die voor een andere bedoeld is, en alle drie ondertekenen berichten:
| Modus | Config | Klasse | Methode |
|---|---|---|---|
| Black box | chain niet ingesteld | FordefiBlackBoxSigner | sign_transaction |
| Native auto | chain ingesteld, push_mode niet ingesteld of "auto" | FordefiNativeAutoSigner | sign_and_send_transaction |
| Native manual | chain ingesteld, push_mode="manual" | FordefiNativeManualSigner | modify_and_sign_transaction |
API-verzoeken worden geauthenticeerd met een ECDSA P-256-verzoekhandtekening: lever de
sleutel aan als private_key_pem, of bewaar deze in een KMS/HSM door een
FordefiRequestSigner-subklasse mee te geven als request_signer.
Native auto-modus: stel chain in ("solana_devnet" of "solana_mainnet"), en
Fordefi kan de blockhash en kosten vervangen, waarna het de
transactie zelf ondertekent en verzendt. Het accepteert alleen een niet-ondertekende transactie waarvan de enige vereiste
ondertekenaar de vault is. fee wordt ongewijzigd aan Fordefi doorgegeven:
import osfrom solana_keychain.fordefi import FordefiSignerConfig, create_fordefi_signersigner = await create_fordefi_signer(FordefiSignerConfig(access_token=os.environ["FORDEFI_ACCESS_TOKEN"],vault_id=os.environ["FORDEFI_VAULT_ID"],public_key=os.environ["FORDEFI_PUBLIC_KEY"], # vault address (base58)private_key_pem=open("./secret/private.pem").read(),chain="solana_devnet",fee={"type": "custom", "priority_fee": "1000"}, # optional))signature = await signer.sign_and_send_transaction(transaction)
Native manual-modus: stel chain en push_mode="manual" in. Fordefi herschrijft
het bericht (recente blockhash, Compute Budget-feeinstructies) en ondertekent het,
maar verzendt niet. De teruggegeven transactie vervangt de transactie die je hebt ingediend,
dus ga altijd verder met result.transaction. Fordefi moet de fee payer zijn en
moet vóór elke andere vereiste ondertekenaar ondertekenen: een transactie die al
handtekeningen bevat wordt geaccepteerd, maar de herschrijving maakt ze ongeldig. De herschrijving wordt niet vergeleken met het origineel,
dus controleer het resultaat vóór het verzenden, en verzend snel, aangezien Fordefi
de verloophoogte van de nieuwe blockhash niet teruggeeft.
signer = await create_fordefi_signer(FordefiSignerConfig(access_token=os.environ["FORDEFI_ACCESS_TOKEN"],vault_id=os.environ["FORDEFI_VAULT_ID"],public_key=os.environ["FORDEFI_PUBLIC_KEY"],private_key_pem=open("./secret/private.pem").read(),chain="solana_devnet",push_mode="manual",))result = await signer.modify_and_sign_transaction(transaction)... # inspect result.transaction, then add any remaining signatures
Verzend de gecontroleerde transactie zoals bij Ondertekenen en verzenden, en stem af voordat je opnieuw probeert als het verzenden mislukt.
Black box-modus: laat chain weg. Fordefi ondertekent je exacte berichtbytes en
verzendt niet. Gebruik dit met een Fordefi black box-vault.
signer = await create_fordefi_signer(FordefiSignerConfig(access_token=os.environ["FORDEFI_ACCESS_TOKEN"],vault_id=os.environ["FORDEFI_BB_VAULT_ID"],public_key=os.environ["FORDEFI_BB_PUBLIC_KEY"],private_key_pem=open("./secret/private.pem").read(),))
Ondertekenen en verzenden
sign_and_send_transaction krijgt een transactie on-chain, ongeacht de vorm van de signer.
Een SendingSigner verzendt via zijn provider en je verzendfunctie
wordt nooit aangeroepen. Een TransactionSigner of ModifyingSigner ondertekent, en je verzend-
functie verzendt het base64-gecodeerde resultaat (het pakket heeft geen RPC-client):
import httpxfrom solana_keychain import sign_and_send_transactionfrom solders.signature import Signatureasync def send(encoded: str) -> Signature:async with httpx.AsyncClient() as client:response = await client.post("https://api.devnet.solana.com",json={"jsonrpc": "2.0","id": 1,"method": "sendTransaction","params": [encoded, {"encoding": "base64"}],},)return Signature.from_string(response.json()["result"])signature = await sign_and_send_transaction(signer, transaction, send)
Een signer die alleen kan ondertekenen en geen verzendfunctie heeft, veroorzaakt CONFIG_ERROR, en een
transactie waaraan na het ondertekenen nog handtekeningen ontbreken, veroorzaakt SIGNING_FAILED.
Foutafhandeling
Elke fout is een SignerError met een stabiele code uit SignerErrorCode:
BROADCAST_UNCONFIRMED, CONFIG_ERROR, EXPECTED_SOLANA_SIGNER, HTTP_ERROR,
INVALID_PRIVATE_KEY, INVALID_PUBLIC_KEY, IO_ERROR, NOT_AVAILABLE,
NOT_INITIALIZED, OTHER, PARSING_ERROR, REMOTE_API_ERROR,
SERIALIZATION_ERROR en SIGNING_FAILED. Match op code, niet op het bericht:
str() en repr() bevatten alleen een vast generiek bericht, nooit sleutelmateriaal of
ruwe providerresponses. status_code en provider_error_code worden ingesteld wanneer de
fout uit een providerrespons kwam.
SignerErrorCode.BROADCAST_UNCONFIRMED ("SIGNER_BROADCAST_UNCONFIRMED") betekent dat
de transactie mogelijk is verwerkt:
- Bij Crossmint en Fordefi native auto wordt
provider_transaction_idingesteld wanneer het aanmaken is geaccepteerd, enidempotency_keybevat de sleutel die is afgeleid van het bericht waaronder het aanmaken is verzonden. Het opnieuw verzenden van de byte-identieke transactie is veilig; een herbouwde transactie (bijvoorbeeld met een nieuwe blockhash) is een nieuwe overdracht. - Vanuit
sign_and_send_transactionveroorzaakt een mislukkende verzendfunctie deze fout mettransaction_signatureingesteld op de handtekening van de voltooide transactie. - Fireblocks veroorzaakt deze fout alleen als uit een poll blijkt dat Fireblocks een
use_program_call-transactie heeft verzonden, ondanks dat deze alleen ter ondertekening was verzonden.
from solana_keychain import SignerError, SignerErrorCodetry:signature = await sign_and_send_transaction(signer, transaction, send)except SignerError as error:if error.code is SignerErrorCode.BROADCAST_UNCONFIRMED:reconcile(error.provider_transaction_id,error.idempotency_key,error.transaction_signature,)raise
Een geannuleerde aanroep werpt opnieuw asyncio.CancelledError, die geen velden bevat. Om
een id te herstellen dat de provider vóór de annulering had geaccepteerd, geef je een
PendingTransactionId() (uit solana_keychain.core) mee als pending_transaction_id van de Crossmint- of
Fordefi native auto-config, en lees je .get() na
de annulering. Stem af met de provider voordat je opnieuw probeert.
Keychain valideert de ondertekeningsstap, niet wat de transactie doet: een door de provider teruggegeven handtekening wordt met ed25519 geverifieerd tegen het lokaal berekende bericht voordat deze wordt toegevoegd. Python kan sleutelbuffers niet op nul zetten, dus behandel bij backends met lokale sleutels (Memory, en de API-sleutels en PEM's in de configs van externe backends) het volledige procesgeheugen als gevoelig. Zie het beveiligingsmodel voor wat elke ondertekeningsvorm garandeert.
Eigen signers toevoegen
Maak een subklasse van de ene capability-klasse die bij jouw backend past (TransactionSigner,
ModifyingSigner of SendingSigner) uit solana_keychain.core. Raadpleeg de
handleiding voor het toevoegen van signers om
extra sleutelbeheerservices te integreren.
Bronnen
Is this page helpful?