Skip to content

Commit

Permalink
Rosetta wallet tx search implementation (#67)
Browse files Browse the repository at this point in the history
* updating code

* fixing indexer logs
fixing address search issue
adding url support in cli

* fixing empty url issue in cli

* fixing clippy issue

* adding tx details functionality

* improving output

* fixing fmt issue

* fixing btc wallet issue

* improving output

* fixing fmt
  • Loading branch information
Haider-Ali-DS authored Jan 2, 2023
1 parent c0ae6a9 commit f5c6b75
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 27 deletions.
2 changes: 1 addition & 1 deletion rosetta-cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ pub enum OperatorEnum {
#[derive(Parser)]
pub struct SearchOpts {
#[clap(long)]
pub indexer_url: String,
pub indexer_url: Option<String>,
#[clap(flatten)]
pub network: NetworkIdentifierOpts,
#[clap(long, value_enum)]
Expand Down
37 changes: 22 additions & 15 deletions rosetta-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,29 +174,36 @@ async fn main() -> Result<()> {
let res = client.events_blocks(&req).await?;
println!("{:#?}", res);
}
Command::Search(opts) => {
let indexer_client = Client::new(&opts.indexer_url)?;
Command::Search(search_opts) => {
let url = if let Some(url) = search_opts.indexer_url {
url
} else if let Some(chain) = opts.chain {
chain.indexer_url().into()
} else {
anyhow::bail!("No indexer url provided");
};
let indexer_client = Client::new(&url)?;

let operator = match opts.operator {
let operator = match search_opts.operator {
Some(OperatorEnum::And) => Some(Operator::And),
Some(OperatorEnum::Or) => Some(Operator::Or),
None => None,
};

let req = SearchTransactionsRequest {
network_identifier: network_identifier(&client, &opts.network).await?,
max_block: opts.max_block,
offset: opts.offset,
limit: opts.limit,
transaction_identifier: opts.transaction.transaction_identifier(),
account_identifier: opts.account.account_identifier(),
r#type: opts.r#type,
success: opts.success,
network_identifier: network_identifier(&client, &search_opts.network).await?,
max_block: search_opts.max_block,
offset: search_opts.offset,
limit: search_opts.limit,
transaction_identifier: search_opts.transaction.transaction_identifier(),
account_identifier: search_opts.account.account_identifier(),
r#type: search_opts.r#type,
success: search_opts.success,
operator,
coin_identifier: opts.coin.coin_identifier(),
currency: opts.currency.currency_identifier(),
address: opts.address,
status: opts.status,
coin_identifier: search_opts.coin.coin_identifier(),
currency: search_opts.currency.currency_identifier(),
address: search_opts.address,
status: search_opts.status,
};
let res = indexer_client.search_transactions(&req).await?;
println!("{:#?}", res);
Expand Down
8 changes: 8 additions & 0 deletions rosetta-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,14 @@ impl Chain {
}
}

pub fn indexer_url(self) -> &'static str {
match self {
Chain::Btc => "http://rosetta.analog.one:8083",
Chain::Eth => "http://rosetta.analog.one:8084",
Chain::Dot => "http://rosetta.analog.one:8085",
}
}

pub fn config(self) -> BlockchainConfig {
match self {
Chain::Btc => BlockchainConfig::bitcoin_regtest(),
Expand Down
24 changes: 23 additions & 1 deletion rosetta-client/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::types::{
};
use crate::{BlockchainConfig, Client, TransactionBuilder};
use anyhow::Result;
use rosetta_types::AccountFaucetRequest;
use rosetta_types::{AccountFaucetRequest, SearchTransactionsRequest, SearchTransactionsResponse};
use serde_json::Value;

pub struct Wallet {
Expand Down Expand Up @@ -235,4 +235,26 @@ impl Wallet {
let resp = self.client.account_faucet(&req).await?;
Ok(resp.transaction_identifier)
}

pub async fn transactions(&self, indexer_url: &str) -> Result<SearchTransactionsResponse> {
let req = SearchTransactionsRequest {
network_identifier: self.config().network.clone(),
operator: None,
max_block: None,
offset: None,
limit: None,
transaction_identifier: None,
account_identifier: Some(self.account.clone()),
coin_identifier: None,
currency: None,
status: None,
r#type: None,
address: None,
success: None,
};

let client = Client::new(indexer_url)?;
let request = client.search_transactions(&req).await?;
Ok(request)
}
}
3 changes: 2 additions & 1 deletion rosetta-indexer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ edition = "2021"
[dependencies]
anyhow = "1.0.68"
clap = { version = "4.0.29", features = ["derive"] }
femme = "2.2.1"
rosetta-client = { path = "../rosetta-client" }
rosetta-types = { path = "../rosetta-types" }
surf = { version = "2.3.2", features = ["h1-client-rustls"], default-features = false }
serde_json = "1.0.70"
tide = "0.16.0"
tokio = { version = "1.23.0", features = ["full"] }
tracing-subscriber = "0.3.16"
2 changes: 1 addition & 1 deletion rosetta-indexer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use rosetta_indexer::server;

#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt::init();
femme::start();

let opts = IndexerArgs::parse();

Expand Down
135 changes: 128 additions & 7 deletions rosetta-indexer/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use rosetta_types::{
Currency, NetworkIdentifier, Operation, PartialBlockIdentifier, Transaction,
TransactionIdentifier,
};
use serde_json::json;
use surf::Body;
use tide::Response;

Expand Down Expand Up @@ -65,6 +66,7 @@ pub fn filter_tx(
if let Some(block) = block_response.block.clone() {
for tx in block.transactions {
let mut vec_of_operations = vec![];
let basic_tx_details = get_basic_details_from_event(&tx);

if !match_tx_id(&req.transaction_identifier, &tx.transaction_identifier.hash) {
continue;
Expand All @@ -75,7 +77,8 @@ pub fn filter_tx(
None => continue,
};

if !match_success(&last_event.r#type, req.success) {
let tx_failed = last_event.r#type.to_lowercase().contains("fail");
if !match_success(tx_failed, req.success) {
continue;
}

Expand All @@ -95,7 +98,11 @@ pub fn filter_tx(
}
}

if !match_address(&req.account_identifier, &op.account) {
if !match_acc_identifier(&req.account_identifier, &op.account) {
continue;
};

if !match_address(&req.address, &op.account) {
continue;
};

Expand All @@ -107,12 +114,21 @@ pub fn filter_tx(
}

if !vec_of_operations.is_empty() {
let transaction = Transaction {
let mut transaction = Transaction {
transaction_identifier: tx.transaction_identifier,
operations: vec_of_operations,
related_transactions: None,
metadata: None,
};

if !basic_tx_details.sender.is_empty()
&& !basic_tx_details.receiver.is_empty()
&& !basic_tx_details.amount.is_empty()
{
let metadata = json!({"from": basic_tx_details.sender, "to": basic_tx_details.receiver, "amount": basic_tx_details.amount,"Success": !tx_failed});
transaction.metadata = Some(metadata);
}

vec_of_extrinsics.push(transaction);
}
}
Expand Down Expand Up @@ -141,8 +157,7 @@ pub fn match_tx_id(tx_identifier: &Option<TransactionIdentifier>, received_tx: &
}
}

pub fn match_success(tx_success: &str, received_success: Option<bool>) -> bool {
let tx_success_status = tx_success.to_lowercase().contains("fail");
pub fn match_success(tx_success_status: bool, received_success: Option<bool>) -> bool {
if let Some(success) = received_success {
if success {
!tx_success_status
Expand All @@ -164,7 +179,7 @@ pub fn match_operation_type(op_type: &Option<String>, received_type: &str) -> bo
}
}

pub fn match_address(
pub fn match_acc_identifier(
received_acc_identifier: &Option<AccountIdentifier>,
op_acc_identifier: &Option<AccountIdentifier>,
) -> bool {
Expand All @@ -173,7 +188,11 @@ pub fn match_address(
if let Some(op_identifier) = op_acc_identifier.clone() {
let address_match =
if filter_address.eq(&op_identifier.address.trim_start_matches("0x").to_string()) {
true
if acc_identifier.sub_account.is_some() {
acc_identifier.sub_account == op_identifier.sub_account
} else {
true
}
} else {
match op_identifier.sub_account.as_ref() {
Some(sub_address) => filter_address
Expand All @@ -190,6 +209,34 @@ pub fn match_address(
}
}

pub fn match_address(
received_address: &Option<String>,
op_acc_identifier: &Option<AccountIdentifier>,
) -> bool {
if let Some(address) = received_address {
let address_without_prefix = address.trim_start_matches("0x");
if let Some(op_identifier) = op_acc_identifier.clone() {
let found_address = if address_without_prefix
.eq(&op_identifier.address.trim_start_matches("0x").to_string())
{
true
} else {
match op_identifier.sub_account.as_ref() {
Some(sub_address) => address_without_prefix
.eq(&sub_address.address.trim_start_matches("0x").to_string()),
None => false,
}
};

found_address
} else {
false
}
} else {
true
}
}

pub fn match_currency(received_curreny: &Option<Currency>, op_amount: &Option<Amount>) -> bool {
if let Some(currency) = received_curreny {
if let Some(amount) = op_amount {
Expand Down Expand Up @@ -261,3 +308,77 @@ fn find_utxo_transfer_operation(operations: Vec<Operation>) -> bool {

(input_amount > 0 && output_amount > 0) && (input_amount - output_amount).abs() < 1000
}

fn get_basic_details_from_event(tx: &Transaction) -> TransferStruct {
let mut sender: String = "".into();
let mut receiver: String = "".into();
let mut amount = "".into();

let transfer_operation = tx
.operations
.iter()
.find(|op| op.r#type.to_lowercase().contains("transfer"));
if let Some(op) = transfer_operation {
if let Some(value) = op.amount.clone() {
amount = value.value;
}
if let Some(tx_sender) = op.account.clone() {
sender = tx_sender.address;
receiver = tx_sender.sub_account.unwrap_or_default().address;
}
} else {
let transfer_operation: Vec<&Operation> = tx
.operations
.iter()
.filter(|op| op.r#type.to_lowercase().contains("call"))
.collect::<_>();

if transfer_operation.len() == 2 {
for tx in transfer_operation.iter() {
if let Some(value) = tx.amount.clone() {
if !value.value.contains('-') {
amount = value.value;
receiver = tx.account.clone().unwrap_or_default().address;
} else {
sender = tx.account.clone().unwrap_or_default().address;
}
}
}
} else {
let transfer_operation = tx
.operations
.iter()
.find(|op| op.r#type.to_lowercase().contains("input"));
if let Some(op) = transfer_operation {
if let Some(tx_sender) = op.account.clone() {
sender = tx_sender.address;
let transfer_operation = tx
.operations
.iter()
.filter(|op| op.r#type.to_lowercase().contains("output"))
.collect::<Vec<&Operation>>();
if transfer_operation.len() == 2 {
for tx in transfer_operation {
let temp_receiver = tx.account.clone().unwrap_or_default().address;
if temp_receiver != sender {
receiver = temp_receiver;
amount = tx.amount.clone().unwrap_or_default().value;
}
}
}
}
}
}
}
TransferStruct {
sender,
receiver,
amount,
}
}

pub struct TransferStruct {
pub sender: String,
pub receiver: String,
pub amount: String,
}
Loading

0 comments on commit f5c6b75

Please sign in to comment.