Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion app/rpc_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use thunder::{
types::{Address, PointedOutput, Txid, WithdrawalBundle},
wallet::Balance,
};
use thunder_app_rpc_api::RpcServer;
use thunder_app_rpc_api::{GetTransactionResponse, RpcServer};
use tower_http::{
cors::CorsLayer,
request_id::{
Expand Down Expand Up @@ -143,6 +143,19 @@ impl RpcServer for RpcServerImpl {
self.app.wallet.get_new_address().map_err(custom_err)
}

async fn get_transaction(
&self,
txid: Txid,
) -> RpcResult<Option<GetTransactionResponse>> {
let res = self
.app
.node
.try_get_transaction(txid)
.map_err(custom_err)?
.map(|(tx, block_hash)| GetTransactionResponse { tx, block_hash });
Ok(res)
}

async fn get_wallet_addresses(&self) -> RpcResult<Vec<Address>> {
let addrs = self.app.wallet.get_addresses().map_err(custom_err)?;
let mut res: Vec<_> = addrs.into_iter().collect();
Expand Down
6 changes: 6 additions & 0 deletions cli/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ pub enum Command {
},
/// Get a new address
GetNewAddress,
/// Get transaction by txid
GetTransaction { txid: Txid },
/// Get wallet addresses, sorted by base58 encoding
GetWalletAddresses,
/// Get wallet UTXOs
Expand Down Expand Up @@ -166,6 +168,10 @@ where
let address = rpc_client.get_new_address().await?;
format!("{address}")
}
Command::GetTransaction { txid } => {
let tx_info = rpc_client.get_transaction(txid).await?;
serde_json::to_string_pretty(&tx_info)?
}
Command::GetWalletAddresses => {
let addresses = rpc_client.get_wallet_addresses().await?;
serde_json::to_string_pretty(&addresses)?
Expand Down
55 changes: 46 additions & 9 deletions lib/archive.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
use std::{
cmp::Ordering,
collections::{HashMap, HashSet},
collections::{BTreeMap, HashMap, HashSet},
path::PathBuf,
};

use bitcoin::{self, hashes::Hash as _};
use fallible_iterator::{FallibleIterator, IteratorExt};
use heed::types::SerdeBincode;
use sneed::{
DatabaseUnique, EnvError, RoTxn, RwTxn, UnitKey,
db::error::Error as DbError, rwtxn::Error as RwTxnError,
DatabaseUnique, DbError, EnvError, RoTxn, RwTxn, RwTxnError, UnitKey, db,
env,
};

use crate::types::{
Accumulator, BlockHash, BmmResult, Body, Header, Tip, VERSION, Version,
proto::mainchain,
Accumulator, BlockHash, BmmResult, Body, Header, Tip, Txid, VERSION,
Version, proto::mainchain,
};

#[derive(Debug, thiserror::Error)]
#[allow(clippy::duplicated_attributes)]
#[derive(Debug, thiserror::Error, transitive::Transitive)]
#[transitive(
from(db::error::Put, DbError),
from(db::error::TryGet, DbError),
from(env::error::CreateDb, EnvError)
)]
pub enum Error {
#[error(transparent)]
Db(#[from] DbError),
Expand Down Expand Up @@ -136,11 +142,16 @@ pub struct Archive {
SerdeBincode<bitcoin::BlockHash>,
SerdeBincode<bitcoin::Work>,
>,
/// Blocks in which a tx has been included, and index within the block
txid_to_inclusions: DatabaseUnique<
SerdeBincode<Txid>,
SerdeBincode<BTreeMap<BlockHash, u32>>,
>,
_version: DatabaseUnique<UnitKey, SerdeBincode<Version>>,
}

impl Archive {
pub const NUM_DBS: u32 = 14;
pub const NUM_DBS: u32 = 15;

pub fn new(env: &sneed::Env) -> Result<Self, Error> {
let mut rwtxn = env.write_txn().map_err(EnvError::from)?;
Expand All @@ -152,10 +163,11 @@ impl Archive {
if db_version
< Version {
major: 0,
minor: 13,
minor: 15,
patch: 0,
} =>
{
// `txid_to_inclusions` added in 0.15.0
// Merkle root structure changed in 0.13.0
// `deposits` and `main_bmm_commitments` were removed in
// 0.12.0, and `main_block_infos` was added
Expand Down Expand Up @@ -229,6 +241,8 @@ impl Archive {
}
let total_work = DatabaseUnique::create(env, &mut rwtxn, "total_work")
.map_err(EnvError::from)?;
let txid_to_inclusions =
DatabaseUnique::create(env, &mut rwtxn, "txid_to_inclusions")?;
rwtxn.commit().map_err(RwTxnError::from)?;
Ok(Self {
accumulators,
Expand All @@ -244,6 +258,7 @@ impl Archive {
main_successors,
successors,
total_work,
txid_to_inclusions,
_version: version,
})
}
Expand Down Expand Up @@ -496,6 +511,19 @@ impl Archive {
.ok_or(Error::NoMainHeaderInfo(block_hash))
}

/// 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<BTreeMap<BlockHash, u32>, Error> {
let inclusions = self
.txid_to_inclusions
.try_get(rotxn, &txid)?
.unwrap_or_default();
Ok(inclusions)
}

/// Try to get the best valid mainchain verification for the specified block.
pub fn try_get_best_main_verification(
&self,
Expand Down Expand Up @@ -690,7 +718,16 @@ impl Archive {
self.bodies
.put(rwtxn, &block_hash, body)
.map_err(DbError::from)?;
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 a header.
Expand Down
7 changes: 4 additions & 3 deletions lib/mempool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@ use std::collections::VecDeque;
use fallible_iterator::FallibleIterator as _;
use heed::types::SerdeBincode;
use sneed::{
DatabaseUnique, EnvError, RoTxn, RwTxn, RwTxnError, UnitKey,
db::error::Error as DbError,
DatabaseUnique, DbError, EnvError, RoTxn, RwTxn, RwTxnError, UnitKey, db,
};

use crate::types::{
Accumulator, AuthorizedTransaction, OutPoint, Txid, UtreexoError, VERSION,
Version,
};

#[derive(Debug, thiserror::Error)]
#[allow(clippy::duplicated_attributes)]
#[derive(Debug, thiserror::Error, transitive::Transitive)]
#[transitive(from(db::error::TryGet, DbError))]
pub enum Error {
#[error(transparent)]
Db(#[from] DbError),
Expand Down
37 changes: 36 additions & 1 deletion lib/node/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::{
};

use bitcoin::amount::CheckedSum;
use fallible_iterator::FallibleIterator;
use fallible_iterator::{FallibleIterator, IteratorExt};
use futures::{Stream, future::BoxFuture};
use sneed::{DbError, Env, EnvError, RwTxnError, env};
use tokio::sync::Mutex;
Expand Down Expand Up @@ -497,6 +497,41 @@ where
Ok((returned_transactions, fee))
}

/// Get a transaction if it exists in the active chain or mempool.
/// Returns the transaction and the block it was included in, if it exists
/// in the active chain.
pub fn try_get_transaction(
&self,
txid: Txid,
) -> Result<Option<(Transaction, Option<BlockHash>)>, Error> {
let rotxn = self.env.read_txn()?;
let tip = self.state.try_get_tip(&rotxn)?;
if let Some(tip) = tip
&& let Some((block_hash, txin)) = self
.archive
.get_tx_inclusions(&rotxn, txid)?
.into_iter()
.map(Ok)
.transpose_into_fallible()
.find(|(block_hash, _idx)| {
self.archive.is_descendant(&rotxn, tip, *block_hash)
})?
{
let body = self.archive.get_body(&rotxn, block_hash)?;
let tx = body.transactions.into_iter().nth(txin as usize).unwrap();
Ok(Some((tx, Some(block_hash))))
} else if let Some(auth_tx) = self
.mempool
.transactions
.try_get(&rotxn, &txid)
.map_err(mempool::Error::from)?
{
Ok(Some((auth_tx.transaction, None)))
} else {
Ok(None)
}
}

pub fn try_get_pending_withdrawal_bundle(
&self,
) -> Result<Option<WithdrawalBundle>, Error> {
Expand Down
21 changes: 19 additions & 2 deletions rpc-api/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,27 @@ use std::net::SocketAddr;

use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use l2l_openapi::open_api;
use serde::{Deserialize, Serialize};
use thunder::{
net::Peer,
types::{
Address, MerkleRoot, OutPoint, Output, OutputContent, PointedOutput,
Txid, WithdrawalBundle, schema as thunder_schema,
Address, BlockHash, MerkleRoot, OutPoint, Output, OutputContent,
PointedOutput, Transaction, Txid, WithdrawalBundle,
schema as thunder_schema,
},
wallet::Balance,
};
use utoipa::ToSchema;

mod schema;

#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
pub struct GetTransactionResponse {
pub tx: Transaction,
/// Block hash, if in the active chain
pub block_hash: Option<BlockHash>,
}

#[open_api(ref_schemas[
Address, MerkleRoot, OutPoint, Output, OutputContent, Txid,
schema::BitcoinTxid, thunder_schema::BitcoinAddr,
Expand Down Expand Up @@ -109,6 +119,13 @@ pub trait Rpc {
#[method(name = "get_new_address")]
async fn get_new_address(&self) -> RpcResult<Address>;

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

/// Get wallet addresses, sorted by base58 encoding
#[method(name = "get_wallet_addresses")]
async fn get_wallet_addresses(&self) -> RpcResult<Vec<Address>>;
Expand Down
Loading