use crate::{
Blockchain, TxPool,
rpc_server::{RpcServerError, parse_hex_bytes},
strings::rpc_server::transaction,
};
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use std::sync::{
Arc,
atomic::{AtomicU64, Ordering},
};
static OPERATION_ID_COUNTER: AtomicU64 = AtomicU64::new(1);
fn generate_operation_id() -> String {
let id = OPERATION_ID_COUNTER.fetch_add(1, Ordering::SeqCst);
format!("{}-{id}", transaction::OPERATION_ID_PREFIX)
}
#[rpc(server, namespace = "transaction")]
pub trait TransactionApi {
#[method(name = "v1_broadcast")]
async fn broadcast(&self, transaction: String) -> RpcResult<Option<String>>;
#[method(name = "v1_stop")]
async fn stop(&self, operation_id: String) -> RpcResult<()>;
}
pub struct TransactionApi {
blockchain: Arc<Blockchain>,
txpool: Arc<TxPool>,
}
impl TransactionApi {
pub fn new(blockchain: Arc<Blockchain>, txpool: Arc<TxPool>) -> Self {
Self { blockchain, txpool }
}
}
#[async_trait::async_trait]
impl TransactionApiServer for TransactionApi {
async fn broadcast(&self, transaction: String) -> RpcResult<Option<String>> {
let tx_bytes = parse_hex_bytes(&transaction, "transaction")?;
self.txpool
.submit(tx_bytes)
.map_err(|e| RpcServerError::Internal(format!("Failed to submit transaction: {e}")))?;
let pending_txs = self.txpool.drain().map_err(|e| {
RpcServerError::Internal(format!("Failed to drain transaction pool: {e}"))
})?;
self.blockchain
.build_block(pending_txs)
.await
.map_err(|e| RpcServerError::Internal(format!("Failed to build block: {e}")))?;
Ok(Some(generate_operation_id()))
}
async fn stop(&self, _operation_id: String) -> RpcResult<()> {
Ok(())
}
}