fuel_tx/transaction/types/
witness.rs

1use educe::Educe;
2use fuel_types::fmt_truncated_hex;
3
4use alloc::vec::Vec;
5
6use crate::{
7    Input,
8    TxId,
9    ValidityError,
10};
11use fuel_crypto::{
12    Message,
13    Signature,
14};
15
16#[cfg(feature = "random")]
17use rand::{
18    distributions::{
19        Distribution,
20        Standard,
21    },
22    Rng,
23};
24
25#[derive(Educe, Default, Clone, PartialEq, Eq, Hash)]
26#[educe(Debug)]
27#[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
28#[derive(serde::Serialize, serde::Deserialize)]
29#[cfg_attr(
30    feature = "da-compression",
31    derive(fuel_compression::Compress, fuel_compression::Decompress)
32)]
33#[derive(fuel_types::canonical::Deserialize, fuel_types::canonical::Serialize)]
34pub struct Witness {
35    #[educe(Debug(method(fmt_truncated_hex::<16>)))]
36    data: Vec<u8>,
37}
38
39impl Witness {
40    pub const fn as_vec(&self) -> &Vec<u8> {
41        &self.data
42    }
43
44    pub fn as_vec_mut(&mut self) -> &mut Vec<u8> {
45        &mut self.data
46    }
47
48    pub fn into_inner(self) -> Vec<u8> {
49        self.data
50    }
51
52    /// ECRecover an address from a witness
53    pub fn recover_witness(
54        &self,
55        txhash: &TxId,
56        input_index: usize,
57    ) -> Result<fuel_types::Address, ValidityError> {
58        let bytes = <[u8; Signature::LEN]>::try_from(self.as_ref())
59            .map_err(|_| ValidityError::InputInvalidSignature { index: input_index })?;
60        let signature = Signature::from_bytes(bytes);
61
62        let message = Message::from_bytes_ref(txhash);
63
64        signature
65            .recover(message)
66            .map_err(|_| ValidityError::InputInvalidSignature { index: input_index })
67            .map(|pk| Input::owner(&pk))
68    }
69}
70
71impl From<Vec<u8>> for Witness {
72    fn from(data: Vec<u8>) -> Self {
73        Self { data }
74    }
75}
76
77impl From<&[u8]> for Witness {
78    fn from(data: &[u8]) -> Self {
79        data.to_vec().into()
80    }
81}
82
83impl AsRef<[u8]> for Witness {
84    fn as_ref(&self) -> &[u8] {
85        self.data.as_ref()
86    }
87}
88
89impl AsMut<[u8]> for Witness {
90    fn as_mut(&mut self) -> &mut [u8] {
91        self.data.as_mut()
92    }
93}
94
95impl Extend<u8> for Witness {
96    fn extend<T: IntoIterator<Item = u8>>(&mut self, iter: T) {
97        self.data.extend(iter);
98    }
99}
100
101#[cfg(feature = "random")]
102impl Distribution<Witness> for Standard {
103    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Witness {
104        let len = rng.gen_range(0..512);
105
106        let mut data = alloc::vec![0u8; len];
107        rng.fill_bytes(data.as_mut_slice());
108
109        data.into()
110    }
111}
112
113#[cfg(feature = "typescript")]
114pub mod typescript {
115    use wasm_bindgen::prelude::*;
116
117    use super::Witness;
118
119    use alloc::{
120        format,
121        string::String,
122        vec::Vec,
123    };
124
125    #[wasm_bindgen]
126    impl Witness {
127        #[wasm_bindgen(js_name = toJSON)]
128        pub fn to_json(&self) -> String {
129            serde_json::to_string(&self.data).expect("unable to json format")
130        }
131
132        #[wasm_bindgen(js_name = toString)]
133        pub fn typescript_to_string(&self) -> String {
134            format!("{:?}", self.data)
135        }
136
137        #[wasm_bindgen(js_name = to_bytes)]
138        pub fn typescript_to_bytes(&self) -> Vec<u8> {
139            use fuel_types::canonical::Serialize;
140            self.to_bytes()
141        }
142
143        #[wasm_bindgen(js_name = from_bytes)]
144        pub fn typescript_from_bytes(value: &[u8]) -> Result<Witness, js_sys::Error> {
145            use alloc::string::ToString;
146            use fuel_types::canonical::Deserialize;
147            <Self as Deserialize>::from_bytes(value)
148                .map_err(|e| js_sys::Error::new(&e.to_string()))
149        }
150    }
151}