Source code for stellar_sdk.transaction_envelope

import copy
from typing import Sequence, Union

from xdrlib3 import Packer

from . import xdr as stellar_xdr
from .base_transaction_envelope import BaseTransactionEnvelope
from .decorated_signature import DecoratedSignature
from .exceptions import SignatureExistError
from .keypair import Keypair
from .signer_key import SignerKeyType
from .transaction import Transaction

__all__ = ["TransactionEnvelope"]


[docs] class TransactionEnvelope(BaseTransactionEnvelope["TransactionEnvelope"]): """The :class:`TransactionEnvelope` object, which represents a transaction envelope ready to sign and submit to send over the network. When a transaction is ready to be prepared for sending over the network, it must be put into a :class:`TransactionEnvelope`, which includes additional metadata such as the signers for a given transaction. Ultimately, this class handles signing and conversion to and from XDR for usage on Stellar's network. :param transaction: The transaction that is encapsulated in this envelope. :param list signatures: which contains a list of signatures that have already been created. :param network_passphrase: The network to connect to for verifying and retrieving additional attributes from. """ def __init__( self, transaction: Transaction, network_passphrase: str, signatures: Sequence[DecoratedSignature] = None, ) -> None: super().__init__(network_passphrase, signatures) self.transaction: Transaction = transaction
[docs] def signature_base(self) -> bytes: """Get the signature base of this transaction envelope. Return the "signature base" of this transaction, which is the value that, when hashed, should be signed to create a signature that validators on the Stellar Network will accept. It is composed of a 4 prefix bytes followed by the xdr-encoded form of this transaction. :return: The signature base of this transaction envelope. """ network_id = self._network_id tx = self.transaction if isinstance(self.transaction, Transaction) and not self.transaction.v1: tx = Transaction.from_xdr_object(self.transaction.to_xdr_object(), v1=False) tx.v1 = True packer = Packer() stellar_xdr.EnvelopeType.ENVELOPE_TYPE_TX.pack(packer) tx.to_xdr_object().pack(packer) return network_id + packer.get_buffer()
[docs] def sign_extra_signers_payload(self, signer: Union[Keypair, str]) -> None: """Sign this extra signers' payload with a given keypair. Note that the signature must not already be in this instance's list of signatures. :param signer: The keypair or secret to use for signing this extra signers' payload. :raise: :exc:`SignatureExistError <stellar_sdk.exception.SignatureExistError>`: if this signature already exists. """ if isinstance(signer, str): signer = Keypair.from_secret(signer) signer_account_id = signer.public_key if ( not self.transaction.preconditions or not self.transaction.preconditions.extra_signers ): return for extra_signer in self.transaction.preconditions.extra_signers: if ( extra_signer.signer_key_type != SignerKeyType.SIGNER_KEY_TYPE_ED25519_SIGNED_PAYLOAD ): continue signed_payload_signer_key = extra_signer.to_signed_payload_signer() if signed_payload_signer_key.account_id != signer_account_id: continue sig = signer.sign_payload_decorated(signed_payload_signer_key.payload) if sig in self.signatures: raise SignatureExistError("The keypair has already signed.") else: self.signatures.append(sig)
[docs] def to_xdr_object(self) -> stellar_xdr.TransactionEnvelope: """Get an XDR object representation of this :class:`TransactionEnvelope`. :return: XDR TransactionEnvelope object """ tx = self.transaction.to_xdr_object() signatures = [signature.to_xdr_object() for signature in self.signatures] if self.transaction.v1: assert isinstance(tx, stellar_xdr.Transaction) te_type = stellar_xdr.EnvelopeType.ENVELOPE_TYPE_TX tx_v1_envelope = stellar_xdr.TransactionV1Envelope(tx, signatures) return stellar_xdr.TransactionEnvelope(type=te_type, v1=tx_v1_envelope) else: assert isinstance(tx, stellar_xdr.TransactionV0) te_type = stellar_xdr.EnvelopeType.ENVELOPE_TYPE_TX_V0 tx_v0_envelope = stellar_xdr.TransactionV0Envelope(tx, signatures) return stellar_xdr.TransactionEnvelope(type=te_type, v0=tx_v0_envelope)
[docs] def to_transaction_envelope_v1(self) -> "TransactionEnvelope": """Create a new :class:`TransactionEnvelope`, if the internal tx is not v1, we will convert it to v1.""" tx = copy.deepcopy(self.transaction) tx.v1 = True return TransactionEnvelope(tx, self.network_passphrase, self.signatures)
[docs] @classmethod def from_xdr_object( cls, xdr_object: stellar_xdr.TransactionEnvelope, network_passphrase: str ) -> "TransactionEnvelope": """Create a new :class:`TransactionEnvelope` from an XDR object. :param xdr_object: The XDR object that represents a transaction envelope. :param network_passphrase: The network to connect to for verifying and retrieving additional attributes from. :return: A new :class:`TransactionEnvelope` object from the given XDR TransactionEnvelope object. """ te_type = xdr_object.type if te_type == stellar_xdr.EnvelopeType.ENVELOPE_TYPE_TX_V0: assert xdr_object.v0 is not None assert xdr_object.v0.signatures is not None tx = Transaction.from_xdr_object(xdr_object.v0.tx, v1=False) signatures = [ DecoratedSignature.from_xdr_object(s) for s in xdr_object.v0.signatures ] elif te_type == stellar_xdr.EnvelopeType.ENVELOPE_TYPE_TX: assert xdr_object.v1 is not None assert xdr_object.v1.signatures is not None tx = Transaction.from_xdr_object(xdr_object.v1.tx, v1=True) signatures = [ DecoratedSignature.from_xdr_object(s) for s in xdr_object.v1.signatures ] else: raise ValueError(f"Unexpected EnvelopeType: {xdr_object.type}.") te = cls(tx, network_passphrase=network_passphrase, signatures=signatures) return te
def __hash__(self): return hash((self.transaction, self.network_passphrase, self.signatures)) def __eq__(self, other: object) -> bool: if not isinstance(other, self.__class__): return NotImplemented return ( self.transaction == other.transaction and self.network_passphrase == other.network_passphrase and self.signatures == other.signatures ) def __str__(self): return ( f"<TransactionEnvelope [transaction={self.transaction}, " f"network_passphrase={self.network_passphrase}, signatures={self.signatures}]>" )