Skip to main content

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