Skip to content

Commit

Permalink
rpc+wallet: add list transactions rpc
Browse files Browse the repository at this point in the history
  • Loading branch information
octobocto committed Nov 7, 2024
1 parent a4b7289 commit 2b11429
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 16 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ nonempty = "0.10.0"
parking_lot = { version = "0.12.3", features = ["send_guard"] }
prost = "0.13.2"
rand = "0.8.5"
prost-types = "0.13.3"
regex = "1.11.0"
rusqlite = { version = "0.28.0", features = ["bundled"] }
rusqlite_migration = "1.0.2"
Expand Down
76 changes: 62 additions & 14 deletions src/server.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{collections::HashMap, sync::Arc};
use std::{collections::HashMap, sync::Arc, vec};

use bdk_wallet::chain::{ChainPosition, ConfirmationBlockTime};
use bitcoin::{
absolute::Height,
hashes::{hmac, ripemd160, sha256, sha512, Hash, HashEngine},
Expand All @@ -11,6 +12,7 @@ use futures::{
StreamExt as _,
};
use miette::IntoDiagnostic as _;
use prost_types::Timestamp;
use thiserror::Error;
use tonic::{Request, Response, Status};

Expand All @@ -29,19 +31,21 @@ use crate::{
create_sidechain_proposal_response, get_bmm_h_star_commitment_response,
get_ctip_response::Ctip, get_sidechain_proposals_response::SidechainProposal,
get_sidechains_response::SidechainInfo, server::ValidatorService,
wallet_service_server::WalletService, BroadcastWithdrawalBundleRequest,
BroadcastWithdrawalBundleResponse, CreateBmmCriticalDataTransactionRequest,
CreateBmmCriticalDataTransactionResponse, CreateDepositTransactionRequest,
CreateDepositTransactionResponse, CreateNewAddressRequest, CreateNewAddressResponse,
CreateSidechainProposalRequest, CreateSidechainProposalResponse, GenerateBlocksRequest,
GenerateBlocksResponse, GetBalanceRequest, GetBalanceResponse,
GetBlockHeaderInfoRequest, GetBlockHeaderInfoResponse, GetBlockInfoRequest,
GetBlockInfoResponse, GetBmmHStarCommitmentRequest, GetBmmHStarCommitmentResponse,
GetChainInfoRequest, GetChainInfoResponse, GetChainTipRequest, GetChainTipResponse,
GetCoinbasePsbtRequest, GetCoinbasePsbtResponse, GetCtipRequest, GetCtipResponse,
GetSidechainProposalsRequest, GetSidechainProposalsResponse, GetSidechainsRequest,
GetSidechainsResponse, GetTwoWayPegDataRequest, GetTwoWayPegDataResponse, Network,
SubscribeEventsRequest, SubscribeEventsResponse,
wallet_service_server::WalletService, wallet_transaction::Confirmation,
BroadcastWithdrawalBundleRequest, BroadcastWithdrawalBundleResponse,
CreateBmmCriticalDataTransactionRequest, CreateBmmCriticalDataTransactionResponse,
CreateDepositTransactionRequest, CreateDepositTransactionResponse,
CreateNewAddressRequest, CreateNewAddressResponse, CreateSidechainProposalRequest,
CreateSidechainProposalResponse, GenerateBlocksRequest, GenerateBlocksResponse,
GetBalanceRequest, GetBalanceResponse, GetBlockHeaderInfoRequest,
GetBlockHeaderInfoResponse, GetBlockInfoRequest, GetBlockInfoResponse,
GetBmmHStarCommitmentRequest, GetBmmHStarCommitmentResponse, GetChainInfoRequest,
GetChainInfoResponse, GetChainTipRequest, GetChainTipResponse, GetCoinbasePsbtRequest,
GetCoinbasePsbtResponse, GetCtipRequest, GetCtipResponse, GetSidechainProposalsRequest,
GetSidechainProposalsResponse, GetSidechainsRequest, GetSidechainsResponse,
GetTwoWayPegDataRequest, GetTwoWayPegDataResponse, ListTransactionsRequest,
ListTransactionsResponse, Network, SubscribeEventsRequest, SubscribeEventsResponse,
WalletTransaction,
},
},
types::{Event, SidechainNumber},
Expand Down Expand Up @@ -925,6 +929,50 @@ impl WalletService for Arc<crate::wallet::Wallet> {

Ok(tonic::Response::new(response))
}

async fn list_transactions(
&self,
_: tonic::Request<ListTransactionsRequest>,
) -> Result<tonic::Response<ListTransactionsResponse>, tonic::Status> {
let transactions = self
.list_wallet_transactions()
.await
.map_err(|err| err.into_status())?;

let response = ListTransactionsResponse {
transactions: transactions
.into_iter()
.map(|tx| WalletTransaction {
txid: Some(ReverseHex::encode(&tx.txid)),
fee_sats: tx.fee.to_sat(),
received_sats: tx.received.to_sat(),
sent_sats: tx.sent.to_sat(),
confirmation_info: match tx.chain_position {
ChainPosition::Confirmed(ConfirmationBlockTime {
block_id,
confirmation_time,
}) => Some(Confirmation {
height: block_id.height,
block_hash: Some(ReverseHex::encode(&block_id.hash)),
timestamp: Some(Timestamp {
seconds: confirmation_time as i64,
nanos: 0,
}),
}),
ChainPosition::Unconfirmed(last_seen) => Some(Confirmation {
height: 0,
block_hash: None,
timestamp: Some(Timestamp {
seconds: last_seen as i64,
nanos: 0,
}),
}),
},
})
.collect(),
};
Ok(tonic::Response::new(response))
}
}

#[derive(Debug, Error)]
Expand Down
13 changes: 12 additions & 1 deletion src/types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::num::TryFromIntError;
use std::{num::TryFromIntError, sync::Arc};

use bdk_wallet::chain::{ChainPosition, ConfirmationBlockTime};
use bitcoin::{
hashes::{sha256d, Hash as _},
Amount, BlockHash, OutPoint, Txid, Work,
Expand Down Expand Up @@ -321,6 +322,16 @@ pub enum Event {
},
}

#[derive(Debug)]
pub struct BDKWalletTransaction {
pub txid: bitcoin::Txid,
pub chain_position: ChainPosition<ConfirmationBlockTime>,
pub transaction: Arc<bitcoin::Transaction>,
pub fee: Amount,
pub received: Amount,
pub sent: Amount,
}

#[cfg(test)]
mod tests {
use miette::Diagnostic as _;
Expand Down
73 changes: 72 additions & 1 deletion src/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ use crate::{
cli::WalletConfig,
convert,
messages::{self, CoinbaseBuilder, M8_BMM_REQUEST_TAG},
types::{Ctip, SidechainAck, SidechainNumber, SidechainProposal},
proto::mainchain::WalletTransaction,
types::{BDKWalletTransaction, Ctip, SidechainAck, SidechainNumber, SidechainProposal},
validator::Validator,
};

Expand Down Expand Up @@ -980,6 +981,76 @@ impl Wallet {
Ok(balance)
}

pub async fn list_wallet_transactions(&self) -> Result<Vec<BDKWalletTransaction>> {
let wallet = self.bitcoin_wallet.lock();
let transactions = wallet.transactions();
let mut txs = Vec::new();

for tx in transactions {
// Calculate total input value
let mut input_value = Amount::ZERO;
for input in tx.tx_node.tx.input.iter() {
let transaction_hex = self
.main_client
.get_raw_transaction(
input.previous_output.txid,
GetRawTransactionVerbose::<false>,
None,
)
.await
.map_err(|err| error::BitcoinCoreRPC {
method: "getrawtransaction".to_string(),
error: err,
})?;

let prev_output =
bitcoin::consensus::encode::deserialize_hex::<Transaction>(&transaction_hex)
.into_diagnostic()?;

input_value += prev_output.output[input.previous_output.vout as usize].value;
}

// Calculate total output value
let output_value = tx.tx_node.tx.output.iter().map(|o| o.value).sum();

// Fee is difference between inputs and outputs
let fee = input_value - output_value;

// Calculate received/sent by checking if outputs/inputs belong to wallet
let mut received = Amount::ZERO;
let mut sent = Amount::ZERO;

// Check outputs to wallet
for output in tx.tx_node.tx.output.iter() {
if wallet.is_mine(output.script_pubkey.clone()) {
received += output.value;
}
}

// Check inputs from wallet
for input in tx.tx_node.tx.input.iter() {
if let Some(prev_output) = wallet.get_tx(input.previous_output.txid) {
let prev_txout =
&prev_output.tx_node.tx.output[input.previous_output.vout as usize];
if wallet.is_mine(prev_txout.script_pubkey.clone()) {
sent += prev_txout.value;
}
}
}

txs.push(BDKWalletTransaction {
txid: tx.tx_node.txid,
chain_position: tx.chain_position.cloned(),
transaction: tx.tx_node.tx.clone(),
fee,
received,
sent,
});
}

Ok(txs)
}

pub fn sync(&self) -> Result<()> {
let start = SystemTime::now();
tracing::trace!("starting wallet sync");
Expand Down

0 comments on commit 2b11429

Please sign in to comment.