kaspa_consensus_client/
outpoint.rs

1//!
2//! Implementation of the client-side [`TransactionOutpoint`] used by the [`TransactionInput`] struct.
3//!
4
5#![allow(non_snake_case)]
6
7use cfg_if::cfg_if;
8
9use crate::imports::*;
10use crate::result::Result;
11
12#[wasm_bindgen(typescript_custom_section)]
13const TS_TRANSACTION_OUTPOINT: &'static str = r#"
14/**
15 * Interface defines the structure of a transaction outpoint (used by transaction input).
16 * 
17 * @category Consensus
18 */
19export interface ITransactionOutpoint {
20    transactionId: HexString;
21    index: number;
22}
23"#;
24
25/// Inner type used by [`TransactionOutpoint`]
26#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Ord, PartialOrd)]
27#[serde(rename_all = "camelCase")]
28pub struct TransactionOutpointInner {
29    pub transaction_id: TransactionId,
30    pub index: TransactionIndexType,
31}
32
33impl std::fmt::Display for TransactionOutpointInner {
34    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35        write!(f, "{}-{}", self.transaction_id, self.index)
36    }
37}
38
39impl TransactionOutpointInner {
40    pub fn new(transaction_id: TransactionId, index: TransactionIndexType) -> Self {
41        Self { transaction_id, index }
42    }
43
44    pub fn to_bytes(&self) -> Vec<u8> {
45        let mut data = self.transaction_id.as_bytes().to_vec();
46        data.extend(self.index.to_be_bytes());
47        data
48    }
49}
50
51impl From<cctx::TransactionOutpoint> for TransactionOutpointInner {
52    fn from(outpoint: cctx::TransactionOutpoint) -> Self {
53        TransactionOutpointInner { transaction_id: outpoint.transaction_id, index: outpoint.index }
54    }
55}
56
57impl TryFrom<&JsValue> for TransactionOutpointInner {
58    type Error = Error;
59    fn try_from(js_value: &JsValue) -> Result<Self, Self::Error> {
60        if let Some(string) = js_value.as_string() {
61            let vec = string.split('-').collect::<Vec<_>>();
62            if vec.len() == 2 {
63                let transaction_id: TransactionId = vec[0].parse()?;
64                let id: u32 = vec[1].parse()?;
65                Ok(TransactionOutpointInner::new(transaction_id, id))
66            } else {
67                Err(Error::InvalidTransactionOutpoint(string))
68            }
69        } else if let Some(object) = js_sys::Object::try_from(js_value) {
70            let transaction_id: TransactionId = object.get_value("transactionId")?.try_into_owned()?;
71            let index = object.get_u32("index")?;
72            Ok(TransactionOutpointInner::new(transaction_id, index))
73        } else {
74            Err("outpoint is not an object".into())
75        }
76    }
77}
78
79/// Represents a Kaspa transaction outpoint.
80/// NOTE: This struct is immutable - to create a custom outpoint
81/// use the `TransactionOutpoint::new` constructor. (in JavaScript
82/// use `new TransactionOutpoint(transactionId, index)`).
83/// @category Consensus
84#[derive(Clone, Debug, Serialize, Deserialize, CastFromJs)]
85#[serde(rename_all = "camelCase")]
86#[wasm_bindgen(inspectable)]
87pub struct TransactionOutpoint {
88    inner: Arc<TransactionOutpointInner>,
89}
90
91impl TransactionOutpoint {
92    pub fn new(transaction_id: TransactionId, index: u32) -> TransactionOutpoint {
93        Self { inner: Arc::new(TransactionOutpointInner { transaction_id, index }) }
94    }
95
96    #[inline(always)]
97    pub fn inner(&self) -> &TransactionOutpointInner {
98        &self.inner
99    }
100
101    #[inline(always)]
102    pub fn transaction_id(&self) -> TransactionId {
103        self.inner().transaction_id
104    }
105
106    #[inline(always)]
107    pub fn index(&self) -> TransactionIndexType {
108        self.inner().index
109    }
110
111    #[inline(always)]
112    pub fn transaction_id_as_ref(&self) -> &TransactionId {
113        &self.inner().transaction_id
114    }
115
116    #[inline(always)]
117    pub fn id(&self) -> &TransactionOutpointInner {
118        self.inner()
119    }
120}
121
122cfg_if! {
123    if #[cfg(feature = "wasm32-sdk")] {
124
125        #[wasm_bindgen]
126        impl TransactionOutpoint {
127            #[wasm_bindgen(constructor)]
128            pub fn ctor(transaction_id: TransactionId, index: u32) -> TransactionOutpoint {
129                Self { inner: Arc::new(TransactionOutpointInner { transaction_id, index }) }
130            }
131
132            #[wasm_bindgen(js_name = "getId")]
133            pub fn id_string(&self) -> String {
134                format!("{}-{}", self.get_transaction_id_as_string(), self.get_index())
135            }
136
137            #[wasm_bindgen(getter, js_name = transactionId)]
138            pub fn get_transaction_id_as_string(&self) -> String {
139                self.inner().transaction_id.to_string()
140            }
141
142            #[wasm_bindgen(getter, js_name = index)]
143            pub fn get_index(&self) -> TransactionIndexType {
144                self.inner().index
145            }
146        }
147    }
148}
149
150impl std::fmt::Display for TransactionOutpoint {
151    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152        let inner = self.inner();
153        write!(f, "({}, {})", inner.transaction_id, inner.index)
154    }
155}
156
157impl TryFrom<&JsValue> for TransactionOutpoint {
158    type Error = Error;
159    fn try_from(js_value: &JsValue) -> Result<Self, Self::Error> {
160        let inner: TransactionOutpointInner = js_value.as_ref().try_into()?;
161        Ok(TransactionOutpoint { inner: Arc::new(inner) })
162    }
163}
164
165impl From<cctx::TransactionOutpoint> for TransactionOutpoint {
166    fn from(outpoint: cctx::TransactionOutpoint) -> Self {
167        let transaction_id = outpoint.transaction_id;
168        let index = outpoint.index;
169        TransactionOutpoint::new(transaction_id, index)
170    }
171}
172
173impl From<TransactionOutpoint> for cctx::TransactionOutpoint {
174    fn from(outpoint: TransactionOutpoint) -> Self {
175        let inner = outpoint.inner();
176        let transaction_id = inner.transaction_id;
177        let index = inner.index;
178        cctx::TransactionOutpoint::new(transaction_id, index)
179    }
180}
181
182impl From<&TransactionOutpoint> for cctx::TransactionOutpoint {
183    fn from(outpoint: &TransactionOutpoint) -> Self {
184        let inner = outpoint.inner();
185        let transaction_id = inner.transaction_id;
186        let index = inner.index;
187        cctx::TransactionOutpoint::new(transaction_id, index)
188    }
189}
190
191impl TransactionOutpoint {
192    pub fn simulated() -> Self {
193        Self::new(TransactionId::from_slice(&rand::random::<[u8; kaspa_hashes::HASH_SIZE]>()), 0)
194    }
195}