Skip to main content

thruput_xdr/
transaction.rs

1// -*-Rust-*-
2// Generated Code: DO NOT CHECK IN
3
4// Generated from xdrc commit: 6ab835b07c234abb4b0b50b61d687904727a4f32
5
6#![allow(non_camel_case_types)]
7#![allow(non_snake_case)]
8#![allow(non_upper_case_globals)]
9#![allow(unused_imports)]
10#![allow(dead_code)]
11#![allow(unused_variables)]
12#![allow(clippy::unnecessary_cast)]
13
14use crate::*;
15use core::option::Option;
16use core::ops::{Deref, DerefMut};
17
18#[cfg(feature = "std")]
19use std::io::{Read, Write};
20#[cfg(feature = "xdrs")]
21use xdrs::*;
22
23
24#[cfg(feature = "serde")]
25use ::serde::{{Serialize, Deserialize}};
26
27
28
29// clang-format on
30
31//==============================================================================
32// Transactions and Invocations
33//
34// This module defines the transaction model for Thruput. Transactions are
35// the fundamental unit of state modification in the system.
36//
37// Key concepts:
38// - Each transaction invokes a contract method with specified parameters
39// - Transactions include authorization claims and signatures
40// - Transactions can deploy new contracts
41// - Thruput executes transactions in parallel, using two-phase commit for conflict resolution
42// - Proposers attach nondeterministic data to ensure deterministic re-execution
43//
44// Transaction lifecycle:
45// 1. User constructs a Transaction with invocation details, auth claims, etc.
46// 2. Signatures are added to create a SignedTransaction
47// 3. Transaction is submitted to the network
48// 4. A proposer executes the transaction and captures nondeterministic data
49// 5. Proposer creates a ProposedTransaction and includes it in a block proposal
50// 6. Validators re-execute using the nondeterministic data to verify correctness
51//==============================================================================
52
53//------------------------------------------------------------------------------
54// Call Data and Return Values
55//------------------------------------------------------------------------------
56
57/// Opaque encoded call arguments passed to a contract invocation.
58///
59/// Contracts can encode calldata arbitrarily, but the SDKs encode a tuple or
60/// struct of the method's parameters in CXS (compact XDR serialization), a
61/// variant of RFC 4506 XDR without 4-byte padding and with variable-length
62/// integers, lengths and discriminants.
63pub type Calldata = BytesM;
64
65/// Opaque encoded return value from a contract invocation.
66///
67/// Like Calldata, contracts can encode return values arbitrarily, but the
68/// SDKs use CXS.
69///
70/// Unbounded here, as `Calldata` is: what a chain permits is stated by
71/// `kConfigParamMaxReturnBufferLen` and enforced where the Return syscall
72/// writes one, rather than where one is decoded. Nothing decodes a buffer
73/// the local executor did not just build, so a bound here would constrain
74/// what a configuration may state without guarding anything.
75pub type ReturnBuffer = BytesM;
76
77//------------------------------------------------------------------------------
78// Invocations
79//------------------------------------------------------------------------------
80
81/// Specifies a contract method invocation with its parameters.
82///
83/// This struct is used both for the root transaction invocation and for
84/// subinvocations made via the Invoke syscall.
85#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
86#[cfg_attr(feature = "xdrs", derive(ReadXDR, WriteXDR, ConstXDRSize))]
87#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
88#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
89#[repr(C)]
90pub struct Invocation {
91    /// Address of the contract to invoke.
92    pub invoked_address: ContractAddress,
93    /// 8-byte identifier of the contract method to call.
94    ///
95    /// See [`ContractEntrypointID`] in contract.x for how these are derived.
96    pub entrypoint: ContractEntrypointID,
97    /// Maximum gas units available for this invocation.
98    ///
99    /// For the root transaction invocation, this is the total gas budget.
100    /// For subinvocations via the Invoke syscall, this is capped at the
101    /// remaining gas in the caller's frame. The SDK default is UINT64_MAX,
102    /// meaning "use all remaining gas".
103    pub gas_limit: GasAmount,
104    /// Encoded arguments to pass to the contract method.
105    pub calldata: Calldata,
106}
107
108#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
109#[cfg_attr(feature = "serde", serde(transparent))]
110#[cfg_attr(feature = "xdrs", derive(ReadXDR, WriteXDR, ConstXDRSize))]
111#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
112#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
113#[derive(Copy)]
114#[repr(transparent)]
115pub struct InvocationFrameStatusCode(pub i32);
116
117impl InvocationFrameStatusCode {
118    pub const Return: Self = Self(0);
119    pub const Fault: Self = Self(1);
120    pub const Abort: Self = Self(2);
121}
122impl From<i32> for InvocationFrameStatusCode {
123    fn from(v: i32) -> Self {
124        Self(v)
125    }
126}
127#[cfg(feature = "std")]
128impl core::fmt::Display for InvocationFrameStatusCode {
129    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
130        match self.0 {
131            x if x == Self::Return.0 => {
132                write!(f, "InvocationFrameStatusCode::Return")
133            },
134            x if x == Self::Fault.0 => {
135                write!(f, "InvocationFrameStatusCode::Fault")
136            },
137            x if x == Self::Abort.0 => {
138                write!(f, "InvocationFrameStatusCode::Abort")
139            },
140            _ => {
141                write!(f, "InvocationFrameStatusCode({})", self.0)
142            }
143        }
144    }
145}
146
147
148#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
149#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
150#[cfg_attr(feature = "xdrs", derive(ReadXDR, WriteXDR, ConstXDRSize))]
151#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
152#[repr(i32)]
153#[non_exhaustive]
154pub enum InvocationFrameOutput {
155    // case Return:
156    //   ReturnBuffer return_buffer;
157    Return { return_buffer: ReturnBuffer } = InvocationFrameStatusCode::Return.0 as i32,
158    // case Abort:
159    //   ReturnBuffer abort_buffer;
160    Abort { abort_buffer: ReturnBuffer } = InvocationFrameStatusCode::Abort.0 as i32,
161    // case Fault:
162    //   void;
163    Fault = InvocationFrameStatusCode::Fault.0 as i32,
164    
165}
166
167//------------------------------------------------------------------------------
168// Transaction Uniqueness and Replay Protection
169//------------------------------------------------------------------------------
170
171/// Ensures transaction uniqueness and prevents replay attacks.
172///
173/// Each transaction must include a uniquifier with two components:
174/// 1. An expiry height - the transaction becomes invalid after this block
175/// 2. A nonce - ensures uniqueness even if the same operation is repeated
176///
177/// The uniquifier is stored in the replay cache (see [`StorageKeyType::ReplayCache`]
178/// in types.x) at the invoked contract's address to prevent replays.
179///
180/// Important: The replay cache considers the (nonce, block_expiry_height) pair as the
181/// unique identifier. Two transactions with the same nonce but different expiry heights
182/// do NOT conflict with each other. For example, (nonce=1, expiry=100) and (nonce=1, expiry=200)
183/// can both be valid transactions.
184#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
185#[cfg_attr(feature = "xdrs", derive(ReadXDR, WriteXDR, ConstXDRSize))]
186#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
187#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
188#[repr(C)]
189pub struct Uniquifier {
190    /// The block height after which this transaction becomes invalid.
191    ///
192    /// Transactions submitted after this height will be rejected. This prevents
193    /// old transactions from being replayed indefinitely.
194    ///
195    /// Note: This is part of the uniqueness check - the pair (nonce, block_expiry_height)
196    /// must be unique, not just the nonce alone.
197    pub block_expiry_height: BlockHeight,
198    /// A 64-bit value chosen by the transaction creator.
199    ///
200    /// Combined with block_expiry_height and the invoked address, this ensures each
201    /// transaction is globally unique. The nonce can be chosen arbitrarily (random,
202    /// sequential, timestamp-based, etc.) as long as the (nonce, block_expiry_height)
203    /// pair hasn't been used before for this address.
204    pub nonce: u64,
205}
206
207//------------------------------------------------------------------------------
208// Transactions
209//------------------------------------------------------------------------------
210
211/// A complete transaction that can be executed on the Thruput blockchain.
212///
213/// Transactions are the fundamental unit of state modification. Each transaction:
214/// - Invokes a single contract method (which may make subinvocations)
215/// - Includes claims about attached signatures, which must be verified before execution
216///   can begin.  A transaction is invalid if any attached signatures are invalid
217///   or if the attached signatures do not match the auth claims.
218/// - Can deploy new contracts that will be available during execution
219/// - Must have a [`Uniquifier`] that has never before been used for this address
220#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
221#[cfg_attr(feature = "xdrs", derive(ReadXDR, WriteXDR, ConstXDRSize))]
222#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
223#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
224#[repr(C)]
225pub struct Transaction {
226    /// The root contract invocation for this transaction.
227    pub invocation: Invocation,
228    /// Uniqueness and replay protection data.
229    pub uniquifier: Uniquifier,
230    /// Contracts made available for deployment during this transaction.
231    ///
232    /// Attaching a contract does not deploy it: nothing reaches the chain
233    /// unless a frame deploys it with the DeployContract syscall, which can then
234    /// be invoked for the rest of the transaction. Contracts whose code is
235    /// already on-chain do not need to be included here.
236    pub contracts_to_deploy: VecM<Contract>,
237    /// Authorization claim specifying required signatures.
238    ///
239    /// See [`AuthClaim`] in auth_claim.x for details.
240    pub auth_claim: AuthClaim,
241}
242
243//------------------------------------------------------------------------------
244// Signatures and Signed Transactions
245//------------------------------------------------------------------------------
246
247/// A cryptographic signature paired with its key identifier.
248///
249/// The public_key_id indicates which key from an address's key set produced
250/// this signature, allowing the signature to be matched against the transaction's
251/// auth claims.
252#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
253#[cfg_attr(feature = "xdrs", derive(ReadXDR, WriteXDR, ConstXDRSize))]
254#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
255#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
256#[repr(C)]
257pub struct DecoratedSignature {
258    /// The KeyID of the public key that created this signature.
259    ///
260    /// This is matched against the auth claims to determine if the signature
261    /// satisfies the transaction's authorization requirements. Keys are
262    /// registered and weighted in [`AddressAuthData`] (address_auth.x); see
263    /// [`AuthClaim`] in auth_claim.x for how a claim selects them.
264    // uint32_t
265    pub public_key_id: u32,
266    /// The cryptographic signature itself.
267    ///
268    /// See [`Signature`] in types.x for the union that supports different schemes.
269    pub signature: Signature,
270}
271
272/// Hash of a transaction body, the identifier users, events and indexers
273/// know a transaction by.
274///
275/// This is the SHA-256 hash of the XDR-encoded [`Transaction`] -- the same
276/// encoding the signatures cover (see [`SignedTransaction`]). It deliberately covers neither the
277/// signatures wrapped around it by [`SignedTransaction`] nor the
278/// nondeterministic data attached to it by [`ProposedTransaction`]:
279///
280///  - Excluding the signatures keeps the identifier fixed however the body
281///    is signed, so a submitter can look up its transaction without knowing
282///    which signing a block carried.
283///  - Excluding the proposal data keeps the identifier computable by the
284///    submitter at signing time. Nondeterministic proposal data does not
285///    exist until a proposer has executed the transaction, so an identifier
286///    covering it could not be used to look up one's own transaction.
287///
288/// Proposals do not name transactions by this hash: they commit to each
289/// transaction's exact signed encoding by [`SignedTransactionHash`].
290///
291/// Uniqueness across blocks rests on replay protection -- the
292/// [`Uniquifier`] every [`Transaction`] carries -- rather than on anything
293/// in the preimage: two distinct commits can never share a hash, because
294/// the same [`Transaction`] cannot commit twice.
295pub type TransactionHash = SHA256Hash;
296
297/// Hash of one exact signed encoding of a transaction.
298///
299/// This is the SHA-256 hash of the canonical XDR of a [`SignedTransaction`]:
300/// the [`Transaction`], its signatures and its `pubkey_claim_key`. Two
301/// signings of one body share a [`TransactionHash`] but not a
302/// [`SignedTransactionHash`].
303///
304/// Proposals name their transactions by this hash, so every validator
305/// executes the encoding the proposer chose rather than whichever signing of
306/// the body it happens to hold. A validator that holds a different signing
307/// treats the transaction as missing and fetches the proposer's. It is not a
308/// lookup key for users: re-signing a body changes it.
309pub type SignedTransactionHash = SHA256Hash;
310
311/// A transaction with cryptographic signatures.
312///
313/// This is the form in which transactions are transmitted and stored.
314/// Before execution, the signatures are verified against the auth claim.
315///
316/// Each signature is over
317/// `cxs(DomainSeparator("Transaction")) || network_hash || xdr(tx)`: the
318/// domain separator keeps it from being reinterpreted as a signature over
319/// another object, and the network hash binds it to one network.
320#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
321#[cfg_attr(feature = "xdrs", derive(ReadXDR, WriteXDR, ConstXDRSize))]
322#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
323#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
324#[repr(C)]
325pub struct SignedTransaction {
326    /// The transaction to execute.
327    pub tx: Transaction,
328    /// Signatures that authorize this transaction.
329    ///
330    /// These signatures must satisfy the auth_claim in the transaction.
331    /// See [`SignedTransaction`] for the message they sign.
332    pub signatures: VecM<DecoratedSignature>,
333    /// The public key a `kPubkeySignature` auth claim names by hash.
334    ///
335    /// Present exactly when the auth claim is `kPubkeySignature`, and then its
336    /// [`PublicKeyHash`] must equal the claim's `pubkey_preimage.pubkey_hash`.
337    /// It sits outside the signed [`Transaction`] because the claim already
338    /// commits to it by hash; keeping it out of the claim keeps the claim that
339    /// contracts read the same size for every scheme.
340    pub pubkey_claim_key: Option<PublicKey>,
341}
342
343//------------------------------------------------------------------------------
344// Batch Types
345//------------------------------------------------------------------------------
346
347/// A batch of transactions (without signatures).
348pub type TransactionBatch = VecM::<Transaction>;
349
350/// A batch of signed transactions.
351pub type SignedTransactionBatch = VecM::<SignedTransaction>;
352
353/// An optional signed transaction (pointer may be null).
354pub type OptSignedTransaction = Option::<SignedTransaction>;
355
356/// A batch of optional signed transactions.
357pub type OptSignedTransactionBatch = VecM::<OptSignedTransaction>;
358
359/// A batch of transaction hashes.
360pub type TransactionHashBatch = VecM::<TransactionHash>;
361
362/// A batch of signed transaction hashes.
363pub type SignedTransactionHashBatch = VecM::<SignedTransactionHash>;
364
365//------------------------------------------------------------------------------
366// Proposed Transactions and Nondeterministic Data
367//------------------------------------------------------------------------------
368
369/// Nondeterministic data captured during transaction execution by the proposer.
370///
371/// Thruput executes transactions in parallel, which introduces nondeterminism
372/// (e.g., from two-phase commit conflict resolution). To ensure validators can deterministically
373/// re-execute transactions, the proposer captures all nondeterministic decisions
374/// and includes them in the block proposal.
375///
376/// This allows validators to replay the exact same execution path as the proposer,
377/// ensuring consensus on the transaction result.
378#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
379#[cfg_attr(feature = "xdrs", derive(ReadXDR, WriteXDR, ConstXDRSize))]
380#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
381#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
382#[repr(C)]
383pub struct NondeterministicProposalData {
384    /// A bitstring of nondeterministic decisions made during execution.
385    ///
386    /// This includes:
387    /// - Two-phase commit conflict resolution results from try_invoke frames (one boolean
388    ///   per try_invoke frame that succeeds and attempts lock acquisition)
389    /// - Other nondeterministic outcomes that must be consistent across validators
390    ///
391    /// Each bit represents a binary decision point encountered during execution.
392    /// The bits are consumed sequentially as the transaction re-executes.
393    pub nondeterministic_bits: BytesM,
394    /// The length of nondeterministic_bits in bits (not bytes).
395    ///
396    /// Since nondeterministic_bits.size() returns the rounded-up length in bytes,
397    /// this field tracks the exact bit count to avoid trailing garbage bits.
398    pub nondeterministic_bit_count: u32,
399    /// Retrocausal queue entries inserted by concurrent transactions in the same
400    /// block that this transaction observed, so validators replay the same reads.
401    ///
402    /// An entry is recorded when a peek or consume reads a concurrent entry.
403    /// Reads of entries from past blocks or inserted by this transaction itself
404    /// record nothing. Entries appear in the order they were first observed
405    /// during execution, interleaved across queues; within one queue they are
406    /// that queue's concurrent entries in index order, starting at 0. Each
407    /// (queue, index) is recorded once however many times it is read, so
408    /// repeated peeks, or a peek followed by a consume, add a single entry.
409    /// Whether each concurrent read found an entry is recorded separately, one
410    /// bit per read, in nondeterministic_bits.
411    pub observed_queue_entry_sequence: VecM<QueueEntry, kMaxConcurrentRcqReadsPerTx>,
412}
413
414/// A signed transaction with nondeterministic execution data attached.
415///
416/// This is the form in which transactions are included in block proposals.
417/// The proposer:
418/// 1. Executes the signed transaction
419/// 2. Captures all nondeterministic decisions (two-phase commit results, queue entries, etc.)
420/// 3. Packages both into a ProposedTransaction
421/// 4. Includes it in the proposed block
422///
423/// Validators then re-execute using the nondeterministic data to verify
424/// the proposer's execution was correct.
425#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
426#[cfg_attr(feature = "xdrs", derive(ReadXDR, WriteXDR, ConstXDRSize))]
427#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
428#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
429#[repr(C)]
430pub struct ProposedTransaction {
431    /// The signed transaction to execute.
432    pub signed_tx: SignedTransaction,
433    /// Nondeterministic data captured during the proposer's execution.
434    ///
435    /// Validators use this to deterministically replay the same execution path.
436    pub nondeterministic_proposal_data: NondeterministicProposalData,
437}
438
439pub type ProposedTransactionBatch = VecM::<ProposedTransaction>;
440
441//------------------------------------------------------------------------------
442// Transaction Statistics
443//------------------------------------------------------------------------------
444
445/// Transaction execution statistics available via the GetTransactionStatistics syscall.
446///
447/// These statistics provide runtime information about transaction execution,
448/// primarily for debugging and monitoring. Some statistics may be disabled
449/// in production builds.
450#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
451#[cfg_attr(feature = "serde", serde(transparent))]
452#[cfg_attr(feature = "xdrs", derive(ReadXDR, WriteXDR, ConstXDRSize))]
453#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
454#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
455#[derive(Copy)]
456#[repr(transparent)]
457pub struct TransactionStatistic(pub i32);
458
459impl TransactionStatistic {
460    /// The amount of gas remaining in the current call frame.
461    ///
462    /// This can be queried to monitor gas consumption during execution.
463    /// Note that this statistic may be disabled or return nondeterministic
464    /// values in test configurations (see syscalls.yaml for details).
465    pub const GasRemaining: Self = Self(0);
466}
467impl From<i32> for TransactionStatistic {
468    fn from(v: i32) -> Self {
469        Self(v)
470    }
471}
472#[cfg(feature = "std")]
473impl core::fmt::Display for TransactionStatistic {
474    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
475        match self.0 {
476            x if x == Self::GasRemaining.0 => {
477                write!(f, "TransactionStatistic::GasRemaining")
478            },
479            _ => {
480                write!(f, "TransactionStatistic({})", self.0)
481            }
482        }
483    }
484}
485
486
487
488//
489// XDR internals below this point
490//
491
492impl InvocationFrameOutput {
493    #[allow(clippy::match_single_binding)]
494    pub fn status_code(&self) -> InvocationFrameStatusCode {
495        match self {
496            
497            _ => (unsafe { *(self as *const Self as *const i32) }).into(),
498        }
499    }
500}
501
502