Versie 2.0 is de eerste stabiele release van het Python-pakket. De migratiegids voor 2.0 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 hoger. 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 hoofdmap van het pakket. Elke andere backend
wordt geïmporteerd uit een eigen submodule (bijvoorbeeld solana_keychain.aws_kms), en
als je er een importeert zonder de bijbehorende extra, volgt er 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 direct bruikbare 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 hiermee 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
geïnstalleerd hoeven te zijn:
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, geeft een SignerError met SignerErrorCode.CONFIG_ERROR. De
factory is getypeerd als teruggevend van 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 mogelijkheidsklasse:
TransactionSigner:sign_transaction(tx)ondertekent je transactie ter plekke en geeft eenSignedTransactionterug. Jij verstuurt deze zelf.ModifyingSigner:modify_and_sign_transaction(tx)laat de provider de transactie herschrijven en ondertekent deze vervolgens zonder te versturen. Je object blijft ongewijzigd; ga verder met de teruggegevenSignedTransaction.transaction.SendingSigner:sign_and_send_transaction(tx)ondertekent en verstuurt via de provider en geeft deSignatureterug van de transactie die is verwerkt. Je transactie wordt nooit gewijzigd.
| Backend | Mogelijkheidsklasse | 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 | Geeft SIGNING_FAILED |
fordefi black box (FordefiBlackBoxSigner) | TransactionSigner | Ja |
fordefi native manual (FordefiNativeManualSigner) | ModifyingSigner | Ja |
fordefi native auto (FordefiNativeAutoSigner) | SendingSigner | Ja |
| crossmint | SendingSigner | Geeft SIGNING_FAILED |
Beperk het type met isinstance. Alle mogelijkheidsklassen worden geëxporteerd vanuit de hoofdmap van
het pakket:
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 benodigde setup voor
je. Als je de signerklasse van Privy, Fireblocks, Dfns, Crossmint, Para, Openfort of
Utila direct aanmaakt, roep dan await signer.init() aan voor gebruik. Tot die tijd geven
pubkey en elke ondertekeningsaanroep SignerErrorCode.NOT_INITIALIZED.
Backendconfiguratie
Memory
Bouw vanuit 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 zijn in de transit engine. 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. Dit
wordt alleen-ondertekenen verzonden, zodat Fireblocks de transactie niet herschrijft en niet
verstuurt, en jij verstuurt 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 kan alleen versturen. De API voert een goedgekeurde transactie altijd
serverzijdig uit en sponsort gas, dus CrossmintSigner is een SendingSigner en
sign_message geeft SIGNING_FAILED. De teruggegeven handtekening identificeert de
transactie die Crossmint heeft verwerkt, en die kan afwijken van de jouwe. Het instellen van
signer_secret (een xmsk1_... delegated-signer-secret) keurt transacties
automatisch goed, waardoor de keuze wat er 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 geeft 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 kent drie ondertekeningsmodi, vastgelegd bij constructie 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 de fees vervangen, waarna het de
transactie zelf ondertekent en verstuurt. Het accepteert alleen een niet-ondertekende transactie waarvan de enige vereiste
ondertekenaar de vault is. fee wordt ongewijzigd doorgegeven aan Fordefi:
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 verstuurt het 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 voordat je verstuurt, en verstuur snel, omdat 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
Verstuur de gecontroleerde transactie zoals bij Ondertekenen en versturen, en reconcilieer voordat je het opnieuw probeert als het versturen mislukt.
Black box-modus: laat chain weg. Fordefi ondertekent je exacte berichtbytes en
verstuurt 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 versturen
sign_and_send_transaction krijgt een transactie on-chain, ongeacht de vorm van de signer.
Een SendingSigner verstuurt via zijn provider en je verstuurfunctie
wordt nooit aangeroepen. Een TransactionSigner of ModifyingSigner ondertekent, en je verstuurfunctie
verstuurt 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 ondertekent en geen verstuurfunctie heeft, geeft CONFIG_ERROR, en een
transactie waarvan na het ondertekenen nog handtekeningen ontbreken, geeft 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 providerantwoorden. status_code en provider_error_code worden ingesteld wanneer de
fout afkomstig was van een providerantwoord.
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 verstuurd. Het opnieuw versturen van de byte-identieke transactie is veilig; een herbouwde transactie (bijvoorbeeld met een nieuwe blockhash) is een nieuwe transfer. - Vanuit
sign_and_send_transactiongeeft een mislukkende verstuurfunctie deze fout mettransaction_signatureingesteld op de handtekening van de voltooide transactie. - Fireblocks geeft deze alleen als uit een poll blijkt dat Fireblocks een
use_program_call-transactie heeft verstuurd, ondanks dat deze alleen-ondertekenen 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. Reconcilieer met de provider voordat je het 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 bij backends met lokale sleutels (Memory, en de API-sleutels en PEM's in de configs van externe backends) moet je het hele procesgeheugen als gevoelig beschouwen. Zie het beveiligingsmodel voor wat elke ondertekeningsvorm garandeert.
Aangepaste 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 Signers toevoegen om
extra sleutelbeheerdiensten te integreren.
Bronnen
Is this page helpful?