Source code for stellar_base.transaction_envelope

# coding: utf-8
import base64

from .keypair import Keypair
from .network import Network, NETWORKS
from .stellarxdr import Xdr
from .transaction import Transaction
from .utils import hashX_sign_decorated, xdr_hash
from .exceptions import SignatureExistError, PreimageLengthError


[docs]class TransactionEnvelope(object): """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 tx: The transaction that is encapsulated in this envelope. :type tx: :class:`Transaction <stellar_base.transaction.Transaction>` :param dict opts: Additional options, such as: - opts.signatures, which contains a list of signatures that have already been created. - opts.network_id, which contains the network ID for which network this transaction envelope is associated with. """ def __init__(self, tx, opts=None): self.tx = tx try: self.signatures = opts.get('signatures') or [] except AttributeError: self.signatures = [] network_id = opts.get('network_id') if network_id: if network_id in NETWORKS: passphrase = NETWORKS[network_id] else: passphrase = network_id else: passphrase = NETWORKS['TESTNET'] self.network_id = Network(passphrase).network_id()
[docs] def sign(self, keypair): """Sign this transaction envelope with a given keypair. Note that the signature must not already be in this instance's list of signatures. :param keypair: The keypair to use for signing this transaction envelope. :type keypair: :class:`Keypair <stellar_base.keypair.Keypair>` :raises: :exc:`SignatureExistError <stellar_base.utils.SignatureExistError>` """ assert isinstance(keypair, Keypair) tx_hash = self.hash_meta() sig = keypair.sign_decorated(tx_hash) sig_dict = [signature.__dict__ for signature in self.signatures] if sig.__dict__ in sig_dict: raise SignatureExistError('already signed') else:
self.signatures.append(sig)
[docs] def sign_hashX(self, preimage): """Sign this transaction envelope with a Hash(X) signature. See Stellar's documentation on `Multi-Sig <https://www.stellar.org/developers/guides/concepts/multi-sig.html>`_ for more details on Hash(x) signatures. :param str preimage: The "x" value to be hashed and used as a signature. """ if len(preimage) > 64: raise PreimageLengthError('preimage must <= 64 bytes') sig = hashX_sign_decorated(preimage) sig_dict = [signature.__dict__ for signature in self.signatures] if sig.__dict__ in sig_dict: raise SignatureExistError('already signed') else:
self.signatures.append(sig)
[docs] def hash_meta(self): """Get the XDR Hash of the signature base. This hash is ultimately what is signed before transactions are sent over the network. See :meth:`signature_base` for more details about this process. :return: The XDR Hash of this transaction envelope's signature base. """
return xdr_hash(self.signature_base())
[docs] def signature_base(self): """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_type = Xdr.StellarXDRPacker() tx_type.pack_EnvelopeType(Xdr.const.ENVELOPE_TYPE_TX) tx_type = tx_type.get_buffer() tx = Xdr.StellarXDRPacker() tx.pack_Transaction(self.tx.to_xdr_object()) tx = tx.get_buffer()
return network_id + tx_type + tx
[docs] def to_xdr_object(self): """Get an XDR object representation of this :class:`TransactionEnvelope`. """ tx = self.tx.to_xdr_object()
return Xdr.types.TransactionEnvelope(tx, self.signatures)
[docs] def xdr(self): """Get the base64 encoded XDR string representing this :class:`TransactionEnvelope`. """ te = Xdr.StellarXDRPacker() te.pack_TransactionEnvelope(self.to_xdr_object()) te = base64.b64encode(te.get_buffer())
return te # FIXME: can not get network id from XDR , default is 'TESTNET'
[docs] @classmethod def from_xdr(cls, xdr): """Create a new :class:`TransactionEnvelope` from an XDR string. :param bytes xdr: The XDR string that represents a transaction envelope. """ xdr_decoded = base64.b64decode(xdr) te = Xdr.StellarXDRUnpacker(xdr_decoded) te_xdr_object = te.unpack_TransactionEnvelope() signatures = te_xdr_object.signatures tx_xdr_object = te_xdr_object.tx tx = Transaction.from_xdr_object(tx_xdr_object) te = TransactionEnvelope(tx, {'signatures': signatures}) # te = TransactionEnvelope( # tx, {'signatures': signatures, 'network_id': 'PUBLIC'})
return te