casper_types/
transfer.rs

1mod error;
2mod transfer_v1;
3mod transfer_v2;
4
5use alloc::vec::Vec;
6
7#[cfg(any(feature = "testing", test))]
8use crate::testing::TestRng;
9#[cfg(feature = "datasize")]
10use datasize::DataSize;
11#[cfg(feature = "json-schema")]
12use once_cell::sync::Lazy;
13#[cfg(any(feature = "testing", test))]
14use rand::Rng;
15#[cfg(feature = "json-schema")]
16use schemars::JsonSchema;
17use serde::{Deserialize, Serialize};
18
19#[cfg(feature = "json-schema")]
20use crate::{account::AccountHash, transaction::TransactionV1Hash, URef};
21use crate::{
22    bytesrepr::{self, FromBytes, ToBytes, U8_SERIALIZED_LENGTH},
23    U512,
24};
25#[cfg(any(feature = "testing", feature = "json-schema", test))]
26use crate::{transaction::TransactionHash, Gas, InitiatorAddr};
27pub use error::TransferFromStrError;
28pub use transfer_v1::{TransferAddr, TransferV1, TRANSFER_ADDR_LENGTH};
29pub use transfer_v2::TransferV2;
30
31const V1_TAG: u8 = 0;
32const V2_TAG: u8 = 1;
33
34#[cfg(feature = "json-schema")]
35pub(super) static TRANSFER: Lazy<Transfer> = Lazy::new(|| {
36    let transaction_hash = TransactionHash::V1(TransactionV1Hash::from_raw([1; 32]));
37    let from = InitiatorAddr::AccountHash(AccountHash::new([2; 32]));
38    let to = Some(AccountHash::new([3; 32]));
39    let source = URef::from_formatted_str(
40        "uref-0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a-007",
41    )
42    .unwrap();
43    let target = URef::from_formatted_str(
44        "uref-1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b-000",
45    )
46    .unwrap();
47    let amount = U512::from(1_000_000_000_000_u64);
48    let gas = Gas::new(2_500_000_000_u64);
49    let id = Some(999);
50    Transfer::V2(TransferV2::new(
51        transaction_hash,
52        from,
53        to,
54        source,
55        target,
56        amount,
57        gas,
58        id,
59    ))
60});
61
62/// A versioned wrapper for a transfer.
63#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Debug)]
64#[cfg_attr(feature = "datasize", derive(DataSize))]
65#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
66pub enum Transfer {
67    /// A version 1 transfer.
68    #[serde(rename = "Version1")]
69    V1(TransferV1),
70    /// A version 2 transfer.
71    #[serde(rename = "Version2")]
72    V2(TransferV2),
73}
74
75impl Transfer {
76    /// Transfer amount.
77    pub fn amount(&self) -> U512 {
78        match self {
79            Transfer::V1(transfer_v1) => transfer_v1.amount,
80            Transfer::V2(transfer_v2) => transfer_v2.amount,
81        }
82    }
83
84    // This method is not intended to be used by third party crates.
85    #[doc(hidden)]
86    #[cfg(feature = "json-schema")]
87    pub fn example() -> &'static Self {
88        &TRANSFER
89    }
90
91    /// Returns a random `Transfer`.
92    #[cfg(any(feature = "testing", test))]
93    pub fn random(rng: &mut TestRng) -> Self {
94        use crate::DeployHash;
95
96        if rng.gen() {
97            Transfer::V1(TransferV1::new(
98                DeployHash::random(rng),
99                rng.gen(),
100                rng.gen(),
101                rng.gen(),
102                rng.gen(),
103                rng.gen(),
104                rng.gen(),
105                rng.gen(),
106            ))
107        } else {
108            Transfer::V2(TransferV2::new(
109                TransactionHash::random(rng),
110                InitiatorAddr::random(rng),
111                rng.gen(),
112                rng.gen(),
113                rng.gen(),
114                rng.gen(),
115                Gas::new(rng.gen::<u64>()),
116                rng.gen(),
117            ))
118        }
119    }
120}
121
122impl From<TransferV1> for Transfer {
123    fn from(v1_transfer: TransferV1) -> Self {
124        Transfer::V1(v1_transfer)
125    }
126}
127
128impl From<TransferV2> for Transfer {
129    fn from(v2_transfer: TransferV2) -> Self {
130        Transfer::V2(v2_transfer)
131    }
132}
133
134impl ToBytes for Transfer {
135    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
136        let mut buffer = bytesrepr::allocate_buffer(self)?;
137        self.write_bytes(&mut buffer)?;
138        Ok(buffer)
139    }
140
141    fn serialized_length(&self) -> usize {
142        U8_SERIALIZED_LENGTH
143            + match self {
144                Transfer::V1(transfer) => transfer.serialized_length(),
145                Transfer::V2(transfer) => transfer.serialized_length(),
146            }
147    }
148
149    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
150        match self {
151            Transfer::V1(transfer) => {
152                V1_TAG.write_bytes(writer)?;
153                transfer.write_bytes(writer)
154            }
155            Transfer::V2(transfer) => {
156                V2_TAG.write_bytes(writer)?;
157                transfer.write_bytes(writer)
158            }
159        }
160    }
161}
162
163impl FromBytes for Transfer {
164    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
165        let (tag, remainder) = u8::from_bytes(bytes)?;
166        match tag {
167            V1_TAG => {
168                let (transfer, remainder) = TransferV1::from_bytes(remainder)?;
169                Ok((Transfer::V1(transfer), remainder))
170            }
171            V2_TAG => {
172                let (transfer, remainder) = TransferV2::from_bytes(remainder)?;
173                Ok((Transfer::V2(transfer), remainder))
174            }
175            _ => Err(bytesrepr::Error::Formatting),
176        }
177    }
178}
179
180/// Proptest generators for [`Transfer`].
181#[cfg(any(feature = "testing", feature = "gens", test))]
182pub mod gens {
183    use proptest::{
184        array,
185        prelude::{prop::option, Arbitrary, Strategy},
186    };
187
188    use super::*;
189    use crate::{
190        gens::{account_hash_arb, u512_arb, uref_arb},
191        transaction::gens::deploy_hash_arb,
192    };
193
194    pub fn transfer_v1_addr_arb() -> impl Strategy<Value = TransferAddr> {
195        array::uniform32(<u8>::arbitrary()).prop_map(TransferAddr::new)
196    }
197
198    pub fn transfer_v1_arb() -> impl Strategy<Value = TransferV1> {
199        (
200            deploy_hash_arb(),
201            account_hash_arb(),
202            option::of(account_hash_arb()),
203            uref_arb(),
204            uref_arb(),
205            u512_arb(),
206            u512_arb(),
207            option::of(<u64>::arbitrary()),
208        )
209            .prop_map(|(deploy_hash, from, to, source, target, amount, gas, id)| {
210                TransferV1 {
211                    deploy_hash,
212                    from,
213                    to,
214                    source,
215                    target,
216                    amount,
217                    gas,
218                    id,
219                }
220            })
221    }
222}
223
224#[cfg(test)]
225mod tests {
226    use crate::bytesrepr;
227
228    use super::*;
229
230    #[test]
231    fn bytesrepr_roundtrip() {
232        let rng = &mut TestRng::new();
233
234        let transfer = Transfer::random(rng);
235        bytesrepr::test_serialization_roundtrip(&transfer);
236    }
237}