Skip to content

Commit

Permalink
add get_transaction and get_transaction_info RPCs
Browse files Browse the repository at this point in the history
  • Loading branch information
Ash-L2L committed May 6, 2024
1 parent 5796aa8 commit 361607c
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 28 deletions.
9 changes: 5 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ members = [
[workspace.package]
authors = [ "Ash Manning <[email protected]>" ]
edition = "2021"
version = "0.7.0"
version = "0.7.1"

[workspace.dependencies.bip300301]
git = "https://github.com/Ash-L2L/bip300301.git"
Expand Down
4 changes: 2 additions & 2 deletions app/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ impl App {
.map(|(outpoint, txid)| {
let inclusions = self.node.get_tx_inclusions(txid)?;
let Some(block_hash) =
inclusions.into_iter().try_find(|block_hash| {
inclusions.into_keys().try_find(|block_hash| {
self.node.is_descendant(*block_hash, tip)
})?
else {
Expand All @@ -184,7 +184,7 @@ impl App {
)
};
let inclusions = self.node.get_tx_inclusions(txid)?;
let Some(block_hash) = inclusions.into_iter().try_find(|block_hash| {
let Some(block_hash) = inclusions.into_keys().try_find(|block_hash| {
self.node.is_descendant(*block_hash, tip)
})? else {
return Ok((spent_output.inpoint, None));
Expand Down
46 changes: 45 additions & 1 deletion app/rpc_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use plain_bitnames::{
},
wallet,
};
use plain_bitnames_app_rpc_api::RpcServer;
use plain_bitnames_app_rpc_api::{RpcServer, TxInfo};

use crate::app::{self, App};

Expand Down Expand Up @@ -94,6 +94,50 @@ impl RpcServer for RpcServerImpl {
self.app.get_paymail(None).map_err(convert_app_err)
}

async fn get_transaction(
&self,
txid: Txid,
) -> RpcResult<Option<Transaction>> {
self.app
.node
.try_get_transaction(txid)
.map_err(convert_node_err)
}

async fn get_transaction_info(
&self,
txid: Txid,
) -> RpcResult<Option<TxInfo>> {
let Some((filled_tx, txin)) = self
.app
.node
.try_get_filled_transaction(txid)
.map_err(convert_node_err)?
else {
return Ok(None);
};
let confirmations = match txin {
Some(txin) => {
let tip_height =
self.app.node.get_tip_height().map_err(convert_node_err)?;
let height = self
.app
.node
.get_height(txin.block_hash)
.map_err(convert_node_err)?;
Some(tip_height - height)
}
None => None,
};
let fee_sats = filled_tx.transaction.fee().unwrap();
let res = TxInfo {
confirmations,
fee_sats,
txin,
};
Ok(Some(res))
}

async fn getblockcount(&self) -> RpcResult<u32> {
self.app.node.get_tip_height().map_err(convert_node_err)
}
Expand Down
27 changes: 15 additions & 12 deletions lib/archive.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{cmp::Ordering, collections::BTreeSet};
use std::{cmp::Ordering, collections::BTreeMap};

use bip300301::{
bitcoin::{self, block::Header as BitcoinHeader, hashes::Hash},
Expand Down Expand Up @@ -56,9 +56,9 @@ pub struct Archive {
/// All ancestors of any block should always be present
total_work:
Database<SerdeBincode<bitcoin::BlockHash>, SerdeBincode<bitcoin::Work>>,
/// Blocks in which a tx has been included
/// Blocks in which a tx has been included, and index within the block
txid_to_inclusions:
Database<SerdeBincode<Txid>, SerdeBincode<BTreeSet<BlockHash>>>,
Database<SerdeBincode<Txid>, SerdeBincode<BTreeMap<BlockHash, u32>>>,
}

impl Archive {
Expand Down Expand Up @@ -255,12 +255,12 @@ impl Archive {
.ok_or(Error::NoMainHeader(block_hash))
}

/// Get blocks in which a tx was included.
/// Get blocks in which a tx was included, and tx index within each block
pub fn get_tx_inclusions(
&self,
rotxn: &RoTxn,
txid: Txid,
) -> Result<BTreeSet<BlockHash>, Error> {
) -> Result<BTreeMap<BlockHash, u32>, Error> {
let inclusions = self
.txid_to_inclusions
.get(rotxn, &txid)?
Expand Down Expand Up @@ -289,13 +289,16 @@ impl Archive {
) -> Result<(), Error> {
let _header = self.get_header(rwtxn, block_hash)?;
self.bodies.put(rwtxn, &block_hash, body)?;
body.transactions.iter().try_for_each(|tx| {
let txid = tx.txid();
let mut inclusions = self.get_tx_inclusions(rwtxn, txid)?;
inclusions.insert(block_hash);
self.txid_to_inclusions.put(rwtxn, &txid, &inclusions)?;
Ok(())
})
body.transactions
.iter()
.enumerate()
.try_for_each(|(txin, tx)| {
let txid = tx.txid();
let mut inclusions = self.get_tx_inclusions(rwtxn, txid)?;
inclusions.insert(block_hash, txin as u32);
self.txid_to_inclusions.put(rwtxn, &txid, &inclusions)?;
Ok(())
})
}

/// Store deposit info for a block
Expand Down
1 change: 1 addition & 0 deletions lib/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![feature(let_chains)]
#![feature(try_find)]

pub mod archive;
pub mod authorization;
Expand Down
83 changes: 77 additions & 6 deletions lib/node.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::{
collections::{BTreeSet, HashMap, HashSet},
collections::{BTreeMap, HashMap, HashSet},
fmt::Debug,
net::SocketAddr,
path::Path,
Expand Down Expand Up @@ -35,7 +35,8 @@ use crate::{
types::{
Address, Authorized, AuthorizedTransaction, BitName, BitNameData,
Block, BlockHash, Body, FilledOutput, FilledTransaction, GetValue,
Header, MerkleRoot, OutPoint, SpentOutput, Txid, WithdrawalBundle,
Header, MerkleRoot, OutPoint, SpentOutput, Transaction, TxIn, Txid,
WithdrawalBundle,
},
};

Expand Down Expand Up @@ -771,6 +772,9 @@ impl NetTask {
}
}

pub type FilledTransactionWithPosition =
(Authorized<FilledTransaction>, Option<TxIn>);

#[derive(Clone)]
pub struct Node {
archive: Archive,
Expand Down Expand Up @@ -893,11 +897,11 @@ impl Node {
Ok(self.archive.get_height(&rotxn, block_hash)?)
}

/// Get blocks in which a tx was included.
/// Get blocks in which a tx was included, and tx index within those blocks
pub fn get_tx_inclusions(
&self,
txid: Txid,
) -> Result<BTreeSet<BlockHash>, Error> {
) -> Result<BTreeMap<BlockHash, u32>, Error> {
let rotxn = self.env.read_txn()?;
Ok(self.archive.get_tx_inclusions(&rotxn, txid)?)
}
Expand Down Expand Up @@ -1113,13 +1117,80 @@ impl Node {
Ok((returned_transactions, fee))
}

/// get a transaction from the archive or mempool, if it exists
pub fn try_get_transaction(
&self,
txid: Txid,
) -> Result<Option<Transaction>, Error> {
let rotxn = self.env.read_txn()?;
if let Some((block_hash, txin)) = self
.archive
.get_tx_inclusions(&rotxn, txid)?
.first_key_value()
{
let body = self.archive.get_body(&rotxn, *block_hash)?;
let tx = body.transactions.into_iter().nth(*txin as usize).unwrap();
Ok(Some(tx))
} else if let Some(auth_tx) =
self.mempool.transactions.get(&rotxn, &txid)?
{
Ok(Some(auth_tx.transaction))
} else {
Ok(None)
}
}

/// get a filled transaction from the archive/state or mempool,
/// and the tx index, if the transaction exists
/// and can be filled with the current state.
/// a tx index of `None` indicates that the tx is in the mempool.
pub fn try_get_filled_transaction(
&self,
txid: Txid,
) -> Result<Option<FilledTransactionWithPosition>, Error> {
let rotxn = self.env.read_txn()?;
let tip = self.state.get_tip(&rotxn)?;
let inclusions = self.archive.get_tx_inclusions(&rotxn, txid)?;
if let Some((block_hash, idx)) =
inclusions.into_iter().try_find(|(block_hash, _)| {
self.archive.is_descendant(&rotxn, *block_hash, tip)
})?
{
let body = self.archive.get_body(&rotxn, block_hash)?;
let auth_txs = body.authorized_transactions();
let auth_tx = auth_txs.into_iter().nth(idx as usize).unwrap();
let filled_tx = self
.state
.fill_transaction_from_stxos(&rotxn, auth_tx.transaction)?;
let auth_tx = Authorized {
transaction: filled_tx,
authorizations: auth_tx.authorizations,
};
let txin = TxIn { block_hash, idx };
let res = (auth_tx, Some(txin));
return Ok(Some(res));
}
if let Some(auth_tx) = self.mempool.transactions.get(&rotxn, &txid)? {
match self.state.fill_authorized_transaction(&rotxn, auth_tx) {
Ok(filled_tx) => {
let res = (filled_tx, None);
Ok(Some(res))
}
Err(state::Error::NoUtxo { .. }) => Ok(None),
Err(err) => Err(err.into()),
}
} else {
Ok(None)
}
}

pub fn get_pending_withdrawal_bundle(
&self,
) -> Result<Option<WithdrawalBundle>, Error> {
let txn = self.env.read_txn()?;
let rotxn = self.env.read_txn()?;
let bundle = self
.state
.get_pending_withdrawal_bundle(&txn)?
.get_pending_withdrawal_bundle(&rotxn)?
.map(|(bundle, _)| bundle);
Ok(bundle)
}
Expand Down
2 changes: 1 addition & 1 deletion lib/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,7 @@ impl State {
}

/// Fill a transaction that has already been applied
fn fill_transaction_from_stxos(
pub fn fill_transaction_from_stxos(
&self,
rotxn: &RoTxn,
tx: Transaction,
Expand Down
7 changes: 7 additions & 0 deletions lib/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,3 +465,10 @@ impl PartialOrd for AggregatedWithdrawal {
Some(self.cmp(other))
}
}

/// Transaction index
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct TxIn {
pub block_hash: BlockHash,
pub idx: u32,
}
1 change: 1 addition & 0 deletions rpc-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ version.workspace = true
bip300301.workspace = true
jsonrpsee = { version = "0.20.0", features = ["macros"] }
plain_bitnames = { path = "../lib" }
serde = { version = "1.0.179", features = ["derive"] }

[lib]
name = "plain_bitnames_app_rpc_api"
Expand Down
24 changes: 23 additions & 1 deletion rpc-api/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,16 @@ use bip300301::bitcoin;
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use plain_bitnames::types::{
hashes::BitName, Address, BitNameData, Block, BlockHash, FilledOutput,
OutPoint, Txid,
OutPoint, Transaction, TxIn, Txid,
};
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct TxInfo {
pub confirmations: Option<u32>,
pub fee_sats: u64,
pub txin: Option<TxIn>,
}

#[rpc(client, server)]
pub trait Rpc {
Expand Down Expand Up @@ -46,6 +54,20 @@ pub trait Rpc {
#[method(name = "get_paymail")]
async fn get_paymail(&self) -> RpcResult<HashMap<OutPoint, FilledOutput>>;

/// Get transaction by txid
#[method(name = "get_transaction")]
async fn get_transaction(
&self,
txid: Txid,
) -> RpcResult<Option<Transaction>>;

/// Get information about a transaction in the current chain
#[method(name = "get_transaction_info")]
async fn get_transaction_info(
&self,
txid: Txid,
) -> RpcResult<Option<TxInfo>>;

/// Get the current block count
#[method(name = "getblockcount")]
async fn getblockcount(&self) -> RpcResult<u32>;
Expand Down

0 comments on commit 361607c

Please sign in to comment.