kaspa_rpc_core/model/
tx.rs

1use borsh::{BorshDeserialize, BorshSerialize};
2use kaspa_addresses::Address;
3use kaspa_consensus_core::tx::{
4    ScriptPublicKey, ScriptVec, TransactionId, TransactionIndexType, TransactionInput, TransactionOutpoint, TransactionOutput,
5    UtxoEntry,
6};
7use kaspa_utils::{hex::ToHex, serde_bytes_fixed_ref};
8use serde::{Deserialize, Serialize};
9use workflow_serializer::prelude::*;
10
11use crate::prelude::{RpcHash, RpcScriptClass, RpcSubnetworkId};
12
13/// Represents the ID of a Kaspa transaction
14pub type RpcTransactionId = TransactionId;
15
16pub type RpcScriptVec = ScriptVec;
17pub type RpcScriptPublicKey = ScriptPublicKey;
18
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
20#[serde(rename_all = "camelCase")]
21pub struct RpcUtxoEntry {
22    pub amount: u64,
23    pub script_public_key: ScriptPublicKey,
24    pub block_daa_score: u64,
25    pub is_coinbase: bool,
26}
27
28impl RpcUtxoEntry {
29    pub fn new(amount: u64, script_public_key: ScriptPublicKey, block_daa_score: u64, is_coinbase: bool) -> Self {
30        Self { amount, script_public_key, block_daa_score, is_coinbase }
31    }
32}
33
34impl From<UtxoEntry> for RpcUtxoEntry {
35    fn from(entry: UtxoEntry) -> Self {
36        Self {
37            amount: entry.amount,
38            script_public_key: entry.script_public_key,
39            block_daa_score: entry.block_daa_score,
40            is_coinbase: entry.is_coinbase,
41        }
42    }
43}
44
45impl From<RpcUtxoEntry> for UtxoEntry {
46    fn from(entry: RpcUtxoEntry) -> Self {
47        Self {
48            amount: entry.amount,
49            script_public_key: entry.script_public_key,
50            block_daa_score: entry.block_daa_score,
51            is_coinbase: entry.is_coinbase,
52        }
53    }
54}
55
56impl Serializer for RpcUtxoEntry {
57    fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
58        store!(u8, &1, writer)?;
59        store!(u64, &self.amount, writer)?;
60        store!(ScriptPublicKey, &self.script_public_key, writer)?;
61        store!(u64, &self.block_daa_score, writer)?;
62        store!(bool, &self.is_coinbase, writer)?;
63
64        Ok(())
65    }
66}
67
68impl Deserializer for RpcUtxoEntry {
69    fn deserialize<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
70        let _version = load!(u8, reader)?;
71        let amount = load!(u64, reader)?;
72        let script_public_key = load!(ScriptPublicKey, reader)?;
73        let block_daa_score = load!(u64, reader)?;
74        let is_coinbase = load!(bool, reader)?;
75
76        Ok(Self { amount, script_public_key, block_daa_score, is_coinbase })
77    }
78}
79
80/// Represents a Kaspa transaction outpoint
81#[derive(Eq, Hash, PartialEq, Debug, Copy, Clone, Serialize, Deserialize)]
82#[serde(rename_all = "camelCase")]
83pub struct RpcTransactionOutpoint {
84    #[serde(with = "serde_bytes_fixed_ref")]
85    pub transaction_id: TransactionId,
86    pub index: TransactionIndexType,
87}
88
89impl From<TransactionOutpoint> for RpcTransactionOutpoint {
90    fn from(outpoint: TransactionOutpoint) -> Self {
91        Self { transaction_id: outpoint.transaction_id, index: outpoint.index }
92    }
93}
94
95impl From<RpcTransactionOutpoint> for TransactionOutpoint {
96    fn from(outpoint: RpcTransactionOutpoint) -> Self {
97        Self { transaction_id: outpoint.transaction_id, index: outpoint.index }
98    }
99}
100
101impl From<kaspa_consensus_client::TransactionOutpoint> for RpcTransactionOutpoint {
102    fn from(outpoint: kaspa_consensus_client::TransactionOutpoint) -> Self {
103        TransactionOutpoint::from(outpoint).into()
104    }
105}
106
107impl From<RpcTransactionOutpoint> for kaspa_consensus_client::TransactionOutpoint {
108    fn from(outpoint: RpcTransactionOutpoint) -> Self {
109        TransactionOutpoint::from(outpoint).into()
110    }
111}
112
113impl Serializer for RpcTransactionOutpoint {
114    fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
115        store!(u8, &1, writer)?;
116        store!(TransactionId, &self.transaction_id, writer)?;
117        store!(TransactionIndexType, &self.index, writer)?;
118
119        Ok(())
120    }
121}
122
123impl Deserializer for RpcTransactionOutpoint {
124    fn deserialize<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
125        let _version = load!(u8, reader)?;
126        let transaction_id = load!(TransactionId, reader)?;
127        let index = load!(TransactionIndexType, reader)?;
128
129        Ok(Self { transaction_id, index })
130    }
131}
132
133/// Represents a Kaspa transaction input
134#[derive(Clone, Serialize, Deserialize)]
135#[serde(rename_all = "camelCase")]
136pub struct RpcTransactionInput {
137    pub previous_outpoint: RpcTransactionOutpoint,
138    #[serde(with = "hex::serde")]
139    pub signature_script: Vec<u8>,
140    pub sequence: u64,
141    pub sig_op_count: u8,
142    pub verbose_data: Option<RpcTransactionInputVerboseData>,
143}
144
145impl std::fmt::Debug for RpcTransactionInput {
146    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147        f.debug_struct("RpcTransactionInput")
148            .field("previous_outpoint", &self.previous_outpoint)
149            .field("signature_script", &self.signature_script.to_hex())
150            .field("sequence", &self.sequence)
151            .field("sig_op_count", &self.sig_op_count)
152            .field("verbose_data", &self.verbose_data)
153            .finish()
154    }
155}
156
157impl From<TransactionInput> for RpcTransactionInput {
158    fn from(input: TransactionInput) -> Self {
159        Self {
160            previous_outpoint: input.previous_outpoint.into(),
161            signature_script: input.signature_script,
162            sequence: input.sequence,
163            sig_op_count: input.sig_op_count,
164            verbose_data: None,
165        }
166    }
167}
168
169impl RpcTransactionInput {
170    pub fn from_transaction_inputs(other: Vec<TransactionInput>) -> Vec<Self> {
171        other.into_iter().map(Self::from).collect()
172    }
173}
174
175impl Serializer for RpcTransactionInput {
176    fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
177        store!(u8, &1, writer)?;
178        serialize!(RpcTransactionOutpoint, &self.previous_outpoint, writer)?;
179        store!(Vec<u8>, &self.signature_script, writer)?;
180        store!(u64, &self.sequence, writer)?;
181        store!(u8, &self.sig_op_count, writer)?;
182        serialize!(Option<RpcTransactionInputVerboseData>, &self.verbose_data, writer)?;
183
184        Ok(())
185    }
186}
187
188impl Deserializer for RpcTransactionInput {
189    fn deserialize<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
190        let _version = load!(u8, reader)?;
191        let previous_outpoint = deserialize!(RpcTransactionOutpoint, reader)?;
192        let signature_script = load!(Vec<u8>, reader)?;
193        let sequence = load!(u64, reader)?;
194        let sig_op_count = load!(u8, reader)?;
195        let verbose_data = deserialize!(Option<RpcTransactionInputVerboseData>, reader)?;
196
197        Ok(Self { previous_outpoint, signature_script, sequence, sig_op_count, verbose_data })
198    }
199}
200
201/// Represent Kaspa transaction input verbose data
202#[derive(Clone, Debug, Serialize, Deserialize)]
203#[serde(rename_all = "camelCase")]
204pub struct RpcTransactionInputVerboseData {}
205
206impl Serializer for RpcTransactionInputVerboseData {
207    fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
208        store!(u8, &1, writer)?;
209        Ok(())
210    }
211}
212
213impl Deserializer for RpcTransactionInputVerboseData {
214    fn deserialize<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
215        let _version = load!(u8, reader)?;
216        Ok(Self {})
217    }
218}
219
220/// Represents a Kaspad transaction output
221#[derive(Clone, Debug, Serialize, Deserialize)]
222#[serde(rename_all = "camelCase")]
223pub struct RpcTransactionOutput {
224    pub value: u64,
225    pub script_public_key: RpcScriptPublicKey,
226    pub verbose_data: Option<RpcTransactionOutputVerboseData>,
227}
228
229impl RpcTransactionOutput {
230    pub fn from_transaction_outputs(other: Vec<TransactionOutput>) -> Vec<Self> {
231        other.into_iter().map(Self::from).collect()
232    }
233}
234
235impl From<TransactionOutput> for RpcTransactionOutput {
236    fn from(output: TransactionOutput) -> Self {
237        Self { value: output.value, script_public_key: output.script_public_key, verbose_data: None }
238    }
239}
240
241impl Serializer for RpcTransactionOutput {
242    fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
243        store!(u8, &1, writer)?;
244        store!(u64, &self.value, writer)?;
245        store!(RpcScriptPublicKey, &self.script_public_key, writer)?;
246        serialize!(Option<RpcTransactionOutputVerboseData>, &self.verbose_data, writer)?;
247
248        Ok(())
249    }
250}
251
252impl Deserializer for RpcTransactionOutput {
253    fn deserialize<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
254        let _version = load!(u8, reader)?;
255        let value = load!(u64, reader)?;
256        let script_public_key = load!(RpcScriptPublicKey, reader)?;
257        let verbose_data = deserialize!(Option<RpcTransactionOutputVerboseData>, reader)?;
258
259        Ok(Self { value, script_public_key, verbose_data })
260    }
261}
262
263/// Represent Kaspa transaction output verbose data
264#[derive(Clone, Debug, Serialize, Deserialize)]
265#[serde(rename_all = "camelCase")]
266pub struct RpcTransactionOutputVerboseData {
267    pub script_public_key_type: RpcScriptClass,
268    pub script_public_key_address: Address,
269}
270
271impl Serializer for RpcTransactionOutputVerboseData {
272    fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
273        store!(u8, &1, writer)?;
274        store!(RpcScriptClass, &self.script_public_key_type, writer)?;
275        store!(Address, &self.script_public_key_address, writer)?;
276
277        Ok(())
278    }
279}
280
281impl Deserializer for RpcTransactionOutputVerboseData {
282    fn deserialize<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
283        let _version = load!(u8, reader)?;
284        let script_public_key_type = load!(RpcScriptClass, reader)?;
285        let script_public_key_address = load!(Address, reader)?;
286
287        Ok(Self { script_public_key_type, script_public_key_address })
288    }
289}
290
291/// Represents a Kaspa transaction
292#[derive(Clone, Serialize, Deserialize)]
293#[serde(rename_all = "camelCase")]
294pub struct RpcTransaction {
295    pub version: u16,
296    pub inputs: Vec<RpcTransactionInput>,
297    pub outputs: Vec<RpcTransactionOutput>,
298    pub lock_time: u64,
299    pub subnetwork_id: RpcSubnetworkId,
300    pub gas: u64,
301    #[serde(with = "hex::serde")]
302    pub payload: Vec<u8>,
303    pub mass: u64,
304    pub verbose_data: Option<RpcTransactionVerboseData>,
305}
306
307impl std::fmt::Debug for RpcTransaction {
308    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
309        f.debug_struct("RpcTransaction")
310            .field("version", &self.version)
311            .field("lock_time", &self.lock_time)
312            .field("subnetwork_id", &self.subnetwork_id)
313            .field("gas", &self.gas)
314            .field("payload", &self.payload.to_hex())
315            .field("mass", &self.mass)
316            .field("inputs", &self.inputs) // Inputs and outputs are placed purposely at the end for better debug visibility 
317            .field("outputs", &self.outputs)
318            .field("verbose_data", &self.verbose_data)
319            .finish()
320    }
321}
322
323impl Serializer for RpcTransaction {
324    fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
325        store!(u16, &1, writer)?;
326        store!(u16, &self.version, writer)?;
327        serialize!(Vec<RpcTransactionInput>, &self.inputs, writer)?;
328        serialize!(Vec<RpcTransactionOutput>, &self.outputs, writer)?;
329        store!(u64, &self.lock_time, writer)?;
330        store!(RpcSubnetworkId, &self.subnetwork_id, writer)?;
331        store!(u64, &self.gas, writer)?;
332        store!(Vec<u8>, &self.payload, writer)?;
333        store!(u64, &self.mass, writer)?;
334        serialize!(Option<RpcTransactionVerboseData>, &self.verbose_data, writer)?;
335
336        Ok(())
337    }
338}
339
340impl Deserializer for RpcTransaction {
341    fn deserialize<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
342        let _struct_version = load!(u16, reader)?;
343        let version = load!(u16, reader)?;
344        let inputs = deserialize!(Vec<RpcTransactionInput>, reader)?;
345        let outputs = deserialize!(Vec<RpcTransactionOutput>, reader)?;
346        let lock_time = load!(u64, reader)?;
347        let subnetwork_id = load!(RpcSubnetworkId, reader)?;
348        let gas = load!(u64, reader)?;
349        let payload = load!(Vec<u8>, reader)?;
350        let mass = load!(u64, reader)?;
351        let verbose_data = deserialize!(Option<RpcTransactionVerboseData>, reader)?;
352
353        Ok(Self { version, inputs, outputs, lock_time, subnetwork_id, gas, payload, mass, verbose_data })
354    }
355}
356
357/// Represent Kaspa transaction verbose data
358#[derive(Clone, Debug, Serialize, Deserialize)]
359#[serde(rename_all = "camelCase")]
360pub struct RpcTransactionVerboseData {
361    pub transaction_id: RpcTransactionId,
362    pub hash: RpcHash,
363    pub compute_mass: u64,
364    pub block_hash: RpcHash,
365    pub block_time: u64,
366}
367
368impl Serializer for RpcTransactionVerboseData {
369    fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
370        store!(u8, &1, writer)?;
371        store!(RpcTransactionId, &self.transaction_id, writer)?;
372        store!(RpcHash, &self.hash, writer)?;
373        store!(u64, &self.compute_mass, writer)?;
374        store!(RpcHash, &self.block_hash, writer)?;
375        store!(u64, &self.block_time, writer)?;
376
377        Ok(())
378    }
379}
380
381impl Deserializer for RpcTransactionVerboseData {
382    fn deserialize<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
383        let _version = load!(u8, reader)?;
384        let transaction_id = load!(RpcTransactionId, reader)?;
385        let hash = load!(RpcHash, reader)?;
386        let compute_mass = load!(u64, reader)?;
387        let block_hash = load!(RpcHash, reader)?;
388        let block_time = load!(u64, reader)?;
389
390        Ok(Self { transaction_id, hash, compute_mass, block_hash, block_time })
391    }
392}
393
394/// Represents accepted transaction ids
395#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
396#[serde(rename_all = "camelCase")]
397pub struct RpcAcceptedTransactionIds {
398    pub accepting_block_hash: RpcHash,
399    pub accepted_transaction_ids: Vec<RpcTransactionId>,
400}