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<Option<PublicKey>>` where:
234    /// * `Ok(Some(PublicKey))` - The 33-byte compressed public key for single-signature transactions
235    /// * `Ok(None)` - Empty SigningPubKey field, indicating a multi-signature transaction
236    /// * `Err(Error)` - If the field cannot be retrieved or has an unexpected size (not 0 or 33 bytes)
237    ///
238    /// # Security Note
239    ///
240    /// The presence of this field doesn't guarantee the signature is valid. Instead, this field
241    /// only provides the key claimed to be used for signing. The XRPL network performs signature
242    /// validation before transaction execution.
243    fn get_signing_pub_key(&self) -> Result<Option<PublicKey>> {
244        get_field(sfield::SigningPubKey).and_then(|blob| match blob.len {
245            0 => Result::Ok(None), // Multi-signature transaction
246            33 => Result::Ok(Some(PublicKey::from(blob.data))), // Single-signature transaction
247            _ => Result::Err(crate::host::Error::from_code(
248                crate::host::error_codes::INTERNAL_ERROR,
249            )),
250        })
251    }
252
253    /// Retrieves the ticket sequence from the current transaction.
254    ///
255    /// This optional field provides the sequence number of the ticket to use in place of a
256    /// Sequence number. If this is provided, Sequence must be 0. Cannot be used with AccountTxnID.
257    ///
258    /// # Returns
259    ///
260    /// Returns a `Result<Option<u32>>` where:
261    /// * `Ok(Some(u32))` - The ticket sequence number if the transaction uses tickets
262    /// * `Ok(None)` - If the transaction uses traditional sequence numbering
263    /// * `Err(Error)` - If an error occurred during field retrieval
264    ///
265    /// # Note
266    ///
267    /// Transactions use either `Sequence` or `TicketSequence`, but not both. Check this
268    /// field when `get_sequence()` fails or when implementing ticket-aware logic.
269    fn get_ticket_sequence(&self) -> Result<Option<u32>> {
270        get_field_optional(sfield::TicketSequence)
271    }
272
273    /// Retrieves the transaction signature from the current transaction.
274    ///
275    /// This mandatory field contains the signature that verifies this transaction as originating
276    /// from the account it says it is from.
277    ///
278    /// Signatures can be either:
279    /// - 64 bytes for EdDSA (Ed25519) signatures
280    /// - 70-72 bytes for ECDSA (secp256k1) signatures
281    ///
282    /// # Returns
283    ///
284    /// Returns a `Result<Signature>` where:
285    /// * `Ok(Signature)` - The transaction signature (up to 72 bytes)
286    /// * `Err(Error)` - If the field cannot be retrieved
287    ///
288    /// # Security Note
289    ///
290    /// The signature is validated by the XRPL network before transaction execution.
291    /// In the programmability context, you can access the signature for logging or
292    /// analysis purposes, but signature validation has already been performed.
293    fn get_txn_signature(&self) -> Result<SignatureBlob> {
294        get_field(sfield::TxnSignature)
295    }
296}
297
298/// Trait providing access to fields specific to EscrowFinish transactions.
299///
300/// This trait extends `TransactionCommonFields` with methods for retrieving fields that are
301/// unique to EscrowFinish transactions. EscrowFinish transactions are used to complete
302/// time-based or condition-based escrows that were previously created with EscrowCreate
303/// transactions.
304///
305/// ## Implementation Requirements
306///
307/// Types implementing this trait should:
308/// - Also implement `TransactionCommonFields` for access to common transaction fields
309/// - Only be used in the context of processing EscrowFinish transactions
310/// - Ensure proper error handling when accessing conditional fields
311pub trait EscrowFinishFields: TransactionCommonFields {
312    /// Retrieves the owner account from the current EscrowFinish transaction.
313    ///
314    /// This mandatory field identifies the XRPL account that originally created the escrow
315    /// with an EscrowCreate transaction. The owner is the account that deposited the XRP
316    /// into the escrow and specified the conditions for its release.
317    ///
318    /// # Returns
319    ///
320    /// Returns a `Result<AccountID>` where:
321    /// * `Ok(AccountID)` - The 20-byte account identifier of the escrow owner
322    /// * `Err(Error)` - If the field cannot be retrieved or has an unexpected size
323    fn get_owner(&self) -> Result<AccountID> {
324        get_field(sfield::Owner)
325    }
326
327    /// Retrieves the offer sequence from the current EscrowFinish transaction.
328    ///
329    /// This mandatory field specifies the sequence number of the original EscrowCreate
330    /// transaction that created the escrow being finished. This creates a unique reference
331    /// to the specific escrow object, as escrows are identified by the combination of
332    /// the owner account and the sequence number of the creating transaction.
333    ///
334    /// # Returns
335    ///
336    /// Returns a `Result<u32>` where:
337    /// * `Ok(u32)` - The sequence number of the EscrowCreate transaction
338    /// * `Err(Error)` - If the field cannot be retrieved or has an unexpected size
339    fn get_offer_sequence(&self) -> Result<u32> {
340        get_field(sfield::OfferSequence)
341    }
342
343    /// Retrieves the cryptographic condition from the current EscrowFinish transaction.
344    ///
345    /// This optional field contains the cryptographic condition in full crypto-condition format.
346    /// For PREIMAGE-SHA-256 conditions, this is 39 bytes:
347    /// - 2 bytes: type tag (A025)
348    /// - 2 bytes: fingerprint length tag (8020)
349    /// - 32 bytes: SHA-256 hash (fingerprint)
350    /// - 2 bytes: cost length tag (8101)
351    /// - 1 byte: cost value (00)
352    ///
353    /// # Returns
354    ///
355    /// Returns a `Result<Option<Condition>>` where:
356    /// * `Ok(Some(Condition))` - The full crypto-condition if the escrow is conditional
357    /// * `Ok(None)` - If the escrow has no cryptographic condition (time-based only)
358    /// * `Err(Error)` - If an error occurred during field retrieval
359    fn get_condition(&self) -> Result<Option<ConditionBlob>> {
360        let mut buffer = ConditionBlob::new();
361        let result_code = unsafe {
362            get_tx_field(
363                sfield::Condition.into(),
364                buffer.data.as_mut_ptr(),
365                buffer.capacity(),
366            )
367        };
368        match_result_code_optional(result_code, || {
369            buffer.len = result_code as usize;
370            (result_code > 0).then_some(buffer)
371        })
372    }
373
374    /// Retrieves the cryptographic fulfillment from the current EscrowFinish transaction.
375    ///
376    /// This optional field contains the cryptographic fulfillment that satisfies the condition
377    /// specified in the original EscrowCreate transaction. The fulfillment must cryptographically
378    /// prove that the condition's requirements have been met. This field is only required
379    /// when the escrow has an associated condition.
380    ///
381    /// # Returns
382    ///
383    /// Returns a `Result<Option<Fulfillment>>` where:
384    /// * `Ok(Some(Fulfillment))` - The fulfillment data if provided
385    /// * `Ok(None)` - If no fulfillment is provided (valid for unconditional escrows)
386    /// * `Err(Error)` - If an error occurred during field retrieval
387    ///
388    /// # Fulfillment Validation
389    ///
390    /// The XRPL network automatically validates that:
391    /// - The fulfillment satisfies the escrow's condition
392    /// - The fulfillment is properly formatted according to RFC 3814
393    /// - The cryptographic proof is mathematically valid
394    ///
395    /// # Size Limits
396    ///
397    /// Fulfillments are limited to 256 bytes in the current XRPL implementation.
398    /// This limit ensures network performance while supporting the most practical
399    /// cryptographic proof scenarios.
400    fn get_fulfillment(&self) -> Result<Option<FulfillmentBlob>> {
401        let mut buffer = FulfillmentBlob::new();
402        let result_code = unsafe {
403            get_tx_field(
404                sfield::Fulfillment.into(),
405                buffer.data.as_mut_ptr(),
406                buffer.capacity(),
407            )
408        };
409        match_result_code_optional(result_code, || {
410            buffer.len = result_code as usize;
411            (result_code > 0).then_some(buffer)
412        })
413    }
414}
415
416#[cfg(test)]
417mod tests {
418
419    use crate::host::host_bindings_trait::MockHostBindings;
420    use crate::sfield::SField;
421    use mockall::predicate::{always, eq};
422    // ========================================
423    // Test helper functions
424    // ========================================
425
426    /// Helper to set up a mock expectation for get_tx_field
427    fn expect_tx_field<T: Send + std::fmt::Debug + PartialEq + 'static, const CODE: i32>(
428        mock: &mut MockHostBindings,
429        field: SField<T, CODE>,
430        size: usize,
431        times: usize,
432    ) {
433        mock.expect_get_tx_field()
434            .with(eq(field), always(), eq(size))
435            .times(times)
436            .returning(move |_, _, _| size as i32);
437    }
438
439    mod escrow_finish_fields {
440
441        mod optional_fields {
442            use crate::core::current_tx::escrow_finish::EscrowFinish;
443            use crate::core::current_tx::traits::EscrowFinishFields;
444            use crate::core::current_tx::traits::tests::expect_tx_field;
445            use crate::core::types::blob::{CONDITION_BLOB_SIZE, FULFILLMENT_BLOB_SIZE};
446            use crate::host::error_codes::{FIELD_NOT_FOUND, INTERNAL_ERROR, INVALID_FIELD};
447            use crate::host::host_bindings_trait::MockHostBindings;
448            use crate::host::setup_mock;
449            use crate::sfield;
450
451            use crate::sfield::{Condition, Fulfillment};
452            use mockall::predicate::{always, eq};
453
454            #[test]
455            fn test_optional_fields_return_some() {
456                let mut mock = MockHostBindings::new();
457
458                // get_condition
459                expect_tx_field(&mut mock, Condition, CONDITION_BLOB_SIZE, 1);
460                // get_fulfillment
461                expect_tx_field(&mut mock, Fulfillment, FULFILLMENT_BLOB_SIZE, 1);
462
463                let _guard = setup_mock(mock);
464
465                let escrow = EscrowFinish;
466
467                // All optional fields should return Ok(Some(...))
468                let condition = escrow.get_condition().unwrap();
469                assert!(condition.is_some());
470                assert_eq!(condition.unwrap().len, CONDITION_BLOB_SIZE);
471
472                let fulfillment = escrow.get_fulfillment().unwrap();
473                assert!(fulfillment.is_some());
474                assert_eq!(fulfillment.unwrap().len, FULFILLMENT_BLOB_SIZE);
475            }
476
477            #[test]
478            fn test_optional_fields_return_none_when_zero_length() {
479                let mut mock = MockHostBindings::new();
480
481                // get_condition - returns None when result code is 0
482                mock.expect_get_tx_field()
483                    .with(eq(sfield::Condition), always(), eq(CONDITION_BLOB_SIZE))
484                    .times(1)
485                    .returning(|_, _, _| 0);
486                // get_fulfillment - returns None when result code is 0
487                mock.expect_get_tx_field()
488                    .with(eq(sfield::Fulfillment), always(), eq(FULFILLMENT_BLOB_SIZE))
489                    .times(1)
490                    .returning(|_, _, _| 0);
491
492                let _guard = setup_mock(mock);
493
494                let escrow = EscrowFinish;
495
496                // Variable-size optional fields return None when result code is 0 (not present)
497                assert!(escrow.get_condition().unwrap().is_none());
498                assert!(escrow.get_fulfillment().unwrap().is_none());
499            }
500
501            #[test]
502            fn test_optional_fields_return_error_on_internal_error() {
503                let mut mock = MockHostBindings::new();
504
505                // get_condition
506                mock.expect_get_tx_field()
507                    .with(eq(sfield::Condition), always(), eq(CONDITION_BLOB_SIZE))
508                    .times(1)
509                    .returning(|_, _, _| INTERNAL_ERROR);
510                // get_fulfillment
511                mock.expect_get_tx_field()
512                    .with(eq(sfield::Fulfillment), always(), eq(FULFILLMENT_BLOB_SIZE))
513                    .times(1)
514                    .returning(|_, _, _| INTERNAL_ERROR);
515
516                let _guard = setup_mock(mock);
517
518                let escrow = EscrowFinish;
519
520                // Optional fields should also return Err on INTERNAL_ERROR
521                let condition_result = escrow.get_condition();
522                assert!(condition_result.is_err());
523                assert_eq!(condition_result.err().unwrap().code(), INTERNAL_ERROR);
524
525                let fulfillment_result = escrow.get_fulfillment();
526                assert!(fulfillment_result.is_err());
527                assert_eq!(fulfillment_result.err().unwrap().code(), INTERNAL_ERROR);
528            }
529
530            #[test]
531            fn test_optional_fields_return_error_on_field_not_found() {
532                let mut mock = MockHostBindings::new();
533
534                // get_condition
535                mock.expect_get_tx_field()
536                    .with(eq(Condition), always(), eq(CONDITION_BLOB_SIZE))
537                    .times(1)
538                    .returning(|_, _, _| FIELD_NOT_FOUND);
539                // get_fulfillment
540                mock.expect_get_tx_field()
541                    .with(eq(Fulfillment), always(), eq(FULFILLMENT_BLOB_SIZE))
542                    .times(1)
543                    .returning(|_, _, _| FIELD_NOT_FOUND);
544
545                let _guard = setup_mock(mock);
546
547                let escrow = EscrowFinish;
548
549                // Optional fields return Err on FIELD_NOT_FOUND (not None)
550                let condition_result = escrow.get_condition();
551                assert!(condition_result.is_err());
552                assert_eq!(condition_result.err().unwrap().code(), FIELD_NOT_FOUND);
553
554                let fulfillment_result = escrow.get_fulfillment();
555                assert!(fulfillment_result.is_err());
556                assert_eq!(fulfillment_result.err().unwrap().code(), FIELD_NOT_FOUND);
557            }
558
559            #[test]
560            fn test_optional_fields_return_error_on_invalid_field() {
561                let mut mock = MockHostBindings::new();
562
563                // get_condition
564                mock.expect_get_tx_field()
565                    .with(eq(sfield::Condition), always(), eq(CONDITION_BLOB_SIZE))
566                    .times(1)
567                    .returning(|_, _, _| INVALID_FIELD);
568                // get_fulfillment
569                mock.expect_get_tx_field()
570                    .with(eq(sfield::Fulfillment), always(), eq(FULFILLMENT_BLOB_SIZE))
571                    .times(1)
572                    .returning(|_, _, _| INVALID_FIELD);
573
574                let _guard = setup_mock(mock);
575
576                let escrow = EscrowFinish;
577
578                // Optional fields should also return Err on INVALID_FIELD
579                let condition_result = escrow.get_condition();
580                assert!(condition_result.is_err());
581                assert_eq!(condition_result.err().unwrap().code(), INVALID_FIELD);
582
583                let fulfillment_result = escrow.get_fulfillment();
584                assert!(fulfillment_result.is_err());
585                assert_eq!(fulfillment_result.err().unwrap().code(), INVALID_FIELD);
586            }
587        }
588
589        mod mandatory_fields {
590            use crate::core::current_tx::escrow_finish::EscrowFinish;
591            use crate::core::current_tx::traits::EscrowFinishFields;
592            use crate::core::current_tx::traits::tests::expect_tx_field;
593            use crate::core::types::account_id::ACCOUNT_ID_SIZE;
594            use crate::host::error_codes::{FIELD_NOT_FOUND, INTERNAL_ERROR, INVALID_FIELD};
595            use crate::host::host_bindings_trait::MockHostBindings;
596            use crate::host::setup_mock;
597            use crate::sfield;
598            use mockall::predicate::{always, eq};
599
600            #[test]
601            fn test_mandatory_fields_return_ok() {
602                let mut mock = MockHostBindings::new();
603
604                // get_owner
605                expect_tx_field(&mut mock, sfield::Owner, ACCOUNT_ID_SIZE, 1);
606                // get_offer_sequence
607                expect_tx_field(&mut mock, sfield::OfferSequence, 4, 1);
608
609                let _guard = setup_mock(mock);
610
611                let escrow = EscrowFinish;
612
613                // All mandatory fields should return Ok
614                assert!(escrow.get_owner().is_ok());
615                assert!(escrow.get_offer_sequence().is_ok());
616            }
617
618            #[test]
619            fn test_mandatory_fields_return_error_when_zero_length() {
620                let mut mock = MockHostBindings::new();
621
622                // get_owner - returns 0 (zero length)
623                mock.expect_get_tx_field()
624                    .with(eq(sfield::Owner), always(), eq(ACCOUNT_ID_SIZE))
625                    .times(1)
626                    .returning(|_, _, _| 0);
627                // get_offer_sequence - returns 0 (zero length)
628                mock.expect_get_tx_field()
629                    .with(eq(sfield::OfferSequence), always(), eq(4))
630                    .times(1)
631                    .returning(|_, _, _| 0);
632
633                let _guard = setup_mock(mock);
634
635                let escrow = EscrowFinish;
636
637                // Mandatory fields should return Err when zero length (INTERNAL_ERROR due to byte mismatch)
638                let owner_result = escrow.get_owner();
639                assert!(owner_result.is_err());
640
641                let offer_seq_result = escrow.get_offer_sequence();
642                assert!(offer_seq_result.is_err());
643            }
644
645            #[test]
646            fn test_mandatory_fields_return_error_on_field_not_found() {
647                let mut mock = MockHostBindings::new();
648
649                // get_owner
650                mock.expect_get_tx_field()
651                    .with(eq(sfield::Owner), always(), eq(ACCOUNT_ID_SIZE))
652                    .times(1)
653                    .returning(|_, _, _| FIELD_NOT_FOUND);
654                // get_offer_sequence
655                mock.expect_get_tx_field()
656                    .with(eq(sfield::OfferSequence), always(), eq(4))
657                    .times(1)
658                    .returning(|_, _, _| FIELD_NOT_FOUND);
659
660                let _guard = setup_mock(mock);
661
662                let escrow = EscrowFinish;
663
664                // All mandatory fields should return Err on FIELD_NOT_FOUND
665                let owner_result = escrow.get_owner();
666                assert!(owner_result.is_err());
667                assert_eq!(owner_result.err().unwrap().code(), FIELD_NOT_FOUND);
668
669                let offer_seq_result = escrow.get_offer_sequence();
670                assert!(offer_seq_result.is_err());
671                assert_eq!(offer_seq_result.err().unwrap().code(), FIELD_NOT_FOUND);
672            }
673
674            #[test]
675            fn test_mandatory_fields_return_error_on_internal_error() {
676                let mut mock = MockHostBindings::new();
677
678                // get_owner
679                mock.expect_get_tx_field()
680                    .with(eq(sfield::Owner), always(), eq(ACCOUNT_ID_SIZE))
681                    .times(1)
682                    .returning(|_, _, _| INTERNAL_ERROR);
683                // get_offer_sequence
684                mock.expect_get_tx_field()
685                    .with(eq(sfield::OfferSequence), always(), eq(4))
686                    .times(1)
687                    .returning(|_, _, _| INTERNAL_ERROR);
688
689                let _guard = setup_mock(mock);
690
691                let escrow = EscrowFinish;
692
693                // All mandatory fields should return Err on INTERNAL_ERROR
694                let owner_result = escrow.get_owner();
695                assert!(owner_result.is_err());
696                assert_eq!(owner_result.err().unwrap().code(), INTERNAL_ERROR);
697
698                let offer_seq_result = escrow.get_offer_sequence();
699                assert!(offer_seq_result.is_err());
700                assert_eq!(offer_seq_result.err().unwrap().code(), INTERNAL_ERROR);
701            }
702
703            #[test]
704            fn test_mandatory_fields_return_error_on_invalid_field() {
705                let mut mock = MockHostBindings::new();
706
707                // get_owner
708                mock.expect_get_tx_field()
709                    .with(eq(sfield::Owner), always(), eq(ACCOUNT_ID_SIZE))
710                    .times(1)
711                    .returning(|_, _, _| INVALID_FIELD);
712                // get_offer_sequence
713                mock.expect_get_tx_field()
714                    .with(eq(sfield::OfferSequence), always(), eq(4))
715                    .times(1)
716                    .returning(|_, _, _| INVALID_FIELD);
717
718                let _guard = setup_mock(mock);
719
720                let escrow = EscrowFinish;
721
722                // All mandatory fields should return Err on INVALID_FIELD
723                let owner_result = escrow.get_owner();
724                assert!(owner_result.is_err());
725                assert_eq!(owner_result.err().unwrap().code(), INVALID_FIELD);
726
727                let offer_seq_result = escrow.get_offer_sequence();
728                assert!(offer_seq_result.is_err());
729                assert_eq!(offer_seq_result.err().unwrap().code(), INVALID_FIELD);
730            }
731        }
732    }
733
734    mod transaction_common_fields {
735
736        mod optional_fields {
737            use crate::core::current_tx::escrow_finish::EscrowFinish;
738            use crate::core::current_tx::traits::TransactionCommonFields;
739            use crate::core::current_tx::traits::tests::expect_tx_field;
740            use crate::core::types::uint::HASH256_SIZE;
741            use crate::host::error_codes::{FIELD_NOT_FOUND, INTERNAL_ERROR, INVALID_FIELD};
742            use crate::host::host_bindings_trait::MockHostBindings;
743            use crate::host::setup_mock;
744            use crate::sfield;
745            use mockall::predicate::{always, eq};
746
747            #[test]
748            fn test_optional_fields_return_some() {
749                let mut mock = MockHostBindings::new();
750
751                // get_account_txn_id
752                expect_tx_field(&mut mock, sfield::AccountTxnID, HASH256_SIZE, 1);
753                // get_flags
754                expect_tx_field(&mut mock, sfield::Flags, 4, 1);
755                // get_last_ledger_sequence
756                expect_tx_field(&mut mock, sfield::LastLedgerSequence, 4, 1);
757                // get_network_id
758                expect_tx_field(&mut mock, sfield::NetworkID, 4, 1);
759                // get_source_tag
760                expect_tx_field(&mut mock, sfield::SourceTag, 4, 1);
761                // get_ticket_sequence
762                expect_tx_field(&mut mock, sfield::TicketSequence, 4, 1);
763
764                let _guard = setup_mock(mock);
765
766                let tx = EscrowFinish;
767
768                // All optional fields should return Ok(Some(...))
769                assert!(tx.get_account_txn_id().unwrap().is_some());
770                assert!(tx.get_flags().unwrap().is_some());
771                assert!(tx.get_last_ledger_sequence().unwrap().is_some());
772                assert!(tx.get_network_id().unwrap().is_some());
773                assert!(tx.get_source_tag().unwrap().is_some());
774                assert!(tx.get_ticket_sequence().unwrap().is_some());
775            }
776
777            #[test]
778            fn test_optional_fields_return_none_when_field_not_found() {
779                let mut mock = MockHostBindings::new();
780
781                // get_account_txn_id
782                mock.expect_get_tx_field()
783                    .with(eq(sfield::AccountTxnID), always(), eq(HASH256_SIZE))
784                    .times(1)
785                    .returning(|_, _, _| FIELD_NOT_FOUND);
786                // get_flags
787                mock.expect_get_tx_field()
788                    .with(eq(sfield::Flags), always(), eq(4))
789                    .times(1)
790                    .returning(|_, _, _| FIELD_NOT_FOUND);
791                // get_last_ledger_sequence
792                mock.expect_get_tx_field()
793                    .with(eq(sfield::LastLedgerSequence), always(), eq(4))
794                    .times(1)
795                    .returning(|_, _, _| FIELD_NOT_FOUND);
796                // get_network_id
797                mock.expect_get_tx_field()
798                    .with(eq(sfield::NetworkID), always(), eq(4))
799                    .times(1)
800                    .returning(|_, _, _| FIELD_NOT_FOUND);
801                // get_source_tag
802                mock.expect_get_tx_field()
803                    .with(eq(sfield::SourceTag), always(), eq(4))
804                    .times(1)
805                    .returning(|_, _, _| FIELD_NOT_FOUND);
806                // get_ticket_sequence
807                mock.expect_get_tx_field()
808                    .with(eq(sfield::TicketSequence), always(), eq(4))
809                    .times(1)
810                    .returning(|_, _, _| FIELD_NOT_FOUND);
811
812                let _guard = setup_mock(mock);
813
814                let tx = EscrowFinish;
815
816                // Fixed-size optional fields should return Ok(None) when FIELD_NOT_FOUND
817                assert!(tx.get_account_txn_id().unwrap().is_none());
818                assert!(tx.get_flags().unwrap().is_none());
819                assert!(tx.get_last_ledger_sequence().unwrap().is_none());
820                assert!(tx.get_network_id().unwrap().is_none());
821                assert!(tx.get_source_tag().unwrap().is_none());
822                assert!(tx.get_ticket_sequence().unwrap().is_none());
823            }
824
825            #[test]
826            fn test_optional_fields_return_none_when_zero_length() {
827                let mut mock = MockHostBindings::new();
828
829                // get_account_txn_id - returns 0 (zero length)
830                mock.expect_get_tx_field()
831                    .with(eq(sfield::AccountTxnID), always(), eq(HASH256_SIZE))
832                    .times(1)
833                    .returning(|_, _, _| 0);
834                // get_flags - returns 0 (zero length)
835                mock.expect_get_tx_field()
836                    .with(eq(sfield::Flags), always(), eq(4))
837                    .times(1)
838                    .returning(|_, _, _| 0);
839                // get_last_ledger_sequence - returns 0 (zero length)
840                mock.expect_get_tx_field()
841                    .with(eq(sfield::LastLedgerSequence), always(), eq(4))
842                    .times(1)
843                    .returning(|_, _, _| 0);
844                // get_network_id - returns 0 (zero length)
845                mock.expect_get_tx_field()
846                    .with(eq(sfield::NetworkID), always(), eq(4))
847                    .times(1)
848                    .returning(|_, _, _| 0);
849                // get_source_tag - returns 0 (zero length)
850                mock.expect_get_tx_field()
851                    .with(eq(sfield::SourceTag), always(), eq(4))
852                    .times(1)
853                    .returning(|_, _, _| 0);
854                // get_ticket_sequence - returns 0 (zero length)
855                mock.expect_get_tx_field()
856                    .with(eq(sfield::TicketSequence), always(), eq(4))
857                    .times(1)
858                    .returning(|_, _, _| 0);
859
860                // Mock trace_num calls (2 calls per field for byte mismatch: expected + actual)
861                mock.expect_trace_num()
862                    .with(always(), always(), always())
863                    .returning(|_, _, _| 0)
864                    .times(12); // 6 fields * 2 calls each
865
866                let _guard = setup_mock(mock);
867
868                let tx = EscrowFinish;
869
870                // Fixed-size optional fields should return Err when zero length (byte mismatch)
871                assert!(tx.get_account_txn_id().is_err());
872                assert!(tx.get_flags().is_err());
873                assert!(tx.get_last_ledger_sequence().is_err());
874                assert!(tx.get_network_id().is_err());
875                assert!(tx.get_source_tag().is_err());
876                assert!(tx.get_ticket_sequence().is_err());
877            }
878
879            #[test]
880            fn test_optional_fields_return_error_on_internal_error() {
881                let mut mock = MockHostBindings::new();
882
883                // get_account_txn_id
884                mock.expect_get_tx_field()
885                    .with(eq(sfield::AccountTxnID), always(), eq(HASH256_SIZE))
886                    .times(1)
887                    .returning(|_, _, _| INTERNAL_ERROR);
888                // get_flags
889                mock.expect_get_tx_field()
890                    .with(eq(sfield::Flags), always(), eq(4))
891                    .times(1)
892                    .returning(|_, _, _| INTERNAL_ERROR);
893                // get_last_ledger_sequence
894                mock.expect_get_tx_field()
895                    .with(eq(sfield::LastLedgerSequence), always(), eq(4))
896                    .times(1)
897                    .returning(|_, _, _| INTERNAL_ERROR);
898                // get_network_id
899                mock.expect_get_tx_field()
900                    .with(eq(sfield::NetworkID), always(), eq(4))
901                    .times(1)
902                    .returning(|_, _, _| INTERNAL_ERROR);
903                // get_source_tag
904                mock.expect_get_tx_field()
905                    .with(eq(sfield::SourceTag), always(), eq(4))
906                    .times(1)
907                    .returning(|_, _, _| INTERNAL_ERROR);
908                // get_ticket_sequence
909                mock.expect_get_tx_field()
910                    .with(eq(sfield::TicketSequence), always(), eq(4))
911                    .times(1)
912                    .returning(|_, _, _| INTERNAL_ERROR);
913
914                // Mock trace_num calls (1 call per field for error codes)
915                mock.expect_trace_num()
916                    .with(always(), always(), always())
917                    .returning(|_, _, _| 0)
918                    .times(6); // 6 fields * 1 call each
919
920                let _guard = setup_mock(mock);
921
922                let tx = EscrowFinish;
923
924                // Optional fields should return Err on INTERNAL_ERROR
925                let account_txn_id_result = tx.get_account_txn_id();
926                assert!(account_txn_id_result.is_err());
927                assert_eq!(account_txn_id_result.err().unwrap().code(), INTERNAL_ERROR);
928
929                let flags_result = tx.get_flags();
930                assert!(flags_result.is_err());
931                assert_eq!(flags_result.err().unwrap().code(), INTERNAL_ERROR);
932
933                let last_ledger_seq_result = tx.get_last_ledger_sequence();
934                assert!(last_ledger_seq_result.is_err());
935                assert_eq!(last_ledger_seq_result.err().unwrap().code(), INTERNAL_ERROR);
936
937                let network_id_result = tx.get_network_id();
938                assert!(network_id_result.is_err());
939                assert_eq!(network_id_result.err().unwrap().code(), INTERNAL_ERROR);
940
941                let source_tag_result = tx.get_source_tag();
942                assert!(source_tag_result.is_err());
943                assert_eq!(source_tag_result.err().unwrap().code(), INTERNAL_ERROR);
944
945                let ticket_seq_result = tx.get_ticket_sequence();
946                assert!(ticket_seq_result.is_err());
947                assert_eq!(ticket_seq_result.err().unwrap().code(), INTERNAL_ERROR);
948            }
949
950            #[test]
951            fn test_optional_fields_return_error_on_invalid_field() {
952                let mut mock = MockHostBindings::new();
953
954                // get_account_txn_id
955                mock.expect_get_tx_field()
956                    .with(eq(sfield::AccountTxnID), always(), eq(HASH256_SIZE))
957                    .times(1)
958                    .returning(|_, _, _| INVALID_FIELD);
959                // get_flags
960                mock.expect_get_tx_field()
961                    .with(eq(sfield::Flags), always(), eq(4))
962                    .times(1)
963                    .returning(|_, _, _| INVALID_FIELD);
964                // get_last_ledger_sequence
965                mock.expect_get_tx_field()
966                    .with(eq(sfield::LastLedgerSequence), always(), eq(4))
967                    .times(1)
968                    .returning(|_, _, _| INVALID_FIELD);
969                // get_network_id
970                mock.expect_get_tx_field()
971                    .with(eq(sfield::NetworkID), always(), eq(4))
972                    .times(1)
973                    .returning(|_, _, _| INVALID_FIELD);
974                // get_source_tag
975                mock.expect_get_tx_field()
976                    .with(eq(sfield::SourceTag), always(), eq(4))
977                    .times(1)
978                    .returning(|_, _, _| INVALID_FIELD);
979                // get_ticket_sequence
980                mock.expect_get_tx_field()
981                    .with(eq(sfield::TicketSequence), always(), eq(4))
982                    .times(1)
983                    .returning(|_, _, _| INVALID_FIELD);
984
985                // Mock trace_num calls (1 call per field for error codes)
986                mock.expect_trace_num()
987                    .with(always(), always(), always())
988                    .returning(|_, _, _| 0)
989                    .times(6); // 6 fields * 1 call each
990
991                let _guard = setup_mock(mock);
992
993                let tx = EscrowFinish;
994
995                // Optional fields should return Err on INVALID_FIELD
996                let account_txn_id_result = tx.get_account_txn_id();
997                assert!(account_txn_id_result.is_err());
998                assert_eq!(account_txn_id_result.err().unwrap().code(), INVALID_FIELD);
999
1000                let flags_result = tx.get_flags();
1001                assert!(flags_result.is_err());
1002                assert_eq!(flags_result.err().unwrap().code(), INVALID_FIELD);
1003
1004                let last_ledger_seq_result = tx.get_last_ledger_sequence();
1005                assert!(last_ledger_seq_result.is_err());
1006                assert_eq!(last_ledger_seq_result.err().unwrap().code(), INVALID_FIELD);
1007
1008                let network_id_result = tx.get_network_id();
1009                assert!(network_id_result.is_err());
1010                assert_eq!(network_id_result.err().unwrap().code(), INVALID_FIELD);
1011
1012                let source_tag_result = tx.get_source_tag();
1013                assert!(source_tag_result.is_err());
1014                assert_eq!(source_tag_result.err().unwrap().code(), INVALID_FIELD);
1015
1016                let ticket_seq_result = tx.get_ticket_sequence();
1017                assert!(ticket_seq_result.is_err());
1018                assert_eq!(ticket_seq_result.err().unwrap().code(), INVALID_FIELD);
1019            }
1020        }
1021
1022        mod required_fields {
1023            use crate::core::current_tx::escrow_finish::EscrowFinish;
1024            use crate::core::current_tx::traits::TransactionCommonFields;
1025            use crate::core::current_tx::traits::tests::expect_tx_field;
1026            use crate::core::types::account_id::ACCOUNT_ID_SIZE;
1027            use crate::core::types::amount::AMOUNT_SIZE;
1028            use crate::core::types::blob::SIGNATURE_BLOB_SIZE;
1029            use crate::core::types::public_key::PUBLIC_KEY_BUFFER_SIZE;
1030            use crate::host::error_codes::{FIELD_NOT_FOUND, INTERNAL_ERROR, INVALID_FIELD};
1031            use crate::host::host_bindings_trait::MockHostBindings;
1032            use crate::host::setup_mock;
1033            use crate::sfield;
1034            use mockall::predicate::{always, eq};
1035
1036            #[test]
1037            fn test_mandatory_fields_return_ok() {
1038                let mut mock = MockHostBindings::new();
1039
1040                // get_account
1041                expect_tx_field(&mut mock, sfield::Account, ACCOUNT_ID_SIZE, 1);
1042                // get_transaction_type
1043                expect_tx_field(&mut mock, sfield::TransactionType, 2, 1);
1044                // get_computation_allowance
1045                expect_tx_field(&mut mock, sfield::ComputationAllowance, 4, 1);
1046                // get_fee
1047                expect_tx_field(&mut mock, sfield::Fee, AMOUNT_SIZE, 1);
1048                // get_sequence
1049                expect_tx_field(&mut mock, sfield::Sequence, 4, 1);
1050                // get_signing_pub_key
1051                expect_tx_field(&mut mock, sfield::SigningPubKey, PUBLIC_KEY_BUFFER_SIZE, 1);
1052                // get_txn_signature
1053                expect_tx_field(&mut mock, sfield::TxnSignature, SIGNATURE_BLOB_SIZE, 1);
1054
1055                let _guard = setup_mock(mock);
1056
1057                let tx = EscrowFinish;
1058
1059                // All mandatory fields should return Ok
1060                assert!(tx.get_account().is_ok());
1061                assert!(tx.get_transaction_type().is_ok());
1062                assert!(tx.get_computation_allowance().is_ok());
1063                assert!(tx.get_fee().is_ok());
1064                assert!(tx.get_sequence().is_ok());
1065                assert!(tx.get_signing_pub_key().is_ok());
1066                assert!(tx.get_txn_signature().is_ok());
1067            }
1068
1069            #[test]
1070            fn test_mandatory_fields_return_error_when_zero_length() {
1071                let mut mock = MockHostBindings::new();
1072
1073                // get_account - returns 0 (zero length)
1074                mock.expect_get_tx_field()
1075                    .with(eq(sfield::Account), always(), eq(ACCOUNT_ID_SIZE))
1076                    .times(1)
1077                    .returning(|_, _, _| 0);
1078                // get_transaction_type - returns 0 (zero length)
1079                mock.expect_get_tx_field()
1080                    .with(eq(sfield::TransactionType), always(), eq(2))
1081                    .times(1)
1082                    .returning(|_, _, _| 0);
1083                // get_computation_allowance - returns 0 (zero length)
1084                mock.expect_get_tx_field()
1085                    .with(eq(sfield::ComputationAllowance), always(), eq(4))
1086                    .times(1)
1087                    .returning(|_, _, _| 0);
1088                // get_fee - returns 0 (zero length)
1089                mock.expect_get_tx_field()
1090                    .with(eq(sfield::Fee), always(), eq(AMOUNT_SIZE))
1091                    .times(1)
1092                    .returning(|_, _, _| 0);
1093                // get_sequence - returns 0 (zero length)
1094                mock.expect_get_tx_field()
1095                    .with(eq(sfield::Sequence), always(), eq(4))
1096                    .times(1)
1097                    .returning(|_, _, _| 0);
1098                // get_signing_pub_key - returns 0 (zero length)
1099                mock.expect_get_tx_field()
1100                    .with(
1101                        eq(sfield::SigningPubKey),
1102                        always(),
1103                        eq(PUBLIC_KEY_BUFFER_SIZE),
1104                    )
1105                    .times(1)
1106                    .returning(|_, _, _| 0);
1107
1108                let _guard = setup_mock(mock);
1109
1110                let tx = EscrowFinish;
1111
1112                // Fixed-size mandatory fields should return Err when zero length (byte mismatch)
1113                let account_result = tx.get_account();
1114                assert!(account_result.is_err());
1115
1116                let tx_type_result = tx.get_transaction_type();
1117                assert!(tx_type_result.is_err());
1118
1119                let comp_allow_result = tx.get_computation_allowance();
1120                assert!(comp_allow_result.is_err());
1121
1122                // Variable-size field (Amount) returns Ok with zero length
1123                let fee_result = tx.get_fee();
1124                assert!(fee_result.is_ok());
1125
1126                let seq_result = tx.get_sequence();
1127                assert!(seq_result.is_err());
1128
1129                // SigningPubKey is special: zero length indicates multi-signature transaction
1130                // and should return Ok(None), not an error
1131                let signing_key_result = tx.get_signing_pub_key();
1132                assert!(signing_key_result.is_ok());
1133                assert!(signing_key_result.unwrap().is_none());
1134            }
1135
1136            #[test]
1137            fn test_mandatory_fields_return_error_on_field_not_found() {
1138                let mut mock = MockHostBindings::new();
1139
1140                // get_account
1141                mock.expect_get_tx_field()
1142                    .with(eq(sfield::Account), always(), eq(ACCOUNT_ID_SIZE))
1143                    .times(1)
1144                    .returning(|_, _, _| FIELD_NOT_FOUND);
1145                // get_transaction_type
1146                mock.expect_get_tx_field()
1147                    .with(eq(sfield::TransactionType), always(), eq(2))
1148                    .times(1)
1149                    .returning(|_, _, _| FIELD_NOT_FOUND);
1150                // get_computation_allowance
1151                mock.expect_get_tx_field()
1152                    .with(eq(sfield::ComputationAllowance), always(), eq(4))
1153                    .times(1)
1154                    .returning(|_, _, _| FIELD_NOT_FOUND);
1155                // get_fee
1156                mock.expect_get_tx_field()
1157                    .with(eq(sfield::Fee), always(), eq(AMOUNT_SIZE))
1158                    .times(1)
1159                    .returning(|_, _, _| FIELD_NOT_FOUND);
1160                // get_sequence
1161                mock.expect_get_tx_field()
1162                    .with(eq(sfield::Sequence), always(), eq(4))
1163                    .times(1)
1164                    .returning(|_, _, _| FIELD_NOT_FOUND);
1165                // get_signing_pub_key
1166                mock.expect_get_tx_field()
1167                    .with(
1168                        eq(sfield::SigningPubKey),
1169                        always(),
1170                        eq(PUBLIC_KEY_BUFFER_SIZE),
1171                    )
1172                    .times(1)
1173                    .returning(|_, _, _| FIELD_NOT_FOUND);
1174
1175                let _guard = setup_mock(mock);
1176
1177                let tx = EscrowFinish;
1178
1179                // All mandatory fields should return Err on FIELD_NOT_FOUND
1180                let account_result = tx.get_account();
1181                assert!(account_result.is_err());
1182                assert_eq!(account_result.err().unwrap().code(), FIELD_NOT_FOUND);
1183
1184                let tx_type_result = tx.get_transaction_type();
1185                assert!(tx_type_result.is_err());
1186                assert_eq!(tx_type_result.err().unwrap().code(), FIELD_NOT_FOUND);
1187
1188                let comp_allow_result = tx.get_computation_allowance();
1189                assert!(comp_allow_result.is_err());
1190                assert_eq!(comp_allow_result.err().unwrap().code(), FIELD_NOT_FOUND);
1191
1192                let fee_result = tx.get_fee();
1193                assert!(fee_result.is_err());
1194                assert_eq!(fee_result.err().unwrap().code(), FIELD_NOT_FOUND);
1195
1196                let seq_result = tx.get_sequence();
1197                assert!(seq_result.is_err());
1198                assert_eq!(seq_result.err().unwrap().code(), FIELD_NOT_FOUND);
1199
1200                let signing_key_result = tx.get_signing_pub_key();
1201                assert!(signing_key_result.is_err());
1202                assert_eq!(signing_key_result.err().unwrap().code(), FIELD_NOT_FOUND);
1203            }
1204
1205            #[test]
1206            fn test_mandatory_fields_return_error_on_internal_error() {
1207                let mut mock = MockHostBindings::new();
1208
1209                // get_account
1210                mock.expect_get_tx_field()
1211                    .with(eq(sfield::Account), always(), eq(ACCOUNT_ID_SIZE))
1212                    .times(1)
1213                    .returning(|_, _, _| INTERNAL_ERROR);
1214                // get_transaction_type
1215                mock.expect_get_tx_field()
1216                    .with(eq(sfield::TransactionType), always(), eq(2))
1217                    .times(1)
1218                    .returning(|_, _, _| INTERNAL_ERROR);
1219                // get_computation_allowance
1220                mock.expect_get_tx_field()
1221                    .with(eq(sfield::ComputationAllowance), always(), eq(4))
1222                    .times(1)
1223                    .returning(|_, _, _| INTERNAL_ERROR);
1224                // get_fee
1225                mock.expect_get_tx_field()
1226                    .with(eq(sfield::Fee), always(), eq(AMOUNT_SIZE))
1227                    .times(1)
1228                    .returning(|_, _, _| INTERNAL_ERROR);
1229                // get_sequence
1230                mock.expect_get_tx_field()
1231                    .with(eq(sfield::Sequence), always(), eq(4))
1232                    .times(1)
1233                    .returning(|_, _, _| INTERNAL_ERROR);
1234                // get_signing_pub_key
1235                mock.expect_get_tx_field()
1236                    .with(
1237                        eq(sfield::SigningPubKey),
1238                        always(),
1239                        eq(PUBLIC_KEY_BUFFER_SIZE),
1240                    )
1241                    .times(1)
1242                    .returning(|_, _, _| INTERNAL_ERROR);
1243
1244                let _guard = setup_mock(mock);
1245
1246                let tx = EscrowFinish;
1247
1248                // All mandatory fields should return Err on INTERNAL_ERROR
1249                let account_result = tx.get_account();
1250                assert!(account_result.is_err());
1251                assert_eq!(account_result.err().unwrap().code(), INTERNAL_ERROR);
1252
1253                let tx_type_result = tx.get_transaction_type();
1254                assert!(tx_type_result.is_err());
1255                assert_eq!(tx_type_result.err().unwrap().code(), INTERNAL_ERROR);
1256
1257                let comp_allow_result = tx.get_computation_allowance();
1258                assert!(comp_allow_result.is_err());
1259                assert_eq!(comp_allow_result.err().unwrap().code(), INTERNAL_ERROR);
1260
1261                let fee_result = tx.get_fee();
1262                assert!(fee_result.is_err());
1263                assert_eq!(fee_result.err().unwrap().code(), INTERNAL_ERROR);
1264
1265                let seq_result = tx.get_sequence();
1266                assert!(seq_result.is_err());
1267                assert_eq!(seq_result.err().unwrap().code(), INTERNAL_ERROR);
1268
1269                let signing_key_result = tx.get_signing_pub_key();
1270                assert!(signing_key_result.is_err());
1271                assert_eq!(signing_key_result.err().unwrap().code(), INTERNAL_ERROR);
1272            }
1273
1274            #[test]
1275            fn test_mandatory_fields_return_error_on_invalid_field() {
1276                let mut mock = MockHostBindings::new();
1277
1278                // get_account
1279                mock.expect_get_tx_field()
1280                    .with(eq(sfield::Account), always(), eq(ACCOUNT_ID_SIZE))
1281                    .times(1)
1282                    .returning(|_, _, _| INVALID_FIELD);
1283                // get_transaction_type
1284                mock.expect_get_tx_field()
1285                    .with(eq(sfield::TransactionType), always(), eq(2))
1286                    .times(1)
1287                    .returning(|_, _, _| INVALID_FIELD);
1288                // get_computation_allowance
1289                mock.expect_get_tx_field()
1290                    .with(eq(sfield::ComputationAllowance), always(), eq(4))
1291                    .times(1)
1292                    .returning(|_, _, _| INVALID_FIELD);
1293                // get_fee
1294                mock.expect_get_tx_field()
1295                    .with(eq(sfield::Fee), always(), eq(AMOUNT_SIZE))
1296                    .times(1)
1297                    .returning(|_, _, _| INVALID_FIELD);
1298                // get_sequence
1299                mock.expect_get_tx_field()
1300                    .with(eq(sfield::Sequence), always(), eq(4))
1301                    .times(1)
1302                    .returning(|_, _, _| INVALID_FIELD);
1303                // get_signing_pub_key
1304                mock.expect_get_tx_field()
1305                    .with(
1306                        eq(sfield::SigningPubKey),
1307                        always(),
1308                        eq(PUBLIC_KEY_BUFFER_SIZE),
1309                    )
1310                    .times(1)
1311                    .returning(|_, _, _| INVALID_FIELD);
1312
1313                let _guard = setup_mock(mock);
1314
1315                let tx = EscrowFinish;
1316
1317                // All mandatory fields should return Err on INVALID_FIELD
1318                let account_result = tx.get_account();
1319                assert!(account_result.is_err());
1320                assert_eq!(account_result.err().unwrap().code(), INVALID_FIELD);
1321
1322                let tx_type_result = tx.get_transaction_type();
1323                assert!(tx_type_result.is_err());
1324                assert_eq!(tx_type_result.err().unwrap().code(), INVALID_FIELD);
1325
1326                let comp_allow_result = tx.get_computation_allowance();
1327                assert!(comp_allow_result.is_err());
1328                assert_eq!(comp_allow_result.err().unwrap().code(), INVALID_FIELD);
1329
1330                let fee_result = tx.get_fee();
1331                assert!(fee_result.is_err());
1332                assert_eq!(fee_result.err().unwrap().code(), INVALID_FIELD);
1333
1334                let seq_result = tx.get_sequence();
1335                assert!(seq_result.is_err());
1336                assert_eq!(seq_result.err().unwrap().code(), INVALID_FIELD);
1337
1338                let signing_key_result = tx.get_signing_pub_key();
1339                assert!(signing_key_result.is_err());
1340                assert_eq!(signing_key_result.err().unwrap().code(), INVALID_FIELD);
1341            }
1342        }
1343    }
1344}