1use crate::error::Error;
6use crate::types::{GetRawTransaction, ScriptPubKey, ScriptSig, Vin, Vout};
7use bitcoin::consensus::encode::{deserialize_hex, serialize_hex};
8use bitcoin::{Address, Amount, BlockHash, Network, OutPoint, Transaction, Txid};
9use jsonrpsee::proc_macros::rpc;
10use sc_client_api::{AuxStore, BlockBackend, HeaderBackend};
11use sp_runtime::traits::Block as BlockT;
12use std::marker::PhantomData;
13use std::sync::Arc;
14use subcoin_network::{NetworkApi, SendTransactionResult};
15use subcoin_primitives::{BitcoinTransactionAdapter, TransactionIndex, TxPosition};
16use subcoin_script::solve;
17
18#[rpc(client, server)]
20pub trait RawTransactionApi {
21 #[method(name = "getrawtransaction")]
29 async fn get_raw_transaction(
30 &self,
31 txid: Txid,
32 verbose: Option<bool>,
33 blockhash: Option<BlockHash>,
34 ) -> Result<serde_json::Value, Error>;
35
36 #[method(name = "sendrawtransaction")]
40 async fn send_raw_transaction(
41 &self,
42 hexstring: String,
43 maxfeerate: Option<f64>,
44 ) -> Result<SendTransactionResult, Error>;
45
46 #[method(name = "decoderawtransaction", blocking)]
48 fn decode_raw_transaction(
49 &self,
50 hexstring: String,
51 iswitness: Option<bool>,
52 ) -> Result<serde_json::Value, Error>;
53}
54
55pub struct RawTransaction<Block, Client, TransactionAdapter> {
57 client: Arc<Client>,
58 network_api: Arc<dyn NetworkApi>,
59 transaction_index: Arc<dyn TransactionIndex + Send + Sync>,
60 network: Network,
61 _phantom: PhantomData<(Block, TransactionAdapter)>,
62}
63
64impl<Block, Client, TransactionAdapter> RawTransaction<Block, Client, TransactionAdapter>
65where
66 Block: BlockT + 'static,
67 Client: HeaderBackend<Block> + BlockBackend<Block> + AuxStore + 'static,
68{
69 pub fn new(
71 client: Arc<Client>,
72 network_api: Arc<dyn NetworkApi>,
73 transaction_index: Arc<dyn TransactionIndex + Send + Sync>,
74 network: Network,
75 ) -> Self {
76 Self {
77 client,
78 network_api,
79 transaction_index,
80 network,
81 _phantom: Default::default(),
82 }
83 }
84
85 fn best_number(&self) -> u32 {
86 use sp_runtime::traits::SaturatedConversion;
87 self.client.info().best_number.saturated_into()
88 }
89
90 fn calculate_confirmations(&self, block_height: u32) -> i32 {
91 let best = self.best_number();
92 if block_height > best {
93 0
94 } else {
95 (best - block_height + 1) as i32
96 }
97 }
98
99 fn tx_to_get_raw_transaction(
100 &self,
101 tx: &Transaction,
102 block_hash: Option<BlockHash>,
103 block_height: Option<u32>,
104 block_time: Option<u32>,
105 ) -> GetRawTransaction {
106 let txid = tx.compute_txid();
107 let wtxid = tx.compute_wtxid();
108
109 let size = tx.total_size() as u32;
111 let vsize = tx.vsize() as u32;
112 let weight = tx.weight().to_wu() as u32;
113
114 let vin: Vec<Vin> = tx
116 .input
117 .iter()
118 .map(|input| {
119 if input.previous_output == OutPoint::null() {
120 Vin {
122 txid: None,
123 vout: None,
124 script_sig: None,
125 txinwitness: if !input.witness.is_empty() {
126 Some(input.witness.iter().map(hex::encode).collect())
127 } else {
128 None
129 },
130 sequence: input.sequence.0,
131 coinbase: Some(hex::encode(input.script_sig.as_bytes())),
132 }
133 } else {
134 Vin {
135 txid: Some(input.previous_output.txid),
136 vout: Some(input.previous_output.vout),
137 script_sig: Some(ScriptSig {
138 asm: input.script_sig.to_asm_string(),
139 hex: hex::encode(input.script_sig.as_bytes()),
140 }),
141 txinwitness: if !input.witness.is_empty() {
142 Some(input.witness.iter().map(hex::encode).collect())
143 } else {
144 None
145 },
146 sequence: input.sequence.0,
147 coinbase: None,
148 }
149 }
150 })
151 .collect();
152
153 let vout: Vec<Vout> = tx
155 .output
156 .iter()
157 .enumerate()
158 .map(|(n, output)| {
159 let script = &output.script_pubkey;
160 let address = Address::from_script(script, self.network)
161 .ok()
162 .map(|a| a.to_string());
163
164 let script_type = solve(script).script_type();
165
166 Vout {
167 value: Amount::from_sat(output.value.to_sat()).to_btc(),
168 n: n as u32,
169 script_pub_key: ScriptPubKey {
170 asm: script.to_asm_string(),
171 hex: hex::encode(script.as_bytes()),
172 script_type: script_type.to_string(),
173 address,
174 },
175 }
176 })
177 .collect();
178
179 let confirmations = block_height.map(|h| self.calculate_confirmations(h));
180
181 GetRawTransaction {
182 in_active_chain: block_hash.map(|_| true),
183 hex: serialize_hex(tx),
184 txid,
185 hash: Txid::from_raw_hash(*wtxid.as_raw_hash()),
186 size,
187 vsize,
188 weight,
189 version: tx.version.0,
190 locktime: tx.lock_time.to_consensus_u32(),
191 vin,
192 vout,
193 blockhash: block_hash,
194 confirmations,
195 blocktime: block_time,
196 time: block_time,
197 }
198 }
199}
200
201#[async_trait::async_trait]
202impl<Block, Client, TransactionAdapter> RawTransactionApiServer
203 for RawTransaction<Block, Client, TransactionAdapter>
204where
205 Block: BlockT + 'static,
206 Client: HeaderBackend<Block> + BlockBackend<Block> + AuxStore + 'static,
207 TransactionAdapter: BitcoinTransactionAdapter<Block> + Send + Sync + 'static,
208{
209 async fn get_raw_transaction(
210 &self,
211 txid: Txid,
212 verbose: Option<bool>,
213 _blockhash: Option<BlockHash>,
214 ) -> Result<serde_json::Value, Error> {
215 if let Some(tx) = self.network_api.get_transaction(txid).await {
217 if verbose.unwrap_or(false) {
218 let result = self.tx_to_get_raw_transaction(&tx, None, None, None);
219 return Ok(serde_json::to_value(result)?);
220 } else {
221 return Ok(serde_json::Value::String(serialize_hex(&tx)));
222 }
223 }
224
225 let Some(TxPosition {
227 block_number,
228 index,
229 }) = self.transaction_index.tx_index(txid)?
230 else {
231 return Err(Error::TransactionNotFound);
232 };
233
234 let substrate_block_hash = self
236 .client
237 .hash(block_number.into())?
238 .ok_or(Error::BlockNotFound)?;
239
240 let substrate_block = self
241 .client
242 .block(substrate_block_hash)?
243 .ok_or(Error::BlockNotFound)?
244 .block;
245
246 let bitcoin_block =
247 subcoin_primitives::convert_to_bitcoin_block::<Block, TransactionAdapter>(
248 substrate_block,
249 )
250 .map_err(Error::Header)?;
251
252 let tx = bitcoin_block
253 .txdata
254 .into_iter()
255 .nth(index as usize)
256 .ok_or(Error::TransactionNotFound)?;
257
258 let block_hash = bitcoin_block.header.block_hash();
259
260 if verbose.unwrap_or(false) {
261 let result = self.tx_to_get_raw_transaction(
262 &tx,
263 Some(block_hash),
264 Some(block_number),
265 Some(bitcoin_block.header.time),
266 );
267 Ok(serde_json::to_value(result)?)
268 } else {
269 Ok(serde_json::Value::String(serialize_hex(&tx)))
270 }
271 }
272
273 async fn send_raw_transaction(
274 &self,
275 hexstring: String,
276 _maxfeerate: Option<f64>,
277 ) -> Result<SendTransactionResult, Error> {
278 if !self.network_api.enabled() {
279 return Err(Error::NetworkUnavailable);
280 }
281
282 let tx: Transaction = deserialize_hex(&hexstring)?;
283 Ok(self.network_api.send_transaction(tx).await)
284 }
285
286 fn decode_raw_transaction(
287 &self,
288 hexstring: String,
289 _iswitness: Option<bool>,
290 ) -> Result<serde_json::Value, Error> {
291 let tx: Transaction = deserialize_hex(&hexstring)?;
292 let result = self.tx_to_get_raw_transaction(&tx, None, None, None);
293 Ok(serde_json::to_value(result)?)
294 }
295}