Skip to main content

xrpl_wasm_stdlib/core/current_tx/
traits.rs

1//! # Transaction Field Access Traits
2//!
3//! This module defines traits for accessing fields from XRPL transactions in a type-safe manner.
4//! It provides a structured interface for retrieving both common transaction fields (shared across
5//! all transaction types) and transaction-specific fields (unique to particular transaction types).
6//!
7//! ## Overview
8//!
9//! XRPL transactions contain a variety of fields, some mandatory and others optional. This module
10//! organizes field access into logical groups:
11//!
12//! - **Common Fields**: Fields present in all XRPL transactions (Account, Fee, Sequence, etc.)
13//! - **Transaction-Specific Fields**: Fields unique to specific transaction types
14//!
15//! ## Design Philosophy
16//!
17//! The trait-based design provides several benefits:
18//!
19//! - **Type Safety**: Each field is accessed through methods with appropriate return types
20//! - **Composability**: Transaction types can implement multiple traits as needed
21//! - **Zero-Cost Abstraction**: Trait methods compile down to direct host function calls
22//! - **Extensibility**: New transaction types can easily implement the relevant traits
23//!
24//! ## Field Categories
25//!
26//! ### Mandatory vs. Optional Fields
27//!
28//! - **Mandatory fields** return `Result<T>` and will error if missing
29//! - **Optional fields** return `Result<Option<T>>` and return `None` if missing
30//!
31//! ### Field Types
32//!
33//! - **AccountID**: 20-byte account identifiers
34//! - **Hash256**: 256-bit cryptographic hashes
35//! - **Amount**: XRP amounts (with future support for tokens)
36//! - **u32**: 32-bit unsigned integers for sequence numbers, flags, etc.
37//! - **Blob**: Variable-length binary data
38//! - **PublicKey**: 33-byte compressed public keys
39//! - **TransactionType**: Enumerated transaction type identifiers
40
41use crate::core::current_tx::{get_field, get_field_optional};
42use crate::core::types::account_id::AccountID;
43use crate::core::types::amount::Amount;
44use crate::core::types::blob::{ConditionBlob, FulfillmentBlob, SignatureBlob};
45use crate::core::types::public_key::PublicKey;
46use crate::core::types::transaction_type::TransactionType;
47use crate::core::types::uint::Hash256;
48use crate::host::error_codes::match_result_code_optional;
49use crate::host::{Result, get_tx_field};
50use crate::sfield;
51
52/// Trait providing access to common fields present in all XRPL transactions.
53///
54/// ## Implementation Requirements
55///
56/// Types implementing this trait should ensure they are used only in the context of a valid
57/// XRPL transaction. The trait methods assume the current transaction context is properly
58/// established by the XRPL Programmability environment.
59pub trait TransactionCommonFields {
60    /// Retrieves the account field from the current transaction.
61    ///
62    /// This field identifies (Required) The unique address of the account that initiated the
63    /// transaction.
64    ///
65    /// # Returns
66    ///
67    /// Returns a `Result<AccountID>` where:
68    /// * `Ok(AccountID)` - The 20-byte account identifier of the transaction sender
69    /// * `Err(Error)` - If the field cannot be retrieved or has an unexpected size
70    fn get_account(&self) -> Result<AccountID> {
71        get_field(sfield::Account)
72    }
73
74    /// Retrieves the transaction type from the current transaction.
75    ///
76    /// This field specifies the type of transaction. Valid transaction types include:
77    /// Payment, OfferCreate, TrustSet, and many others.
78    ///
79    /// # Returns
80    ///
81    /// Returns a `Result<TransactionType>` where:
82    /// * `Ok(TransactionType)` - An enumerated value representing the transaction type
83    /// * `Err(Error)` - If the field cannot be retrieved or has an unexpected size
84    ///
85    fn get_transaction_type(&self) -> Result<TransactionType> {
86        get_field(sfield::TransactionType)
87    }
88
89    /// Retrieves the computation allowance from the current transaction.
90    ///
91    /// This field specifies the maximum computational resources that the transaction is
92    /// allowed to consume during execution in the XRPL Programmability environment.
93    /// It helps prevent runaway computations and ensures network stability.
94    ///
95    /// # Returns
96    ///
97    /// Returns a `Result<u32>` where:
98    /// * `Ok(u32)` - The computation allowance value in platform-defined units
99    /// * `Err(Error)` - If the field cannot be retrieved or has an unexpected size
100    fn get_computation_allowance(&self) -> Result<u32> {
101        get_field(sfield::ComputationAllowance)
102    }
103
104    /// Retrieves the fee amount from the current transaction.
105    ///
106    /// This field specifies the amount of XRP (in drops) that the sender is willing to pay
107    /// as a transaction fee. The fee is consumed regardless of whether the transaction
108    /// succeeds or fails, and higher fees can improve transaction priority during
109    /// network congestion.
110    ///
111    /// # Returns
112    ///
113    /// Returns a `Result<Amount>` where:
114    /// * `Ok(Amount)` - The fee amount as an XRP amount in drops
115    /// * `Err(Error)` - If the field cannot be retrieved or has an unexpected size
116    ///
117    /// # Note
118    ///
119    /// Returns XRP amounts only (for now). Future versions may support other token types
120    /// when the underlying amount handling is enhanced.
121    fn get_fee(&self) -> Result<Amount> {
122        get_field(sfield::Fee)
123    }
124
125    /// Retrieves the sequence number from the current transaction.
126    ///
127    /// This field represents the sequence number of the account sending the transaction. A
128    /// transaction is only valid if the Sequence number is exactly 1 greater than the previous
129    /// transaction from the same account. The special case 0 means the transaction is using a
130    /// Ticket instead (Added by the TicketBatch amendment).
131    ///
132    /// # Returns
133    ///
134    /// Returns a `Result<u32>` where:
135    /// * `Ok(u32)` - The transaction sequence number
136    /// * `Err(Error)` - If the field cannot be retrieved or has an unexpected size
137    ///
138    /// # Note
139    ///
140    /// If the transaction uses tickets instead of sequence numbers, this field may not
141    /// be present. In such cases, use `get_ticket_sequence()` instead.
142    fn get_sequence(&self) -> Result<u32> {
143        get_field(sfield::Sequence)
144    }
145
146    /// Retrieves the account transaction ID from the current transaction.
147    ///
148    /// This optional field contains the hash value identifying another transaction. If provided,
149    /// this transaction is only valid if the sending account's previously sent transaction matches
150    /// the provided hash.
151    ///
152    /// # Returns
153    ///
154    /// Returns a `Result<Option<Hash256>>` where:
155    /// * `Ok(Some(Hash256))` - The hash of the required previous transaction
156    /// * `Ok(None)` - If no previous transaction requirement is specified
157    /// * `Err(Error)` - If an error occurred during field retrieval
158    fn get_account_txn_id(&self) -> Result<Option<Hash256>> {
159        get_field_optional(sfield::AccountTxnID)
160    }
161
162    /// Retrieves the `flags` field from the current transaction.
163    ///
164    /// This optional field contains a bitfield of transaction-specific flags that modify
165    /// the transaction's behavior.
166    ///
167    /// # Returns
168    ///
169    /// Returns a `Result<Option<u32>>` where:
170    /// * `Ok(Some(u32))` - The flags bitfield if present
171    /// * `Ok(None)` - If no flags are specified (equivalent to flags = 0)
172    /// * `Err(Error)` - If an error occurred during field retrieval
173    fn get_flags(&self) -> Result<Option<u32>> {
174        get_field_optional(sfield::Flags)
175    }
176
177    /// Retrieves the last ledger sequence from the current transaction.
178    ///
179    /// This optional field specifies the highest ledger index this transaction can appear in.
180    /// Specifying this field places a strict upper limit on how long the transaction can wait to
181    /// be validated or rejected. See Reliable Transaction Submission for more details.
182    ///
183    /// # Returns
184    ///
185    /// Returns a `Result<Option<u32>>` where:
186    /// * `Ok(Some(u32))` - The maximum ledger index for transaction inclusion
187    /// * `Ok(None)` - If no expiration is specified (transaction never expires)
188    /// * `Err(Error)` - If an error occurred during field retrieval
189    fn get_last_ledger_sequence(&self) -> Result<Option<u32>> {
190        get_field_optional(sfield::LastLedgerSequence)
191    }
192
193    /// Retrieves the network ID from the current transaction.
194    ///
195    /// This optional field identifies the network ID of the chain this transaction is intended for.
196    /// MUST BE OMITTED for Mainnet and some test networks. REQUIRED on chains whose network ID is
197    /// 1025 or higher.
198    ///
199    /// # Returns
200    ///
201    /// Returns a `Result<Option<u32>>` where:
202    /// * `Ok(Some(u32))` - The network identifier
203    /// * `Ok(None)` - If no specific network is specified (uses default network)
204    /// * `Err(Error)` - If an error occurred during field retrieval
205    fn get_network_id(&self) -> Result<Option<u32>> {
206        get_field_optional(sfield::NetworkID)
207    }
208
209    /// Retrieves the source tag from the current transaction.
210    ///
211    /// This optional field is an arbitrary integer used to identify the reason for this payment, or
212    /// a sender on whose behalf this transaction is made. Conventionally, a refund should specify
213    /// the initial payment's SourceTag as the refund payment's DestinationTag.
214    ///
215    /// # Returns
216    ///
217    /// Returns a `Result<Option<u32>>` where:
218    /// * `Ok(Some(u32))` - The source tag identifier
219    /// * `Ok(None)` - If no source tag is specified
220    /// * `Err(Error)` - If an error occurred during field retrieval
221    fn get_source_tag(&self) -> Result<Option<u32>> {
222        get_field_optional(sfield::SourceTag)
223    }
224
225    /// Retrieves the signing public key from the current transaction.
226    ///
227    /// This field contains the hex representation of the public key that corresponds to the
228    /// private key used to sign this transaction. If an empty string, this field indicates that a
229    /// multi-signature is present in the Signers field instead.
230    ///
231    /// # Returns
232    ///
233    /// Returns a `Result<PublicKey>` where:
234    /// * `Ok(PublicKey)` - The 33-byte compressed public key used for signing
235    /// * `Err(Error)` - If the field cannot be retrieved or has an unexpected size
236    ///
237    /// # Security Note
238    ///
239    /// The presence of this field doesn't guarantee the signature is valid. Instead, this field
240    /// only provides the key claimed to be used for signing. The XRPL network performs signature
241    /// validation before transaction execution.
242    fn get_signing_pub_key(&self) -> Result<PublicKey> {
243        get_field(sfield::SigningPubKey)
244    }
245
246    /// Retrieves the ticket sequence from the current transaction.
247    ///
248    /// This optional field provides the sequence number of the ticket to use in place of a
249    /// Sequence number. If this is provided, Sequence must be 0. Cannot be used with AccountTxnID.
250    ///
251    /// # Returns
252    ///
253    /// Returns a `Result<Option<u32>>` where:
254    /// * `Ok(Some(u32))` - The ticket sequence number if the transaction uses tickets
255    /// * `Ok(None)` - If the transaction uses traditional sequence numbering
256    /// * `Err(Error)` - If an error occurred during field retrieval
257    ///
258    /// # Note
259    ///
260    /// Transactions use either `Sequence` or `TicketSequence`, but not both. Check this
261    /// field when `get_sequence()` fails or when implementing ticket-aware logic.
262    fn get_ticket_sequence(&self) -> Result<Option<u32>> {
263        get_field_optional(sfield::TicketSequence)
264    }
265
266    /// Retrieves the transaction signature from the current transaction.
267    ///
268    /// This mandatory field contains the signature that verifies this transaction as originating
269    /// from the account it says it is from.
270    ///
271    /// Signatures can be either:
272    /// - 64 bytes for EdDSA (Ed25519) signatures
273    /// - 70-72 bytes for ECDSA (secp256k1) signatures
274    ///
275    /// # Returns
276    ///
277    /// Returns a `Result<Signature>` where:
278    /// * `Ok(Signature)` - The transaction signature (up to 72 bytes)
279    /// * `Err(Error)` - If the field cannot be retrieved
280    ///
281    /// # Security Note
282    ///
283    /// The signature is validated by the XRPL network before transaction execution.
284    /// In the programmability context, you can access the signature for logging or
285    /// analysis purposes, but signature validation has already been performed.
286    fn get_txn_signature(&self) -> Result<SignatureBlob> {
287        get_field(sfield::TxnSignature)
288    }
289}
290
291/// Trait providing access to fields specific to EscrowFinish transactions.
292///
293/// This trait extends `TransactionCommonFields` with methods for retrieving fields that are
294/// unique to EscrowFinish transactions. EscrowFinish transactions are used to complete
295/// time-based or condition-based escrows that were previously created with EscrowCreate
296/// transactions.
297///
298/// ## Implementation Requirements
299///
300/// Types implementing this trait should:
301/// - Also implement `TransactionCommonFields` for access to common transaction fields
302/// - Only be used in the context of processing EscrowFinish transactions
303/// - Ensure proper error handling when accessing conditional fields
304pub trait EscrowFinishFields: TransactionCommonFields {
305    /// Retrieves the owner account from the current EscrowFinish transaction.
306    ///
307    /// This mandatory field identifies the XRPL account that originally created the escrow
308    /// with an EscrowCreate transaction. The owner is the account that deposited the XRP
309    /// into the escrow and specified the conditions for its release.
310    ///
311    /// # Returns
312    ///
313    /// Returns a `Result<AccountID>` where:
314    /// * `Ok(AccountID)` - The 20-byte account identifier of the escrow owner
315    /// * `Err(Error)` - If the field cannot be retrieved or has an unexpected size
316    fn get_owner(&self) -> Result<AccountID> {
317        get_field(sfield::Owner)
318    }
319
320    /// Retrieves the offer sequence from the current EscrowFinish transaction.
321    ///
322    /// This mandatory field specifies the sequence number of the original EscrowCreate
323    /// transaction that created the escrow being finished. This creates a unique reference
324    /// to the specific escrow object, as escrows are identified by the combination of
325    /// the owner account and the sequence number of the creating transaction.
326    ///
327    /// # Returns
328    ///
329    /// Returns a `Result<u32>` where:
330    /// * `Ok(u32)` - The sequence number of the EscrowCreate transaction
331    /// * `Err(Error)` - If the field cannot be retrieved or has an unexpected size
332    fn get_offer_sequence(&self) -> Result<u32> {
333        get_field(sfield::OfferSequence)
334    }
335
336    /// Retrieves the cryptographic condition from the current EscrowFinish transaction.
337    ///
338    /// This optional field contains the cryptographic condition in full crypto-condition format.
339    /// For PREIMAGE-SHA-256 conditions, this is 39 bytes:
340    /// - 2 bytes: type tag (A025)
341    /// - 2 bytes: fingerprint length tag (8020)
342    /// - 32 bytes: SHA-256 hash (fingerprint)
343    /// - 2 bytes: cost length tag (8101)
344    /// - 1 byte: cost value (00)
345    ///
346    /// # Returns
347    ///
348    /// Returns a `Result<Option<Condition>>` where:
349    /// * `Ok(Some(Condition))` - The full crypto-condition if the escrow is conditional
350    /// * `Ok(None)` - If the escrow has no cryptographic condition (time-based only)
351    /// * `Err(Error)` - If an error occurred during field retrieval
352    fn get_condition(&self) -> Result<Option<ConditionBlob>> {
353        let mut buffer = ConditionBlob::new();
354        let result_code = unsafe {
355            get_tx_field(
356                sfield::Condition.into(),
357                buffer.data.as_mut_ptr(),
358                buffer.capacity(),
359            )
360        };
361        match_result_code_optional(result_code, || {
362            buffer.len = result_code as usize;
363            (result_code > 0).then_some(buffer)
364        })
365    }
366
367    /// Retrieves the cryptographic fulfillment from the current EscrowFinish transaction.
368    ///
369    /// This optional field contains the cryptographic fulfillment that satisfies the condition
370    /// specified in the original EscrowCreate transaction. The fulfillment must cryptographically
371    /// prove that the condition's requirements have been met. This field is only required
372    /// when the escrow has an associated condition.
373    ///
374    /// # Returns
375    ///
376    /// Returns a `Result<Option<Fulfillment>>` where:
377    /// * `Ok(Some(Fulfillment))` - The fulfillment data if provided
378    /// * `Ok(None)` - If no fulfillment is provided (valid for unconditional escrows)
379    /// * `Err(Error)` - If an error occurred during field retrieval
380    ///
381    /// # Fulfillment Validation
382    ///
383    /// The XRPL network automatically validates that:
384    /// - The fulfillment satisfies the escrow's condition
385    /// - The fulfillment is properly formatted according to RFC 3814
386    /// - The cryptographic proof is mathematically valid
387    ///
388    /// # Size Limits
389    ///
390    /// Fulfillments are limited to 256 bytes in the current XRPL implementation.
391    /// This limit ensures network performance while supporting the most practical
392    /// cryptographic proof scenarios.
393    fn get_fulfillment(&self) -> Result<Option<FulfillmentBlob>> {
394        let mut buffer = FulfillmentBlob::new();
395        let result_code = unsafe {
396            get_tx_field(
397                sfield::Fulfillment.into(),
398                buffer.data.as_mut_ptr(),
399                buffer.capacity(),
400            )
401        };
402        match_result_code_optional(result_code, || {
403            buffer.len = result_code as usize;
404            (result_code > 0).then_some(buffer)
405        })
406    }
407}
408
409#[cfg(test)]
410mod tests {
411
412    use crate::host::host_bindings_trait::MockHostBindings;
413    use crate::sfield::SField;
414    use mockall::predicate::{always, eq};
415    // ========================================
416    // Test helper functions
417    // ========================================
418
419    /// Helper to set up a mock expectation for get_tx_field
420    fn expect_tx_field<T: Send + std::fmt::Debug + PartialEq + 'static, const CODE: i32>(
421        mock: &mut MockHostBindings,
422        field: SField<T, CODE>,
423        size: usize,
424        times: usize,
425    ) {
426        mock.expect_get_tx_field()
427            .with(eq(field), always(), eq(size))
428            .times(times)
429            .returning(move |_, _, _| size as i32);
430    }
431
432    mod escrow_finish_fields {
433
434        mod optional_fields {
435            use crate::core::current_tx::escrow_finish::EscrowFinish;
436            use crate::core::current_tx::traits::EscrowFinishFields;
437            use crate::core::current_tx::traits::tests::expect_tx_field;
438            use crate::core::types::blob::{CONDITION_BLOB_SIZE, FULFILLMENT_BLOB_SIZE};
439            use crate::host::error_codes::{FIELD_NOT_FOUND, INTERNAL_ERROR, INVALID_FIELD};
440            use crate::host::host_bindings_trait::MockHostBindings;
441            use crate::host::setup_mock;
442            use crate::sfield;
443
444            use crate::sfield::{Condition, Fulfillment};
445            use mockall::predicate::{always, eq};
446
447            #[test]
448            fn test_optional_fields_return_some() {
449                let mut mock = MockHostBindings::new();
450
451                // get_condition
452                expect_tx_field(&mut mock, Condition, CONDITION_BLOB_SIZE, 1);
453                // get_fulfillment
454                expect_tx_field(&mut mock, Fulfillment, FULFILLMENT_BLOB_SIZE, 1);
455
456                let _guard = setup_mock(mock);
457
458                let escrow = EscrowFinish;
459
460                // All optional fields should return Ok(Some(...))
461                let condition = escrow.get_condition().unwrap();
462                assert!(condition.is_some());
463                assert_eq!(condition.unwrap().len, CONDITION_BLOB_SIZE);
464
465                let fulfillment = escrow.get_fulfillment().unwrap();
466                assert!(fulfillment.is_some());
467                assert_eq!(fulfillment.unwrap().len, FULFILLMENT_BLOB_SIZE);
468            }
469
470            #[test]
471            fn test_optional_fields_return_none_when_zero_length() {
472                let mut mock = MockHostBindings::new();
473
474                // get_condition - returns None when result code is 0
475                mock.expect_get_tx_field()
476                    .with(eq(sfield::Condition), always(), eq(CONDITION_BLOB_SIZE))
477                    .times(1)
478                    .returning(|_, _, _| 0);
479                // get_fulfillment - returns None when result code is 0
480                mock.expect_get_tx_field()
481                    .with(eq(sfield::Fulfillment), always(), eq(FULFILLMENT_BLOB_SIZE))
482                    .times(1)
483                    .returning(|_, _, _| 0);
484
485                let _guard = setup_mock(mock);
486
487                let escrow = EscrowFinish;
488
489                // Variable-size optional fields return None when result code is 0 (not present)
490                assert!(escrow.get_condition().unwrap().is_none());
491                assert!(escrow.get_fulfillment().unwrap().is_none());
492            }
493
494            #[test]
495            fn test_optional_fields_return_error_on_internal_error() {
496                let mut mock = MockHostBindings::new();
497
498                // get_condition
499                mock.expect_get_tx_field()
500                    .with(eq(sfield::Condition), always(), eq(CONDITION_BLOB_SIZE))
501                    .times(1)
502                    .returning(|_, _, _| INTERNAL_ERROR);
503                // get_fulfillment
504                mock.expect_get_tx_field()
505                    .with(eq(sfield::Fulfillment), always(), eq(FULFILLMENT_BLOB_SIZE))
506                    .times(1)
507                    .returning(|_, _, _| INTERNAL_ERROR);
508
509                let _guard = setup_mock(mock);
510
511                let escrow = EscrowFinish;
512
513                // Optional fields should also return Err on INTERNAL_ERROR
514                let condition_result = escrow.get_condition();
515                assert!(condition_result.is_err());
516                assert_eq!(condition_result.err().unwrap().code(), INTERNAL_ERROR);
517
518                let fulfillment_result = escrow.get_fulfillment();
519                assert!(fulfillment_result.is_err());
520                assert_eq!(fulfillment_result.err().unwrap().code(), INTERNAL_ERROR);
521            }
522
523            #[test]
524            fn test_optional_fields_return_error_on_field_not_found() {
525                let mut mock = MockHostBindings::new();
526
527                // get_condition
528                mock.expect_get_tx_field()
529                    .with(eq(Condition), always(), eq(CONDITION_BLOB_SIZE))
530                    .times(1)
531                    .returning(|_, _, _| FIELD_NOT_FOUND);
532                // get_fulfillment
533                mock.expect_get_tx_field()
534                    .with(eq(Fulfillment), always(), eq(FULFILLMENT_BLOB_SIZE))
535                    .times(1)
536                    .returning(|_, _, _| FIELD_NOT_FOUND);
537
538                let _guard = setup_mock(mock);
539
540                let escrow = EscrowFinish;
541
542                // Optional fields return Err on FIELD_NOT_FOUND (not None)
543                let condition_result = escrow.get_condition();
544                assert!(condition_result.is_err());
545                assert_eq!(condition_result.err().unwrap().code(), FIELD_NOT_FOUND);
546
547                let fulfillment_result = escrow.get_fulfillment();
548                assert!(fulfillment_result.is_err());
549                assert_eq!(fulfillment_result.err().unwrap().code(), FIELD_NOT_FOUND);
550            }
551
552            #[test]
553            fn test_optional_fields_return_error_on_invalid_field() {
554                let mut mock = MockHostBindings::new();
555
556                // get_condition
557                mock.expect_get_tx_field()
558                    .with(eq(sfield::Condition), always(), eq(CONDITION_BLOB_SIZE))
559                    .times(1)
560                    .returning(|_, _, _| INVALID_FIELD);
561                // get_fulfillment
562                mock.expect_get_tx_field()
563                    .with(eq(sfield::Fulfillment), always(), eq(FULFILLMENT_BLOB_SIZE))
564                    .times(1)
565                    .returning(|_, _, _| INVALID_FIELD);
566
567                let _guard = setup_mock(mock);
568
569                let escrow = EscrowFinish;
570
571                // Optional fields should also return Err on INVALID_FIELD
572                let condition_result = escrow.get_condition();
573                assert!(condition_result.is_err());
574                assert_eq!(condition_result.err().unwrap().code(), INVALID_FIELD);
575
576                let fulfillment_result = escrow.get_fulfillment();
577                assert!(fulfillment_result.is_err());
578                assert_eq!(fulfillment_result.err().unwrap().code(), INVALID_FIELD);
579            }
580        }
581
582        mod mandatory_fields {
583            use crate::core::current_tx::escrow_finish::EscrowFinish;
584            use crate::core::current_tx::traits::EscrowFinishFields;
585            use crate::core::current_tx::traits::tests::expect_tx_field;
586            use crate::core::types::account_id::ACCOUNT_ID_SIZE;
587            use crate::host::error_codes::{FIELD_NOT_FOUND, INTERNAL_ERROR, INVALID_FIELD};
588            use crate::host::host_bindings_trait::MockHostBindings;
589            use crate::host::setup_mock;
590            use crate::sfield;
591            use mockall::predicate::{always, eq};
592
593            #[test]
594            fn test_mandatory_fields_return_ok() {
595                let mut mock = MockHostBindings::new();
596
597                // get_owner
598                expect_tx_field(&mut mock, sfield::Owner, ACCOUNT_ID_SIZE, 1);
599                // get_offer_sequence
600                expect_tx_field(&mut mock, sfield::OfferSequence, 4, 1);
601
602                let _guard = setup_mock(mock);
603
604                let escrow = EscrowFinish;
605
606                // All mandatory fields should return Ok
607                assert!(escrow.get_owner().is_ok());
608                assert!(escrow.get_offer_sequence().is_ok());
609            }
610
611            #[test]
612            fn test_mandatory_fields_return_error_when_zero_length() {
613                let mut mock = MockHostBindings::new();
614
615                // get_owner - returns 0 (zero length)
616                mock.expect_get_tx_field()
617                    .with(eq(sfield::Owner), always(), eq(ACCOUNT_ID_SIZE))
618                    .times(1)
619                    .returning(|_, _, _| 0);
620                // get_offer_sequence - returns 0 (zero length)
621                mock.expect_get_tx_field()
622                    .with(eq(sfield::OfferSequence), always(), eq(4))
623                    .times(1)
624                    .returning(|_, _, _| 0);
625
626                let _guard = setup_mock(mock);
627
628                let escrow = EscrowFinish;
629
630                // Mandatory fields should return Err when zero length (INTERNAL_ERROR due to byte mismatch)
631                let owner_result = escrow.get_owner();
632                assert!(owner_result.is_err());
633
634                let offer_seq_result = escrow.get_offer_sequence();
635                assert!(offer_seq_result.is_err());
636            }
637
638            #[test]
639            fn test_mandatory_fields_return_error_on_field_not_found() {
640                let mut mock = MockHostBindings::new();
641
642                // get_owner
643                mock.expect_get_tx_field()
644                    .with(eq(sfield::Owner), always(), eq(ACCOUNT_ID_SIZE))
645                    .times(1)
646                    .returning(|_, _, _| FIELD_NOT_FOUND);
647                // get_offer_sequence
648                mock.expect_get_tx_field()
649                    .with(eq(sfield::OfferSequence), always(), eq(4))
650                    .times(1)
651                    .returning(|_, _, _| FIELD_NOT_FOUND);
652
653                let _guard = setup_mock(mock);
654
655                let escrow = EscrowFinish;
656
657                // All mandatory fields should return Err on FIELD_NOT_FOUND
658                let owner_result = escrow.get_owner();
659                assert!(owner_result.is_err());
660                assert_eq!(owner_result.err().unwrap().code(), FIELD_NOT_FOUND);
661
662                let offer_seq_result = escrow.get_offer_sequence();
663                assert!(offer_seq_result.is_err());
664                assert_eq!(offer_seq_result.err().unwrap().code(), FIELD_NOT_FOUND);
665            }
666
667            #[test]
668            fn test_mandatory_fields_return_error_on_internal_error() {
669                let mut mock = MockHostBindings::new();
670
671                // get_owner
672                mock.expect_get_tx_field()
673                    .with(eq(sfield::Owner), always(), eq(ACCOUNT_ID_SIZE))
674                    .times(1)
675                    .returning(|_, _, _| INTERNAL_ERROR);
676                // get_offer_sequence
677                mock.expect_get_tx_field()
678                    .with(eq(sfield::OfferSequence), always(), eq(4))
679                    .times(1)
680                    .returning(|_, _, _| INTERNAL_ERROR);
681
682                let _guard = setup_mock(mock);
683
684                let escrow = EscrowFinish;
685
686                // All mandatory fields should return Err on INTERNAL_ERROR
687                let owner_result = escrow.get_owner();
688                assert!(owner_result.is_err());
689                assert_eq!(owner_result.err().unwrap().code(), INTERNAL_ERROR);
690
691                let offer_seq_result = escrow.get_offer_sequence();
692                assert!(offer_seq_result.is_err());
693                assert_eq!(offer_seq_result.err().unwrap().code(), INTERNAL_ERROR);
694            }
695
696            #[test]
697            fn test_mandatory_fields_return_error_on_invalid_field() {
698                let mut mock = MockHostBindings::new();
699
700                // get_owner
701                mock.expect_get_tx_field()
702                    .with(eq(sfield::Owner), always(), eq(ACCOUNT_ID_SIZE))
703                    .times(1)
704                    .returning(|_, _, _| INVALID_FIELD);
705                // get_offer_sequence
706                mock.expect_get_tx_field()
707                    .with(eq(sfield::OfferSequence), always(), eq(4))
708                    .times(1)
709                    .returning(|_, _, _| INVALID_FIELD);
710
711                let _guard = setup_mock(mock);
712
713                let escrow = EscrowFinish;
714
715                // All mandatory fields should return Err on INVALID_FIELD
716                let owner_result = escrow.get_owner();
717                assert!(owner_result.is_err());
718                assert_eq!(owner_result.err().unwrap().code(), INVALID_FIELD);
719
720                let offer_seq_result = escrow.get_offer_sequence();
721                assert!(offer_seq_result.is_err());
722                assert_eq!(offer_seq_result.err().unwrap().code(), INVALID_FIELD);
723            }
724        }
725    }
726
727    mod transaction_common_fields {
728
729        mod optional_fields {
730            use crate::core::current_tx::escrow_finish::EscrowFinish;
731            use crate::core::current_tx::traits::TransactionCommonFields;
732            use crate::core::current_tx::traits::tests::expect_tx_field;
733            use crate::core::types::uint::HASH256_SIZE;
734            use crate::host::error_codes::{FIELD_NOT_FOUND, INTERNAL_ERROR, INVALID_FIELD};
735            use crate::host::host_bindings_trait::MockHostBindings;
736            use crate::host::setup_mock;
737            use crate::sfield;
738            use mockall::predicate::{always, eq};
739
740            #[test]
741            fn test_optional_fields_return_some() {
742                let mut mock = MockHostBindings::new();
743
744                // get_account_txn_id
745                expect_tx_field(&mut mock, sfield::AccountTxnID, HASH256_SIZE, 1);
746                // get_flags
747                expect_tx_field(&mut mock, sfield::Flags, 4, 1);
748                // get_last_ledger_sequence
749                expect_tx_field(&mut mock, sfield::LastLedgerSequence, 4, 1);
750                // get_network_id
751                expect_tx_field(&mut mock, sfield::NetworkID, 4, 1);
752                // get_source_tag
753                expect_tx_field(&mut mock, sfield::SourceTag, 4, 1);
754                // get_ticket_sequence
755                expect_tx_field(&mut mock, sfield::TicketSequence, 4, 1);
756
757                let _guard = setup_mock(mock);
758
759                let tx = EscrowFinish;
760
761                // All optional fields should return Ok(Some(...))
762                assert!(tx.get_account_txn_id().unwrap().is_some());
763                assert!(tx.get_flags().unwrap().is_some());
764                assert!(tx.get_last_ledger_sequence().unwrap().is_some());
765                assert!(tx.get_network_id().unwrap().is_some());
766                assert!(tx.get_source_tag().unwrap().is_some());
767                assert!(tx.get_ticket_sequence().unwrap().is_some());
768            }
769
770            #[test]
771            fn test_optional_fields_return_none_when_field_not_found() {
772                let mut mock = MockHostBindings::new();
773
774                // get_account_txn_id
775                mock.expect_get_tx_field()
776                    .with(eq(sfield::AccountTxnID), always(), eq(HASH256_SIZE))
777                    .times(1)
778                    .returning(|_, _, _| FIELD_NOT_FOUND);
779                // get_flags
780                mock.expect_get_tx_field()
781                    .with(eq(sfield::Flags), always(), eq(4))
782                    .times(1)
783                    .returning(|_, _, _| FIELD_NOT_FOUND);
784                // get_last_ledger_sequence
785                mock.expect_get_tx_field()
786                    .with(eq(sfield::LastLedgerSequence), always(), eq(4))
787                    .times(1)
788                    .returning(|_, _, _| FIELD_NOT_FOUND);
789                // get_network_id
790                mock.expect_get_tx_field()
791                    .with(eq(sfield::NetworkID), always(), eq(4))
792                    .times(1)
793                    .returning(|_, _, _| FIELD_NOT_FOUND);
794                // get_source_tag
795                mock.expect_get_tx_field()
796                    .with(eq(sfield::SourceTag), always(), eq(4))
797                    .times(1)
798                    .returning(|_, _, _| FIELD_NOT_FOUND);
799                // get_ticket_sequence
800                mock.expect_get_tx_field()
801                    .with(eq(sfield::TicketSequence), always(), eq(4))
802                    .times(1)
803                    .returning(|_, _, _| FIELD_NOT_FOUND);
804
805                let _guard = setup_mock(mock);
806
807                let tx = EscrowFinish;
808
809                // Fixed-size optional fields should return Ok(None) when FIELD_NOT_FOUND
810                assert!(tx.get_account_txn_id().unwrap().is_none());
811                assert!(tx.get_flags().unwrap().is_none());
812                assert!(tx.get_last_ledger_sequence().unwrap().is_none());
813                assert!(tx.get_network_id().unwrap().is_none());
814                assert!(tx.get_source_tag().unwrap().is_none());
815                assert!(tx.get_ticket_sequence().unwrap().is_none());
816            }
817
818            #[test]
819            fn test_optional_fields_return_none_when_zero_length() {
820                let mut mock = MockHostBindings::new();
821
822                // get_account_txn_id - returns 0 (zero length)
823                mock.expect_get_tx_field()
824                    .with(eq(sfield::AccountTxnID), always(), eq(HASH256_SIZE))
825                    .times(1)
826                    .returning(|_, _, _| 0);
827                // get_flags - returns 0 (zero length)
828                mock.expect_get_tx_field()
829                    .with(eq(sfield::Flags), always(), eq(4))
830                    .times(1)
831                    .returning(|_, _, _| 0);
832                // get_last_ledger_sequence - returns 0 (zero length)
833                mock.expect_get_tx_field()
834                    .with(eq(sfield::LastLedgerSequence), always(), eq(4))
835                    .times(1)
836                    .returning(|_, _, _| 0);
837                // get_network_id - returns 0 (zero length)
838                mock.expect_get_tx_field()
839                    .with(eq(sfield::NetworkID), always(), eq(4))
840                    .times(1)
841                    .returning(|_, _, _| 0);
842                // get_source_tag - returns 0 (zero length)
843                mock.expect_get_tx_field()
844                    .with(eq(sfield::SourceTag), always(), eq(4))
845                    .times(1)
846                    .returning(|_, _, _| 0);
847                // get_ticket_sequence - returns 0 (zero length)
848                mock.expect_get_tx_field()
849                    .with(eq(sfield::TicketSequence), always(), eq(4))
850                    .times(1)
851                    .returning(|_, _, _| 0);
852
853                // Mock trace_num calls (2 calls per field for byte mismatch: expected + actual)
854                mock.expect_trace_num()
855                    .with(always(), always(), always())
856                    .returning(|_, _, _| 0)
857                    .times(12); // 6 fields * 2 calls each
858
859                let _guard = setup_mock(mock);
860
861                let tx = EscrowFinish;
862
863                // Fixed-size optional fields should return Err when zero length (byte mismatch)
864                assert!(tx.get_account_txn_id().is_err());
865                assert!(tx.get_flags().is_err());
866                assert!(tx.get_last_ledger_sequence().is_err());
867                assert!(tx.get_network_id().is_err());
868                assert!(tx.get_source_tag().is_err());
869                assert!(tx.get_ticket_sequence().is_err());
870            }
871
872            #[test]
873            fn test_optional_fields_return_error_on_internal_error() {
874                let mut mock = MockHostBindings::new();
875
876                // get_account_txn_id
877                mock.expect_get_tx_field()
878                    .with(eq(sfield::AccountTxnID), always(), eq(HASH256_SIZE))
879                    .times(1)
880                    .returning(|_, _, _| INTERNAL_ERROR);
881                // get_flags
882                mock.expect_get_tx_field()
883                    .with(eq(sfield::Flags), always(), eq(4))
884                    .times(1)
885                    .returning(|_, _, _| INTERNAL_ERROR);
886                // get_last_ledger_sequence
887                mock.expect_get_tx_field()
888                    .with(eq(sfield::LastLedgerSequence), always(), eq(4))
889                    .times(1)
890                    .returning(|_, _, _| INTERNAL_ERROR);
891                // get_network_id
892                mock.expect_get_tx_field()
893                    .with(eq(sfield::NetworkID), always(), eq(4))
894                    .times(1)
895                    .returning(|_, _, _| INTERNAL_ERROR);
896                // get_source_tag
897                mock.expect_get_tx_field()
898                    .with(eq(sfield::SourceTag), always(), eq(4))
899                    .times(1)
900                    .returning(|_, _, _| INTERNAL_ERROR);
901                // get_ticket_sequence
902                mock.expect_get_tx_field()
903                    .with(eq(sfield::TicketSequence), always(), eq(4))
904                    .times(1)
905                    .returning(|_, _, _| INTERNAL_ERROR);
906
907                // Mock trace_num calls (1 call per field for error codes)
908                mock.expect_trace_num()
909                    .with(always(), always(), always())
910                    .returning(|_, _, _| 0)
911                    .times(6); // 6 fields * 1 call each
912
913                let _guard = setup_mock(mock);
914
915                let tx = EscrowFinish;
916
917                // Optional fields should return Err on INTERNAL_ERROR
918                let account_txn_id_result = tx.get_account_txn_id();
919                assert!(account_txn_id_result.is_err());
920                assert_eq!(account_txn_id_result.err().unwrap().code(), INTERNAL_ERROR);
921
922                let flags_result = tx.get_flags();
923                assert!(flags_result.is_err());
924                assert_eq!(flags_result.err().unwrap().code(), INTERNAL_ERROR);
925
926                let last_ledger_seq_result = tx.get_last_ledger_sequence();
927                assert!(last_ledger_seq_result.is_err());
928                assert_eq!(last_ledger_seq_result.err().unwrap().code(), INTERNAL_ERROR);
929
930                let network_id_result = tx.get_network_id();
931                assert!(network_id_result.is_err());
932                assert_eq!(network_id_result.err().unwrap().code(), INTERNAL_ERROR);
933
934                let source_tag_result = tx.get_source_tag();
935                assert!(source_tag_result.is_err());
936                assert_eq!(source_tag_result.err().unwrap().code(), INTERNAL_ERROR);
937
938                let ticket_seq_result = tx.get_ticket_sequence();
939                assert!(ticket_seq_result.is_err());
940                assert_eq!(ticket_seq_result.err().unwrap().code(), INTERNAL_ERROR);
941            }
942
943            #[test]
944            fn test_optional_fields_return_error_on_invalid_field() {
945                let mut mock = MockHostBindings::new();
946
947                // get_account_txn_id
948                mock.expect_get_tx_field()
949                    .with(eq(sfield::AccountTxnID), always(), eq(HASH256_SIZE))
950                    .times(1)
951                    .returning(|_, _, _| INVALID_FIELD);
952                // get_flags
953                mock.expect_get_tx_field()
954                    .with(eq(sfield::Flags), always(), eq(4))
955                    .times(1)
956                    .returning(|_, _, _| INVALID_FIELD);
957                // get_last_ledger_sequence
958                mock.expect_get_tx_field()
959                    .with(eq(sfield::LastLedgerSequence), always(), eq(4))
960                    .times(1)
961                    .returning(|_, _, _| INVALID_FIELD);
962                // get_network_id
963                mock.expect_get_tx_field()
964                    .with(eq(sfield::NetworkID), always(), eq(4))
965                    .times(1)
966                    .returning(|_, _, _| INVALID_FIELD);
967                // get_source_tag
968                mock.expect_get_tx_field()
969                    .with(eq(sfield::SourceTag), always(), eq(4))
970                    .times(1)
971                    .returning(|_, _, _| INVALID_FIELD);
972                // get_ticket_sequence
973                mock.expect_get_tx_field()
974                    .with(eq(sfield::TicketSequence), always(), eq(4))
975                    .times(1)
976                    .returning(|_, _, _| INVALID_FIELD);
977
978                // Mock trace_num calls (1 call per field for error codes)
979                mock.expect_trace_num()
980                    .with(always(), always(), always())
981                    .returning(|_, _, _| 0)
982                    .times(6); // 6 fields * 1 call each
983
984                let _guard = setup_mock(mock);
985
986                let tx = EscrowFinish;
987
988                // Optional fields should return Err on INVALID_FIELD
989                let account_txn_id_result = tx.get_account_txn_id();
990                assert!(account_txn_id_result.is_err());
991                assert_eq!(account_txn_id_result.err().unwrap().code(), INVALID_FIELD);
992
993                let flags_result = tx.get_flags();
994                assert!(flags_result.is_err());
995                assert_eq!(flags_result.err().unwrap().code(), INVALID_FIELD);
996
997                let last_ledger_seq_result = tx.get_last_ledger_sequence();
998                assert!(last_ledger_seq_result.is_err());
999                assert_eq!(last_ledger_seq_result.err().unwrap().code(), INVALID_FIELD);
1000
1001                let network_id_result = tx.get_network_id();
1002                assert!(network_id_result.is_err());
1003                assert_eq!(network_id_result.err().unwrap().code(), INVALID_FIELD);
1004
1005                let source_tag_result = tx.get_source_tag();
1006                assert!(source_tag_result.is_err());
1007                assert_eq!(source_tag_result.err().unwrap().code(), INVALID_FIELD);
1008
1009                let ticket_seq_result = tx.get_ticket_sequence();
1010                assert!(ticket_seq_result.is_err());
1011                assert_eq!(ticket_seq_result.err().unwrap().code(), INVALID_FIELD);
1012            }
1013        }
1014
1015        mod required_fields {
1016            use crate::core::current_tx::escrow_finish::EscrowFinish;
1017            use crate::core::current_tx::traits::TransactionCommonFields;
1018            use crate::core::current_tx::traits::tests::expect_tx_field;
1019            use crate::core::types::account_id::ACCOUNT_ID_SIZE;
1020            use crate::core::types::amount::AMOUNT_SIZE;
1021            use crate::core::types::blob::SIGNATURE_BLOB_SIZE;
1022            use crate::core::types::public_key::PUBLIC_KEY_BUFFER_SIZE;
1023            use crate::host::error_codes::{FIELD_NOT_FOUND, INTERNAL_ERROR, INVALID_FIELD};
1024            use crate::host::host_bindings_trait::MockHostBindings;
1025            use crate::host::setup_mock;
1026            use crate::sfield;
1027            use mockall::predicate::{always, eq};
1028
1029            #[test]
1030            fn test_mandatory_fields_return_ok() {
1031                let mut mock = MockHostBindings::new();
1032
1033                // get_account
1034                expect_tx_field(&mut mock, sfield::Account, ACCOUNT_ID_SIZE, 1);
1035                // get_transaction_type
1036                expect_tx_field(&mut mock, sfield::TransactionType, 2, 1);
1037                // get_computation_allowance
1038                expect_tx_field(&mut mock, sfield::ComputationAllowance, 4, 1);
1039                // get_fee
1040                expect_tx_field(&mut mock, sfield::Fee, AMOUNT_SIZE, 1);
1041                // get_sequence
1042                expect_tx_field(&mut mock, sfield::Sequence, 4, 1);
1043                // get_signing_pub_key
1044                expect_tx_field(&mut mock, sfield::SigningPubKey, PUBLIC_KEY_BUFFER_SIZE, 1);
1045                // get_txn_signature
1046                expect_tx_field(&mut mock, sfield::TxnSignature, SIGNATURE_BLOB_SIZE, 1);
1047
1048                let _guard = setup_mock(mock);
1049
1050                let tx = EscrowFinish;
1051
1052                // All mandatory fields should return Ok
1053                assert!(tx.get_account().is_ok());
1054                assert!(tx.get_transaction_type().is_ok());
1055                assert!(tx.get_computation_allowance().is_ok());
1056                assert!(tx.get_fee().is_ok());
1057                assert!(tx.get_sequence().is_ok());
1058                assert!(tx.get_signing_pub_key().is_ok());
1059                assert!(tx.get_txn_signature().is_ok());
1060            }
1061
1062            #[test]
1063            fn test_mandatory_fields_return_error_when_zero_length() {
1064                let mut mock = MockHostBindings::new();
1065
1066                // get_account - returns 0 (zero length)
1067                mock.expect_get_tx_field()
1068                    .with(eq(sfield::Account), always(), eq(ACCOUNT_ID_SIZE))
1069                    .times(1)
1070                    .returning(|_, _, _| 0);
1071                // get_transaction_type - returns 0 (zero length)
1072                mock.expect_get_tx_field()
1073                    .with(eq(sfield::TransactionType), always(), eq(2))
1074                    .times(1)
1075                    .returning(|_, _, _| 0);
1076                // get_computation_allowance - returns 0 (zero length)
1077                mock.expect_get_tx_field()
1078                    .with(eq(sfield::ComputationAllowance), always(), eq(4))
1079                    .times(1)
1080                    .returning(|_, _, _| 0);
1081                // get_fee - returns 0 (zero length)
1082                mock.expect_get_tx_field()
1083                    .with(eq(sfield::Fee), always(), eq(AMOUNT_SIZE))
1084                    .times(1)
1085                    .returning(|_, _, _| 0);
1086                // get_sequence - returns 0 (zero length)
1087                mock.expect_get_tx_field()
1088                    .with(eq(sfield::Sequence), always(), eq(4))
1089                    .times(1)
1090                    .returning(|_, _, _| 0);
1091                // get_signing_pub_key - returns 0 (zero length)
1092                mock.expect_get_tx_field()
1093                    .with(
1094                        eq(sfield::SigningPubKey),
1095                        always(),
1096                        eq(PUBLIC_KEY_BUFFER_SIZE),
1097                    )
1098                    .times(1)
1099                    .returning(|_, _, _| 0);
1100
1101                let _guard = setup_mock(mock);
1102
1103                let tx = EscrowFinish;
1104
1105                // Fixed-size mandatory fields should return Err when zero length (byte mismatch)
1106                let account_result = tx.get_account();
1107                assert!(account_result.is_err());
1108
1109                let tx_type_result = tx.get_transaction_type();
1110                assert!(tx_type_result.is_err());
1111
1112                let comp_allow_result = tx.get_computation_allowance();
1113                assert!(comp_allow_result.is_err());
1114
1115                // Variable-size field (Amount) returns Ok with zero length
1116                let fee_result = tx.get_fee();
1117                assert!(fee_result.is_ok());
1118
1119                let seq_result = tx.get_sequence();
1120                assert!(seq_result.is_err());
1121
1122                let signing_key_result = tx.get_signing_pub_key();
1123                assert!(signing_key_result.is_err());
1124            }
1125
1126            #[test]
1127            fn test_mandatory_fields_return_error_on_field_not_found() {
1128                let mut mock = MockHostBindings::new();
1129
1130                // get_account
1131                mock.expect_get_tx_field()
1132                    .with(eq(sfield::Account), always(), eq(ACCOUNT_ID_SIZE))
1133                    .times(1)
1134                    .returning(|_, _, _| FIELD_NOT_FOUND);
1135                // get_transaction_type
1136                mock.expect_get_tx_field()
1137                    .with(eq(sfield::TransactionType), always(), eq(2))
1138                    .times(1)
1139                    .returning(|_, _, _| FIELD_NOT_FOUND);
1140                // get_computation_allowance
1141                mock.expect_get_tx_field()
1142                    .with(eq(sfield::ComputationAllowance), always(), eq(4))
1143                    .times(1)
1144                    .returning(|_, _, _| FIELD_NOT_FOUND);
1145                // get_fee
1146                mock.expect_get_tx_field()
1147                    .with(eq(sfield::Fee), always(), eq(AMOUNT_SIZE))
1148                    .times(1)
1149                    .returning(|_, _, _| FIELD_NOT_FOUND);
1150                // get_sequence
1151                mock.expect_get_tx_field()
1152                    .with(eq(sfield::Sequence), always(), eq(4))
1153                    .times(1)
1154                    .returning(|_, _, _| FIELD_NOT_FOUND);
1155                // get_signing_pub_key
1156                mock.expect_get_tx_field()
1157                    .with(
1158                        eq(sfield::SigningPubKey),
1159                        always(),
1160                        eq(PUBLIC_KEY_BUFFER_SIZE),
1161                    )
1162                    .times(1)
1163                    .returning(|_, _, _| FIELD_NOT_FOUND);
1164
1165                let _guard = setup_mock(mock);
1166
1167                let tx = EscrowFinish;
1168
1169                // All mandatory fields should return Err on FIELD_NOT_FOUND
1170                let account_result = tx.get_account();
1171                assert!(account_result.is_err());
1172                assert_eq!(account_result.err().unwrap().code(), FIELD_NOT_FOUND);
1173
1174                let tx_type_result = tx.get_transaction_type();
1175                assert!(tx_type_result.is_err());
1176                assert_eq!(tx_type_result.err().unwrap().code(), FIELD_NOT_FOUND);
1177
1178                let comp_allow_result = tx.get_computation_allowance();
1179                assert!(comp_allow_result.is_err());
1180                assert_eq!(comp_allow_result.err().unwrap().code(), FIELD_NOT_FOUND);
1181
1182                let fee_result = tx.get_fee();
1183                assert!(fee_result.is_err());
1184                assert_eq!(fee_result.err().unwrap().code(), FIELD_NOT_FOUND);
1185
1186                let seq_result = tx.get_sequence();
1187                assert!(seq_result.is_err());
1188                assert_eq!(seq_result.err().unwrap().code(), FIELD_NOT_FOUND);
1189
1190                let signing_key_result = tx.get_signing_pub_key();
1191                assert!(signing_key_result.is_err());
1192                assert_eq!(signing_key_result.err().unwrap().code(), FIELD_NOT_FOUND);
1193            }
1194
1195            #[test]
1196            fn test_mandatory_fields_return_error_on_internal_error() {
1197                let mut mock = MockHostBindings::new();
1198
1199                // get_account
1200                mock.expect_get_tx_field()
1201                    .with(eq(sfield::Account), always(), eq(ACCOUNT_ID_SIZE))
1202                    .times(1)
1203                    .returning(|_, _, _| INTERNAL_ERROR);
1204                // get_transaction_type
1205                mock.expect_get_tx_field()
1206                    .with(eq(sfield::TransactionType), always(), eq(2))
1207                    .times(1)
1208                    .returning(|_, _, _| INTERNAL_ERROR);
1209                // get_computation_allowance
1210                mock.expect_get_tx_field()
1211                    .with(eq(sfield::ComputationAllowance), always(), eq(4))
1212                    .times(1)
1213                    .returning(|_, _, _| INTERNAL_ERROR);
1214                // get_fee
1215                mock.expect_get_tx_field()
1216                    .with(eq(sfield::Fee), always(), eq(AMOUNT_SIZE))
1217                    .times(1)
1218                    .returning(|_, _, _| INTERNAL_ERROR);
1219                // get_sequence
1220                mock.expect_get_tx_field()
1221                    .with(eq(sfield::Sequence), always(), eq(4))
1222                    .times(1)
1223                    .returning(|_, _, _| INTERNAL_ERROR);
1224                // get_signing_pub_key
1225                mock.expect_get_tx_field()
1226                    .with(
1227                        eq(sfield::SigningPubKey),
1228                        always(),
1229                        eq(PUBLIC_KEY_BUFFER_SIZE),
1230                    )
1231                    .times(1)
1232                    .returning(|_, _, _| INTERNAL_ERROR);
1233
1234                let _guard = setup_mock(mock);
1235
1236                let tx = EscrowFinish;
1237
1238                // All mandatory fields should return Err on INTERNAL_ERROR
1239                let account_result = tx.get_account();
1240                assert!(account_result.is_err());
1241                assert_eq!(account_result.err().unwrap().code(), INTERNAL_ERROR);
1242
1243                let tx_type_result = tx.get_transaction_type();
1244                assert!(tx_type_result.is_err());
1245                assert_eq!(tx_type_result.err().unwrap().code(), INTERNAL_ERROR);
1246
1247                let comp_allow_result = tx.get_computation_allowance();
1248                assert!(comp_allow_result.is_err());
1249                assert_eq!(comp_allow_result.err().unwrap().code(), INTERNAL_ERROR);
1250
1251                let fee_result = tx.get_fee();
1252                assert!(fee_result.is_err());
1253                assert_eq!(fee_result.err().unwrap().code(), INTERNAL_ERROR);
1254
1255                let seq_result = tx.get_sequence();
1256                assert!(seq_result.is_err());
1257                assert_eq!(seq_result.err().unwrap().code(), INTERNAL_ERROR);
1258
1259                let signing_key_result = tx.get_signing_pub_key();
1260                assert!(signing_key_result.is_err());
1261                assert_eq!(signing_key_result.err().unwrap().code(), INTERNAL_ERROR);
1262            }
1263
1264            #[test]
1265            fn test_mandatory_fields_return_error_on_invalid_field() {
1266                let mut mock = MockHostBindings::new();
1267
1268                // get_account
1269                mock.expect_get_tx_field()
1270                    .with(eq(sfield::Account), always(), eq(ACCOUNT_ID_SIZE))
1271                    .times(1)
1272                    .returning(|_, _, _| INVALID_FIELD);
1273                // get_transaction_type
1274                mock.expect_get_tx_field()
1275                    .with(eq(sfield::TransactionType), always(), eq(2))
1276                    .times(1)
1277                    .returning(|_, _, _| INVALID_FIELD);
1278                // get_computation_allowance
1279                mock.expect_get_tx_field()
1280                    .with(eq(sfield::ComputationAllowance), always(), eq(4))
1281                    .times(1)
1282                    .returning(|_, _, _| INVALID_FIELD);
1283                // get_fee
1284                mock.expect_get_tx_field()
1285                    .with(eq(sfield::Fee), always(), eq(AMOUNT_SIZE))
1286                    .times(1)
1287                    .returning(|_, _, _| INVALID_FIELD);
1288                // get_sequence
1289                mock.expect_get_tx_field()
1290                    .with(eq(sfield::Sequence), always(), eq(4))
1291                    .times(1)
1292                    .returning(|_, _, _| INVALID_FIELD);
1293                // get_signing_pub_key
1294                mock.expect_get_tx_field()
1295                    .with(
1296                        eq(sfield::SigningPubKey),
1297                        always(),
1298                        eq(PUBLIC_KEY_BUFFER_SIZE),
1299                    )
1300                    .times(1)
1301                    .returning(|_, _, _| INVALID_FIELD);
1302
1303                let _guard = setup_mock(mock);
1304
1305                let tx = EscrowFinish;
1306
1307                // All mandatory fields should return Err on INVALID_FIELD
1308                let account_result = tx.get_account();
1309                assert!(account_result.is_err());
1310                assert_eq!(account_result.err().unwrap().code(), INVALID_FIELD);
1311
1312                let tx_type_result = tx.get_transaction_type();
1313                assert!(tx_type_result.is_err());
1314                assert_eq!(tx_type_result.err().unwrap().code(), INVALID_FIELD);
1315
1316                let comp_allow_result = tx.get_computation_allowance();
1317                assert!(comp_allow_result.is_err());
1318                assert_eq!(comp_allow_result.err().unwrap().code(), INVALID_FIELD);
1319
1320                let fee_result = tx.get_fee();
1321                assert!(fee_result.is_err());
1322                assert_eq!(fee_result.err().unwrap().code(), INVALID_FIELD);
1323
1324                let seq_result = tx.get_sequence();
1325                assert!(seq_result.is_err());
1326                assert_eq!(seq_result.err().unwrap().code(), INVALID_FIELD);
1327
1328                let signing_key_result = tx.get_signing_pub_key();
1329                assert!(signing_key_result.is_err());
1330                assert_eq!(signing_key_result.err().unwrap().code(), INVALID_FIELD);
1331            }
1332        }
1333    }
1334}