From fe544d564aa0cf0646cf6ff8a5f1514553490dfd Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli Date: Mon, 2 Dec 2024 12:48:13 +0100 Subject: [PATCH 1/6] parsing ack tx --- .../2024-07-04-103941_crawler_state/down.sql | 1 + .../2024-12-01-170248_ibc_ack/up.sql | 9 ++ .../down.sql | 3 + orm/src/schema.rs | 100 ++++++++---------- shared/src/block_result.rs | 73 +++++++++++-- shared/src/transaction.rs | 34 ++++++ transactions/Cargo.toml | 2 + transactions/src/services/tx.rs | 91 ++++++++++++++++ 8 files changed, 248 insertions(+), 65 deletions(-) create mode 100644 orm/migrations/2024-12-01-170248_ibc_ack/up.sql create mode 100644 transactions/src/services/tx.rs diff --git a/orm/migrations/2024-07-04-103941_crawler_state/down.sql b/orm/migrations/2024-07-04-103941_crawler_state/down.sql index 9122f91e4..23de38c6d 100644 --- a/orm/migrations/2024-07-04-103941_crawler_state/down.sql +++ b/orm/migrations/2024-07-04-103941_crawler_state/down.sql @@ -1,4 +1,5 @@ -- This file should undo anything in `up.sql` DROP TABLE crawler_state; + DROP TYPE CRAWLER_NAME; diff --git a/orm/migrations/2024-12-01-170248_ibc_ack/up.sql b/orm/migrations/2024-12-01-170248_ibc_ack/up.sql new file mode 100644 index 000000000..f8ba62400 --- /dev/null +++ b/orm/migrations/2024-12-01-170248_ibc_ack/up.sql @@ -0,0 +1,9 @@ +-- Your SQL goes here +CREATE TYPE IBC_STATUS AS ENUM ('fail', 'success', 'timeout', 'unknown'); + +CREATE TABLE ibc_ack ( + id VARCHAR PRIMARY KEY, + tx_hash VARCHAR NOT NULL, + timeout INT NOT NULL, + status IBC_STATUS +); diff --git a/orm/migrations/2024-12-10-104502_transaction_types/down.sql b/orm/migrations/2024-12-10-104502_transaction_types/down.sql index d9a93fe9a..566ff9fd1 100644 --- a/orm/migrations/2024-12-10-104502_transaction_types/down.sql +++ b/orm/migrations/2024-12-10-104502_transaction_types/down.sql @@ -1 +1,4 @@ -- This file should undo anything in `up.sql` +DROP TABLE ibc_ack; + +DROP TYPE IBC_STATUS; \ No newline at end of file diff --git a/orm/src/schema.rs b/orm/src/schema.rs index 796a8d6ae..320a7c8d4 100644 --- a/orm/src/schema.rs +++ b/orm/src/schema.rs @@ -1,91 +1,51 @@ // @generated automatically by Diesel CLI. pub mod sql_types { - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "crawler_name"))] pub struct CrawlerName; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "governance_kind"))] pub struct GovernanceKind; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "governance_result"))] pub struct GovernanceResult; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "governance_tally_type"))] pub struct GovernanceTallyType; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[diesel(postgres_type(name = "ibc_status"))] + pub struct IbcStatus; + + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "payment_kind"))] pub struct PaymentKind; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "payment_recurrence"))] pub struct PaymentRecurrence; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "token_type"))] pub struct TokenType; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "transaction_kind"))] pub struct TransactionKind; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "transaction_result"))] pub struct TransactionResult; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "validator_state"))] pub struct ValidatorState; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "vote_kind"))] pub struct VoteKind; } @@ -209,6 +169,18 @@ diesel::table! { } } +diesel::table! { + use diesel::sql_types::*; + use super::sql_types::IbcStatus; + + ibc_ack (id) { + id -> Varchar, + tx_hash -> Varchar, + timeout -> Int4, + status -> Nullable, + } +} + diesel::table! { ibc_token (address) { #[max_length = 45] @@ -243,6 +215,21 @@ diesel::table! { } } +diesel::table! { + use diesel::sql_types::*; + use super::sql_types::PaymentRecurrence; + use super::sql_types::PaymentKind; + + public_good_funding (id) { + id -> Int4, + proposal_id -> Int4, + payment_recurrence -> PaymentRecurrence, + payment_kind -> PaymentKind, + receipient -> Varchar, + amount -> Numeric, + } +} + diesel::table! { revealed_pk (id) { id -> Int4, @@ -315,6 +302,7 @@ diesel::joinable!(governance_votes -> governance_proposals (proposal_id)); diesel::joinable!(ibc_token -> token (address)); diesel::joinable!(inner_transactions -> wrapper_transactions (wrapper_id)); diesel::joinable!(pos_rewards -> validators (validator_id)); +diesel::joinable!(public_good_funding -> governance_proposals (proposal_id)); diesel::joinable!(unbonds -> validators (validator_id)); diesel::joinable!(wrapper_transactions -> blocks (block_height)); @@ -328,9 +316,11 @@ diesel::allow_tables_to_appear_in_same_query!( gas_price, governance_proposals, governance_votes, + ibc_ack, ibc_token, inner_transactions, pos_rewards, + public_good_funding, revealed_pk, token, unbonds, diff --git a/shared/src/block_result.rs b/shared/src/block_result.rs index d0e7f08d8..f24145693 100644 --- a/shared/src/block_result.rs +++ b/shared/src/block_result.rs @@ -1,6 +1,7 @@ use std::collections::BTreeMap; use std::str::FromStr; +use namada_ibc::apps::transfer::types::events::AckEvent; use namada_tx::data::TxResult; use tendermint_rpc::endpoint::block_results::Response as TendermintBlockResultResponse; @@ -10,6 +11,7 @@ use crate::transaction::TransactionExitStatus; #[derive(Debug, Clone)] pub enum EventKind { Applied, + SendPacket, Unknown, } @@ -17,6 +19,7 @@ impl From<&String> for EventKind { fn from(value: &String) -> Self { match value.as_str() { "tx/applied" => Self::Applied, + "send_packet" => Self::SendPacket, _ => Self::Unknown, } } @@ -32,7 +35,7 @@ pub struct BlockResult { #[derive(Debug, Clone)] pub struct Event { pub kind: EventKind, - pub attributes: Option, + pub attributes: Option, } #[derive(Debug, Clone, Default, Copy)] @@ -107,7 +110,7 @@ impl BatchResults { } #[derive(Debug, Clone, Default)] -pub struct TxAttributes { +pub struct TxApplied { pub code: TxEventStatusCode, pub gas: u64, pub hash: Id, @@ -116,14 +119,48 @@ pub struct TxAttributes { pub info: String, } -impl TxAttributes { +#[derive(Debug, Clone, Default)] +pub struct SendPacket { + pub source_port: String, + pub dest_port: String, + pub source_channel: String, + pub dest_channel: String, + pub sequence: String, +} + +#[derive(Debug, Clone)] +pub enum TxAttributesType { + TxApplied(TxApplied), + SendPacket(SendPacket) +} + +impl TxAttributesType { pub fn deserialize( event_kind: &EventKind, attributes: &BTreeMap, ) -> Option { match event_kind { EventKind::Unknown => None, - EventKind::Applied => Some(Self { + EventKind::SendPacket => { + let source_port = + attributes.get("packet_src_port").unwrap().to_owned(); + let dest_port = + attributes.get("packet_dst_port").unwrap().to_owned(); + let source_channel = + attributes.get("packet_src_channel").unwrap().to_owned(); + let dest_channel = + attributes.get("packet_dst_channel").unwrap().to_owned(); + let sequence = + attributes.get("packet_sequence").unwrap().to_owned(); + Some(Self::SendPacket(SendPacket { + source_port, + dest_port, + source_channel, + dest_channel, + sequence, + })) + } + EventKind::Applied => Some(Self::TxApplied(TxApplied { code: attributes .get("code") .map(|code| TxEventStatusCode::from(code.as_str())) @@ -153,7 +190,7 @@ impl TxAttributes { }) .unwrap(), info: attributes.get("info").unwrap().to_owned(), - }), + })), } } } @@ -177,7 +214,7 @@ impl From for BlockResult { }, ); let attributes = - TxAttributes::deserialize(&kind, &raw_attributes); + TxAttributesType::deserialize(&kind, &raw_attributes); Event { kind, attributes } }) .collect::>(); @@ -198,7 +235,7 @@ impl From for BlockResult { }, ); let attributes = - TxAttributes::deserialize(&kind, &raw_attributes); + TxAttributesType::deserialize(&kind, &raw_attributes); Event { kind, attributes } }) .collect::>(); @@ -221,7 +258,15 @@ impl BlockResult { let exit_status = self .end_events .iter() - .filter_map(|event| event.attributes.clone()) + .filter_map(|event| { + if let Some(TxAttributesType::TxApplied(data)) = + &event.attributes + { + Some(data.clone()) + } else { + None + } + }) .find(|attributes| attributes.hash.eq(tx_hash)) .map(|attributes| attributes.clone().code) .map(TransactionExitStatus::from); @@ -237,7 +282,15 @@ impl BlockResult { let exit_status = self .end_events .iter() - .filter_map(|event| event.attributes.clone()) + .filter_map(|event| { + if let Some(TxAttributesType::TxApplied(data)) = + &event.attributes + { + Some(data.clone()) + } else { + None + } + }) .find(|attributes| attributes.hash.eq(wrapper_hash)) .map(|attributes| attributes.batch.is_successful(inner_hash)) .map(|successful| match successful { @@ -246,4 +299,4 @@ impl BlockResult { }); exit_status.unwrap_or(TransactionExitStatus::Rejected) } -} +} \ No newline at end of file diff --git a/shared/src/transaction.rs b/shared/src/transaction.rs index d86ca4ba7..7dbbb0307 100644 --- a/shared/src/transaction.rs +++ b/shared/src/transaction.rs @@ -423,3 +423,37 @@ impl Transaction { self.extra_sections.get(§ion_id).cloned() } } + +#[derive(Debug, Clone)] +pub enum IbcAckStatus { + Success, + Fail, + Timeout, + Unknown, +} + +#[derive(Debug, Clone)] +pub struct IbcAck { + pub sequence_number: String, + pub source_port: String, + pub dest_port: String, + pub source_channel: String, + pub dest_channel: String, + pub status: IbcAckStatus, +} + +impl IbcAck { + pub fn id_source(&self) -> String { + format!( + "{}/{}/{}", + self.source_port, self.source_channel, self.sequence_number + ) + } + + pub fn id_dest(&self) -> String { + format!( + "{}/{}/{}", + self.dest_port, self.dest_channel, self.sequence_number + ) + } +} diff --git a/transactions/Cargo.toml b/transactions/Cargo.toml index 57ff1f28d..7967dc86b 100644 --- a/transactions/Cargo.toml +++ b/transactions/Cargo.toml @@ -27,6 +27,8 @@ deadpool-diesel.workspace = true diesel.workspace = true diesel_migrations.workspace = true orm.workspace = true +clap-verbosity-flag.workspace = true +serde_json.workspace = true [build-dependencies] vergen = { version = "8.0.0", features = ["build", "git", "gitcl"] } diff --git a/transactions/src/services/tx.rs b/transactions/src/services/tx.rs new file mode 100644 index 000000000..7b1928bc5 --- /dev/null +++ b/transactions/src/services/tx.rs @@ -0,0 +1,91 @@ +use namada_sdk::ibc::core::{ + channel::types::{acknowledgement::AcknowledgementStatus, msgs::PacketMsg}, + handler::types::msgs::MsgEnvelope, +}; +use shared::{ + block_result::{BlockResult, SendPacket, TxAttributesType}, + transaction::{IbcAck, IbcAckStatus, InnerTransaction, TransactionKind}, +}; + +pub fn get_ibc_packets(block_results: &BlockResult) -> Vec { + block_results + .end_events + .iter() + .filter_map(|event| { + if let Some(attributes) = &event.attributes { + match attributes { + TxAttributesType::SendPacket(packet) => { + Some(packet.to_owned()) + } + _ => None, + } + } else { + None + } + }) + .collect::>() +} + +pub fn get_ibc_recv_ack(inner_txs: &Vec) -> Vec { + inner_txs.iter().filter_map(|tx| match tx.kind.clone() { + TransactionKind::IbcMsgTransfer(ibc_message) => match ibc_message { + Some(ibc_message) => match ibc_message.0 { + namada_sdk::ibc::IbcMessage::Envelope(msg_envelope) => { + match *msg_envelope { + MsgEnvelope::Packet(packet_msg) => match packet_msg { + PacketMsg::Recv(_) => None, + PacketMsg::Ack(msg) => { + let ack = match serde_json::from_slice::< + AcknowledgementStatus, + >( + msg.acknowledgement.as_bytes() + ) { + Ok(status) => IbcAck { + sequence_number: msg.packet.seq_on_a.to_string(), + source_port: msg.packet.port_id_on_a.to_string(), + dest_port: msg.packet.port_id_on_b.to_string(), + source_channel: msg.packet.chan_id_on_a.to_string(), + dest_channel: msg.packet.chan_id_on_b.to_string(), + status: match status { + AcknowledgementStatus::Success(_) => IbcAckStatus::Success, + AcknowledgementStatus::Error(_) => IbcAckStatus::Fail, + }, + }, + Err(_) => IbcAck { + sequence_number: msg.packet.seq_on_a.to_string(), + source_port: msg.packet.port_id_on_a.to_string(), + dest_port: msg.packet.port_id_on_b.to_string(), + source_channel: msg.packet.chan_id_on_a.to_string(), + dest_channel: msg.packet.chan_id_on_b.to_string(), + status: IbcAckStatus::Unknown, + }, + }; + Some(ack) + } + PacketMsg::Timeout(msg) => Some(IbcAck { + sequence_number: msg.packet.seq_on_a.to_string(), + source_port: msg.packet.port_id_on_a.to_string(), + dest_port: msg.packet.port_id_on_b.to_string(), + source_channel: msg.packet.chan_id_on_a.to_string(), + dest_channel: msg.packet.chan_id_on_b.to_string(), + status: IbcAckStatus::Timeout, + }), + PacketMsg::TimeoutOnClose(msg) => Some(IbcAck { + sequence_number: msg.packet.seq_on_a.to_string(), + source_port: msg.packet.port_id_on_a.to_string(), + dest_port: msg.packet.port_id_on_b.to_string(), + source_channel: msg.packet.chan_id_on_a.to_string(), + dest_channel: msg.packet.chan_id_on_b.to_string(), + status: IbcAckStatus::Timeout, + }), + }, + _ => None, + } + } + _ => None, + }, + None => None, + }, + _ => None, + }).collect() +} From e7900849c220c82a7ce084bf473732b83b1e533c Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli Date: Mon, 2 Dec 2024 16:00:16 +0100 Subject: [PATCH 2/6] parse sequence + ack + tx id --- orm/src/ibc.rs | 55 +++++++ orm/src/lib.rs | 1 + orm/src/schema.rs | 72 +++++++-- shared/src/block_result.rs | 9 +- shared/src/transaction.rs | 35 +++++ transactions/src/main.rs | 28 +++- transactions/src/repository/transactions.rs | 49 +++++- transactions/src/services/mod.rs | 1 + transactions/src/services/tx.rs | 156 +++++++++++--------- 9 files changed, 318 insertions(+), 88 deletions(-) create mode 100644 orm/src/ibc.rs diff --git a/orm/src/ibc.rs b/orm/src/ibc.rs new file mode 100644 index 000000000..e939b9cbe --- /dev/null +++ b/orm/src/ibc.rs @@ -0,0 +1,55 @@ +use diesel::{AsChangeset, Insertable}; +use serde::{Deserialize, Serialize}; +use shared::transaction::{IbcAckStatus, IbcSequence}; + +use crate::schema::ibc_ack; + +#[derive(Debug, Clone, Serialize, Deserialize, diesel_derive_enum::DbEnum)] +#[ExistingTypePath = "crate::schema::sql_types::IbcStatus"] +pub enum IbcAckStatusDb { + Unknown, + Timeout, + Fail, + Success, +} + +impl From for IbcAckStatusDb { + fn from(value: IbcAckStatus) -> Self { + match value { + IbcAckStatus::Success => Self::Success, + IbcAckStatus::Fail => Self::Fail, + IbcAckStatus::Timeout => Self::Timeout, + IbcAckStatus::Unknown => Self::Unknown, + } + } +} + +#[derive(Serialize, Insertable, AsChangeset, Clone)] +#[diesel(table_name = ibc_ack)] +#[diesel(check_for_backend(diesel::pg::Pg))] +pub struct IbcAckDb { + pub id: String, + pub tx_hash: String, + pub timeout: i32, + pub status: IbcAckStatusDb, +} + +pub type IbcAckInsertDb = IbcAckDb; + +impl From for IbcAckInsertDb { + fn from(value: IbcSequence) -> Self { + Self { + id: value.id(), + tx_hash: value.tx_id.to_string(), + timeout: value.timeout as i32, + status: IbcAckStatusDb::Unknown, + } + } +} + +#[derive(Serialize, AsChangeset, Clone)] +#[diesel(table_name = ibc_ack)] +#[diesel(check_for_backend(diesel::pg::Pg))] +pub struct IbcSequencekStatusUpdateDb { + pub status: IbcAckStatusDb, +} diff --git a/orm/src/lib.rs b/orm/src/lib.rs index 340f5ae82..4cce8ef60 100644 --- a/orm/src/lib.rs +++ b/orm/src/lib.rs @@ -7,6 +7,7 @@ pub mod governance_proposal; pub mod governance_votes; pub mod group_by_macros; pub mod helpers; +pub mod ibc; pub mod migrations; pub mod parameters; pub mod pos_rewards; diff --git a/orm/src/schema.rs b/orm/src/schema.rs index 320a7c8d4..c373c02c9 100644 --- a/orm/src/schema.rs +++ b/orm/src/schema.rs @@ -1,51 +1,99 @@ // @generated automatically by Diesel CLI. pub mod sql_types { - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "crawler_name"))] pub struct CrawlerName; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "governance_kind"))] pub struct GovernanceKind; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "governance_result"))] pub struct GovernanceResult; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "governance_tally_type"))] pub struct GovernanceTallyType; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "ibc_status"))] pub struct IbcStatus; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "payment_kind"))] pub struct PaymentKind; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "payment_recurrence"))] pub struct PaymentRecurrence; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "token_type"))] pub struct TokenType; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "transaction_kind"))] pub struct TransactionKind; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "transaction_result"))] pub struct TransactionResult; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "validator_state"))] pub struct ValidatorState; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "vote_kind"))] pub struct VoteKind; } diff --git a/shared/src/block_result.rs b/shared/src/block_result.rs index f24145693..b6899b663 100644 --- a/shared/src/block_result.rs +++ b/shared/src/block_result.rs @@ -1,7 +1,6 @@ use std::collections::BTreeMap; use std::str::FromStr; -use namada_ibc::apps::transfer::types::events::AckEvent; use namada_tx::data::TxResult; use tendermint_rpc::endpoint::block_results::Response as TendermintBlockResultResponse; @@ -125,6 +124,7 @@ pub struct SendPacket { pub dest_port: String, pub source_channel: String, pub dest_channel: String, + pub timeout_timestamp: u64, pub sequence: String, } @@ -152,11 +152,18 @@ impl TxAttributesType { attributes.get("packet_dst_channel").unwrap().to_owned(); let sequence = attributes.get("packet_sequence").unwrap().to_owned(); + let timeout_timestamp = attributes + .get("packet_timeout_timestamp") + .unwrap_or(&"0".to_string()) + .parse::() + .unwrap_or_default() + .to_owned(); Some(Self::SendPacket(SendPacket { source_port, dest_port, source_channel, dest_channel, + timeout_timestamp, sequence, })) } diff --git a/shared/src/transaction.rs b/shared/src/transaction.rs index 7dbbb0307..8dfa64909 100644 --- a/shared/src/transaction.rs +++ b/shared/src/transaction.rs @@ -424,6 +424,30 @@ impl Transaction { } } +#[derive(Debug, Clone)] +pub struct IbcSequence { + pub sequence_number: String, + pub source_port: String, + pub dest_port: String, + pub source_channel: String, + pub dest_channel: String, + pub timeout: u64, + pub tx_id: Id, +} + +impl IbcSequence { + pub fn id(&self) -> String { + format!( + "{}/{}/{}/{}/{}", + self.dest_port, + self.dest_channel, + self.source_port, + self.source_channel, + self.sequence_number + ) + } +} + #[derive(Debug, Clone)] pub enum IbcAckStatus { Success, @@ -456,4 +480,15 @@ impl IbcAck { self.dest_port, self.dest_channel, self.sequence_number ) } + + pub fn id(&self) -> String { + format!( + "{}/{}/{}/{}/{}", + self.dest_port, + self.dest_channel, + self.source_port, + self.source_channel, + self.sequence_number + ) + } } diff --git a/transactions/src/main.rs b/transactions/src/main.rs index 36c9bc56d..32243bea1 100644 --- a/transactions/src/main.rs +++ b/transactions/src/main.rs @@ -21,7 +21,7 @@ use transactions::repository::{ }; use transactions::services::{ db as db_service, namada as namada_service, - tendermint as tendermint_service, + tendermint as tendermint_service, tx as tx_service, }; #[tokio::main] @@ -143,11 +143,17 @@ async fn crawling_fn( let inner_txs = block.inner_txs(); let wrapper_txs = block.wrapper_txs(); - tracing::debug!( - block = block_height, - txs = inner_txs.len(), - "Deserialized {} txs...", - wrapper_txs.len() + inner_txs.len() + let ibc_sequence_packet = + tx_service::get_ibc_packets(&block_results, &inner_txs); + let ibc_ack_packet = tx_service::get_ibc_ack_packet(&inner_txs); + + tracing::info!( + "Deserialized {} wrappers, {} inners, {} ibc sequence numbers and {} ibc acks \ + events...", + wrapper_txs.len(), + inner_txs.len(), + ibc_sequence_packet.len(), + ibc_ack_packet.len() ); // Because transaction crawler starts from block 1 we read timestamp from @@ -187,6 +193,16 @@ async fn crawling_fn( crawler_state, )?; + transaction_repo::insert_ibc_sequence( + transaction_conn, + ibc_sequence_packet, + )?; + + transaction_repo::update_ibc_sequence( + transaction_conn, + ibc_ack_packet, + )?; + anyhow::Ok(()) }) }) diff --git a/transactions/src/repository/transactions.rs b/transactions/src/repository/transactions.rs index 8fb982179..4b5d403b3 100644 --- a/transactions/src/repository/transactions.rs +++ b/transactions/src/repository/transactions.rs @@ -1,12 +1,20 @@ use anyhow::Context; use chrono::NaiveDateTime; use diesel::upsert::excluded; -use diesel::{ExpressionMethods, PgConnection, RunQueryDsl}; +use diesel::{ + ExpressionMethods, OptionalEmptyChangesetExtension, PgConnection, + RunQueryDsl, +}; use orm::crawler_state::{BlockStateInsertDb, CrawlerNameDb}; -use orm::schema::{crawler_state, inner_transactions, wrapper_transactions}; +use orm::ibc::{IbcAckInsertDb, IbcAckStatusDb, IbcSequencekStatusUpdateDb}; +use orm::schema::{ + crawler_state, ibc_ack, inner_transactions, wrapper_transactions, +}; use orm::transactions::{InnerTransactionInsertDb, WrapperTransactionInsertDb}; use shared::crawler_state::{BlockCrawlerState, CrawlerName}; -use shared::transaction::{InnerTransaction, WrapperTransaction}; +use shared::transaction::{ + IbcAck, IbcSequence, InnerTransaction, WrapperTransaction, +}; pub fn insert_inner_transactions( transaction_conn: &mut PgConnection, @@ -76,3 +84,38 @@ pub fn update_crawler_timestamp( anyhow::Ok(()) } + +pub fn insert_ibc_sequence( + transaction_conn: &mut PgConnection, + ibc_sequences: Vec, +) -> anyhow::Result<()> { + diesel::insert_into(ibc_ack::table) + .values::>( + ibc_sequences + .into_iter() + .map(IbcAckInsertDb::from) + .collect(), + ) + .execute(transaction_conn) + .context("Failed to update crawler state in db")?; + + anyhow::Ok(()) +} + +pub fn update_ibc_sequence( + transaction_conn: &mut PgConnection, + ibc_acks: Vec, +) -> anyhow::Result<()> { + for ack in ibc_acks { + let ack_update = IbcSequencekStatusUpdateDb { + status: IbcAckStatusDb::from(ack.status.clone()), + }; + diesel::update(ibc_ack::table) + .set(ack_update) + .filter(ibc_ack::dsl::id.eq(ack.id())) + .execute(transaction_conn) + .optional_empty_changeset() + .context("Failed to update validator metadata in db")?; + } + anyhow::Ok(()) +} diff --git a/transactions/src/services/mod.rs b/transactions/src/services/mod.rs index a9dfa39f9..5c0b9ddc2 100644 --- a/transactions/src/services/mod.rs +++ b/transactions/src/services/mod.rs @@ -1,3 +1,4 @@ pub mod db; pub mod namada; pub mod tendermint; +pub mod tx; \ No newline at end of file diff --git a/transactions/src/services/tx.rs b/transactions/src/services/tx.rs index 7b1928bc5..833421442 100644 --- a/transactions/src/services/tx.rs +++ b/transactions/src/services/tx.rs @@ -1,22 +1,49 @@ -use namada_sdk::ibc::core::{ - channel::types::{acknowledgement::AcknowledgementStatus, msgs::PacketMsg}, - handler::types::msgs::MsgEnvelope, -}; -use shared::{ - block_result::{BlockResult, SendPacket, TxAttributesType}, - transaction::{IbcAck, IbcAckStatus, InnerTransaction, TransactionKind}, +use namada_sdk::ibc::core::channel::types::acknowledgement::AcknowledgementStatus; +use namada_sdk::ibc::core::channel::types::msgs::PacketMsg; +use namada_sdk::ibc::core::handler::types::msgs::MsgEnvelope; +use shared::block_result::{BlockResult, TxAttributesType}; +use shared::ser::IbcMessage; +use shared::transaction::{ + IbcAck, IbcAckStatus, IbcSequence, InnerTransaction, TransactionExitStatus, + TransactionKind, }; -pub fn get_ibc_packets(block_results: &BlockResult) -> Vec { +pub fn get_ibc_packets( + block_results: &BlockResult, + inner_txs: &[InnerTransaction], +) -> Vec { + let mut ibc_txs = inner_txs + .iter() + .filter_map(|tx| { + if matches!( + tx.kind, + TransactionKind::IbcMsgTransfer(Some(IbcMessage(_))) + ) && matches!(tx.exit_code, TransactionExitStatus::Applied) + { + Some(tx.tx_id.clone()) + } else { + None + } + }) + .collect::>(); + + ibc_txs.reverse(); + block_results .end_events .iter() .filter_map(|event| { if let Some(attributes) = &event.attributes { match attributes { - TxAttributesType::SendPacket(packet) => { - Some(packet.to_owned()) - } + TxAttributesType::SendPacket(packet) => Some(IbcSequence { + sequence_number: packet.sequence.clone(), + source_port: packet.source_port.clone(), + dest_port: packet.dest_port.clone(), + source_channel: packet.source_channel.clone(), + dest_channel: packet.dest_channel.clone(), + timeout: packet.timeout_timestamp, + tx_id: ibc_txs.pop().unwrap(), + }), _ => None, } } else { @@ -26,65 +53,62 @@ pub fn get_ibc_packets(block_results: &BlockResult) -> Vec { .collect::>() } -pub fn get_ibc_recv_ack(inner_txs: &Vec) -> Vec { +pub fn get_ibc_ack_packet(inner_txs: &[InnerTransaction]) -> Vec { inner_txs.iter().filter_map(|tx| match tx.kind.clone() { - TransactionKind::IbcMsgTransfer(ibc_message) => match ibc_message { - Some(ibc_message) => match ibc_message.0 { - namada_sdk::ibc::IbcMessage::Envelope(msg_envelope) => { - match *msg_envelope { - MsgEnvelope::Packet(packet_msg) => match packet_msg { - PacketMsg::Recv(_) => None, - PacketMsg::Ack(msg) => { - let ack = match serde_json::from_slice::< - AcknowledgementStatus, - >( - msg.acknowledgement.as_bytes() - ) { - Ok(status) => IbcAck { - sequence_number: msg.packet.seq_on_a.to_string(), - source_port: msg.packet.port_id_on_a.to_string(), - dest_port: msg.packet.port_id_on_b.to_string(), - source_channel: msg.packet.chan_id_on_a.to_string(), - dest_channel: msg.packet.chan_id_on_b.to_string(), - status: match status { - AcknowledgementStatus::Success(_) => IbcAckStatus::Success, - AcknowledgementStatus::Error(_) => IbcAckStatus::Fail, - }, + TransactionKind::IbcMsgTransfer(Some(ibc_message)) => match ibc_message.0 { + namada_sdk::ibc::IbcMessage::Envelope(msg_envelope) => { + match *msg_envelope { + MsgEnvelope::Packet(packet_msg) => match packet_msg { + PacketMsg::Recv(_) => None, + PacketMsg::Ack(msg) => { + let ack = match serde_json::from_slice::< + AcknowledgementStatus, + >( + msg.acknowledgement.as_bytes() + ) { + Ok(status) => IbcAck { + sequence_number: msg.packet.seq_on_a.to_string(), + source_port: msg.packet.port_id_on_a.to_string(), + dest_port: msg.packet.port_id_on_b.to_string(), + source_channel: msg.packet.chan_id_on_a.to_string(), + dest_channel: msg.packet.chan_id_on_b.to_string(), + status: match status { + AcknowledgementStatus::Success(_) => IbcAckStatus::Success, + AcknowledgementStatus::Error(_) => IbcAckStatus::Fail, }, - Err(_) => IbcAck { - sequence_number: msg.packet.seq_on_a.to_string(), - source_port: msg.packet.port_id_on_a.to_string(), - dest_port: msg.packet.port_id_on_b.to_string(), - source_channel: msg.packet.chan_id_on_a.to_string(), - dest_channel: msg.packet.chan_id_on_b.to_string(), - status: IbcAckStatus::Unknown, - }, - }; - Some(ack) - } - PacketMsg::Timeout(msg) => Some(IbcAck { - sequence_number: msg.packet.seq_on_a.to_string(), - source_port: msg.packet.port_id_on_a.to_string(), - dest_port: msg.packet.port_id_on_b.to_string(), - source_channel: msg.packet.chan_id_on_a.to_string(), - dest_channel: msg.packet.chan_id_on_b.to_string(), - status: IbcAckStatus::Timeout, - }), - PacketMsg::TimeoutOnClose(msg) => Some(IbcAck { - sequence_number: msg.packet.seq_on_a.to_string(), - source_port: msg.packet.port_id_on_a.to_string(), - dest_port: msg.packet.port_id_on_b.to_string(), - source_channel: msg.packet.chan_id_on_a.to_string(), - dest_channel: msg.packet.chan_id_on_b.to_string(), - status: IbcAckStatus::Timeout, - }), - }, - _ => None, - } + }, + Err(_) => IbcAck { + sequence_number: msg.packet.seq_on_a.to_string(), + source_port: msg.packet.port_id_on_a.to_string(), + dest_port: msg.packet.port_id_on_b.to_string(), + source_channel: msg.packet.chan_id_on_a.to_string(), + dest_channel: msg.packet.chan_id_on_b.to_string(), + status: IbcAckStatus::Unknown, + }, + }; + Some(ack) + } + PacketMsg::Timeout(msg) => Some(IbcAck { + sequence_number: msg.packet.seq_on_a.to_string(), + source_port: msg.packet.port_id_on_a.to_string(), + dest_port: msg.packet.port_id_on_b.to_string(), + source_channel: msg.packet.chan_id_on_a.to_string(), + dest_channel: msg.packet.chan_id_on_b.to_string(), + status: IbcAckStatus::Timeout, + }), + PacketMsg::TimeoutOnClose(msg) => Some(IbcAck { + sequence_number: msg.packet.seq_on_a.to_string(), + source_port: msg.packet.port_id_on_a.to_string(), + dest_port: msg.packet.port_id_on_b.to_string(), + source_channel: msg.packet.chan_id_on_a.to_string(), + dest_channel: msg.packet.chan_id_on_b.to_string(), + status: IbcAckStatus::Timeout, + }), + }, + _ => None, } - _ => None, }, - None => None, + _ => None }, _ => None, }).collect() From 4d5886fd0aa728295d2d7f29e6ff88d62579d914 Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli Date: Tue, 3 Dec 2024 09:20:19 +0100 Subject: [PATCH 3/6] added handler --- .../2024-12-01-170248_ibc_ack/up.sql | 4 +- orm/src/ibc.rs | 9 ++- orm/src/schema.rs | 76 ++++--------------- shared/src/block.rs | 3 - shared/src/block_result.rs | 3 + shared/src/ser.rs | 17 ++++- transactions/src/main.rs | 1 - webserver/src/app.rs | 5 +- webserver/src/error/api.rs | 4 + webserver/src/error/ibc.rs | 28 +++++++ webserver/src/error/mod.rs | 1 + webserver/src/handler/ibc.rs | 19 +++++ webserver/src/handler/mod.rs | 1 + webserver/src/repository/ibc.rs | 45 +++++++++++ webserver/src/repository/mod.rs | 1 + webserver/src/response/ibc.rs | 16 ++++ webserver/src/response/mod.rs | 1 + webserver/src/service/ibc.rs | 42 ++++++++++ webserver/src/service/mod.rs | 1 + webserver/src/state/common.rs | 3 + 20 files changed, 203 insertions(+), 77 deletions(-) create mode 100644 webserver/src/error/ibc.rs create mode 100644 webserver/src/handler/ibc.rs create mode 100644 webserver/src/repository/ibc.rs create mode 100644 webserver/src/response/ibc.rs create mode 100644 webserver/src/service/ibc.rs diff --git a/orm/migrations/2024-12-01-170248_ibc_ack/up.sql b/orm/migrations/2024-12-01-170248_ibc_ack/up.sql index f8ba62400..652a3e23e 100644 --- a/orm/migrations/2024-12-01-170248_ibc_ack/up.sql +++ b/orm/migrations/2024-12-01-170248_ibc_ack/up.sql @@ -4,6 +4,6 @@ CREATE TYPE IBC_STATUS AS ENUM ('fail', 'success', 'timeout', 'unknown'); CREATE TABLE ibc_ack ( id VARCHAR PRIMARY KEY, tx_hash VARCHAR NOT NULL, - timeout INT NOT NULL, - status IBC_STATUS + timeout BIGINT NOT NULL, + status IBC_STATUS NOT NULL ); diff --git a/orm/src/ibc.rs b/orm/src/ibc.rs index e939b9cbe..0583f47e5 100644 --- a/orm/src/ibc.rs +++ b/orm/src/ibc.rs @@ -1,4 +1,5 @@ -use diesel::{AsChangeset, Insertable}; +use diesel::prelude::Queryable; +use diesel::{AsChangeset, Insertable, Selectable}; use serde::{Deserialize, Serialize}; use shared::transaction::{IbcAckStatus, IbcSequence}; @@ -24,13 +25,13 @@ impl From for IbcAckStatusDb { } } -#[derive(Serialize, Insertable, AsChangeset, Clone)] +#[derive(Serialize, Queryable, Insertable, Selectable, Clone, Debug)] #[diesel(table_name = ibc_ack)] #[diesel(check_for_backend(diesel::pg::Pg))] pub struct IbcAckDb { pub id: String, pub tx_hash: String, - pub timeout: i32, + pub timeout: i64, pub status: IbcAckStatusDb, } @@ -41,7 +42,7 @@ impl From for IbcAckInsertDb { Self { id: value.id(), tx_hash: value.tx_id.to_string(), - timeout: value.timeout as i32, + timeout: value.timeout as i64, status: IbcAckStatusDb::Unknown, } } diff --git a/orm/src/schema.rs b/orm/src/schema.rs index c373c02c9..9ff237292 100644 --- a/orm/src/schema.rs +++ b/orm/src/schema.rs @@ -1,99 +1,51 @@ // @generated automatically by Diesel CLI. pub mod sql_types { - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "crawler_name"))] pub struct CrawlerName; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "governance_kind"))] pub struct GovernanceKind; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "governance_result"))] pub struct GovernanceResult; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "governance_tally_type"))] pub struct GovernanceTallyType; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "ibc_status"))] pub struct IbcStatus; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "payment_kind"))] pub struct PaymentKind; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "payment_recurrence"))] pub struct PaymentRecurrence; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "token_type"))] pub struct TokenType; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "transaction_kind"))] pub struct TransactionKind; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "transaction_result"))] pub struct TransactionResult; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "validator_state"))] pub struct ValidatorState; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "vote_kind"))] pub struct VoteKind; } @@ -224,8 +176,8 @@ diesel::table! { ibc_ack (id) { id -> Varchar, tx_hash -> Varchar, - timeout -> Int4, - status -> Nullable, + timeout -> Int8, + status -> IbcStatus, } } diff --git a/shared/src/block.rs b/shared/src/block.rs index 465ad40fe..ccfb0c563 100644 --- a/shared/src/block.rs +++ b/shared/src/block.rs @@ -104,7 +104,6 @@ pub struct Block { pub hash: Id, pub header: BlockHeader, pub transactions: Vec<(WrapperTransaction, Vec)>, - pub epoch: Epoch, } impl Block { @@ -113,7 +112,6 @@ impl Block { block_results: &BlockResult, proposer_address_namada: &Option, // Provide the namada address of the proposer, if available checksums: Checksums, - epoch: Epoch, block_height: BlockHeight, ) -> Self { let transactions = block_response @@ -154,7 +152,6 @@ impl Block { app_hash: Id::from(&block_response.block.header.app_hash), }, transactions, - epoch, } } diff --git a/shared/src/block_result.rs b/shared/src/block_result.rs index b6899b663..ac5f91a69 100644 --- a/shared/src/block_result.rs +++ b/shared/src/block_result.rs @@ -158,6 +158,9 @@ impl TxAttributesType { .parse::() .unwrap_or_default() .to_owned(); + + tracing::error!("{}", timeout_timestamp); + Some(Self::SendPacket(SendPacket { source_port, dest_port, diff --git a/shared/src/ser.rs b/shared/src/ser.rs index a776b783f..a9e9ee609 100644 --- a/shared/src/ser.rs +++ b/shared/src/ser.rs @@ -3,6 +3,7 @@ use std::str::FromStr; use namada_core::address::Address; use namada_core::masp::MaspTxId; +use namada_sdk::borsh::BorshSerializeExt; use namada_sdk::ibc::IbcMessage as NamadaIbcMessage; use namada_sdk::token::{ Account as NamadaAccount, DenominatedAmount as NamadaDenominatedAmount, @@ -10,6 +11,7 @@ use namada_sdk::token::{ }; use serde::ser::SerializeStruct; use serde::{Deserialize, Serialize}; +use subtle_encoding::hex; #[derive(Debug, Clone)] pub struct AccountsMap(pub BTreeMap); @@ -120,10 +122,19 @@ impl Serialize for IbcMessage { state.end() } - NamadaIbcMessage::Envelope(_) => { - let state = serializer.serialize_struct("IbcEnvelope", 0)?; + NamadaIbcMessage::Envelope(data) => { + let mut state = + serializer.serialize_struct("IbcEnvelope", 1)?; + + // todo: implement this bs :( - // TODO: serialize envelope message correctly + state.serialize_field( + "data", + &String::from_utf8_lossy(&hex::encode( + data.serialize_to_vec(), + )) + .into_owned(), + )?; state.end() } diff --git a/transactions/src/main.rs b/transactions/src/main.rs index 32243bea1..5249deb98 100644 --- a/transactions/src/main.rs +++ b/transactions/src/main.rs @@ -136,7 +136,6 @@ async fn crawling_fn( &block_results, &proposer_address_namada, checksums, - 1_u32, block_height, ); diff --git a/webserver/src/app.rs b/webserver/src/app.rs index 734ba4c17..935eb0d57 100644 --- a/webserver/src/app.rs +++ b/webserver/src/app.rs @@ -21,8 +21,8 @@ use crate::config::AppConfig; use crate::handler::{ balance as balance_handlers, chain as chain_handlers, crawler_state as crawler_state_handlers, gas as gas_handlers, - governance as gov_handlers, pk as pk_handlers, pos as pos_handlers, - transaction as transaction_handlers, + governance as gov_handlers, ibc as ibc_handler, pk as pk_handlers, + pos as pos_handlers, transaction as transaction_handlers }; use crate::state::common::CommonState; @@ -129,6 +129,7 @@ impl ApplicationServer { "/chain/epoch/latest", get(chain_handlers::get_last_processed_epoch), ) + .route("/ibc/:tx_id/status", get(ibc_handler::get_ibc_status)) .route( "/crawlers/timestamps", get(crawler_state_handlers::get_crawlers_timestamps), diff --git a/webserver/src/error/api.rs b/webserver/src/error/api.rs index 1db8ed5f3..1d0045a7c 100644 --- a/webserver/src/error/api.rs +++ b/webserver/src/error/api.rs @@ -6,6 +6,7 @@ use super::chain::ChainError; use super::crawler_state::CrawlerStateError; use super::gas::GasError; use super::governance::GovernanceError; +use super::ibc::IbcError; use super::pos::PoSError; use super::revealed_pk::RevealedPkError; use super::transaction::TransactionError; @@ -27,6 +28,8 @@ pub enum ApiError { #[error(transparent)] GasError(#[from] GasError), #[error(transparent)] + IbcError(#[from] IbcError), + #[error(transparent)] CrawlerStateError(#[from] CrawlerStateError), } @@ -40,6 +43,7 @@ impl IntoResponse for ApiError { ApiError::GovernanceError(error) => error.into_response(), ApiError::RevealedPkError(error) => error.into_response(), ApiError::GasError(error) => error.into_response(), + ApiError::IbcError(error) => error.into_response(), ApiError::CrawlerStateError(error) => error.into_response(), } } diff --git a/webserver/src/error/ibc.rs b/webserver/src/error/ibc.rs new file mode 100644 index 000000000..869d738ce --- /dev/null +++ b/webserver/src/error/ibc.rs @@ -0,0 +1,28 @@ +use axum::http::StatusCode; +use axum::response::IntoResponse; +use thiserror::Error; + +use crate::response::api::ApiErrorResponse; + +#[derive(Error, Debug)] +pub enum IbcError { + #[error("Revealed public key {0} not found")] + NotFound(u64), + #[error("Database error: {0}")] + Database(String), + #[error("Unknown error: {0}")] + Unknown(String), +} + +impl IntoResponse for IbcError { + fn into_response(self) -> axum::response::Response { + let status_code = match self { + IbcError::NotFound(_) => StatusCode::NOT_FOUND, + IbcError::Unknown(_) | IbcError::Database(_) => { + StatusCode::INTERNAL_SERVER_ERROR + } + }; + + ApiErrorResponse::send(status_code.as_u16(), Some(self.to_string())) + } +} diff --git a/webserver/src/error/mod.rs b/webserver/src/error/mod.rs index 94f2c2724..67b033aa3 100644 --- a/webserver/src/error/mod.rs +++ b/webserver/src/error/mod.rs @@ -4,6 +4,7 @@ pub mod chain; pub mod crawler_state; pub mod gas; pub mod governance; +pub mod ibc; pub mod pos; pub mod revealed_pk; pub mod transaction; diff --git a/webserver/src/handler/ibc.rs b/webserver/src/handler/ibc.rs new file mode 100644 index 000000000..ecf8ff83d --- /dev/null +++ b/webserver/src/handler/ibc.rs @@ -0,0 +1,19 @@ +use axum::extract::{Path, State}; +use axum::http::HeaderMap; +use axum::Json; +use axum_macros::debug_handler; + +use crate::error::api::ApiError; +use crate::response::ibc::IbcAck; +use crate::state::common::CommonState; + +#[debug_handler] +pub async fn get_ibc_status( + _headers: HeaderMap, + Path(tx_id): Path, + State(state): State, +) -> Result, ApiError> { + let ibc_ack_status = state.ibc_service.get_ack_by_tx_id(tx_id).await?; + + Ok(Json(ibc_ack_status)) +} diff --git a/webserver/src/handler/mod.rs b/webserver/src/handler/mod.rs index be4fc91e8..87f96ddbc 100644 --- a/webserver/src/handler/mod.rs +++ b/webserver/src/handler/mod.rs @@ -3,6 +3,7 @@ pub mod chain; pub mod crawler_state; pub mod gas; pub mod governance; +pub mod ibc; pub mod pk; pub mod pos; pub mod transaction; diff --git a/webserver/src/repository/ibc.rs b/webserver/src/repository/ibc.rs new file mode 100644 index 000000000..e55ef4a60 --- /dev/null +++ b/webserver/src/repository/ibc.rs @@ -0,0 +1,45 @@ +use axum::async_trait; +use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl, SelectableHelper}; +use orm::ibc::IbcAckDb; +use orm::schema::ibc_ack; + +use crate::appstate::AppState; + +#[derive(Clone)] +pub struct IbcRepository { + pub(crate) app_state: AppState, +} + +#[async_trait] +pub trait IbcRepositoryTrait { + fn new(app_state: AppState) -> Self; + + async fn find_ibc_ack( + &self, + id: String, + ) -> Result, String>; +} + +#[async_trait] +impl IbcRepositoryTrait for IbcRepository { + fn new(app_state: AppState) -> Self { + Self { app_state } + } + + async fn find_ibc_ack( + &self, + id: String, + ) -> Result, String> { + let conn = self.app_state.get_db_connection().await; + + conn.interact(move |conn| { + ibc_ack::table + .filter(ibc_ack::dsl::tx_hash.eq(id)) + .select(IbcAckDb::as_select()) + .first(conn) + .ok() + }) + .await + .map_err(|e| e.to_string()) + } +} diff --git a/webserver/src/repository/mod.rs b/webserver/src/repository/mod.rs index 0ffcdf615..0db518011 100644 --- a/webserver/src/repository/mod.rs +++ b/webserver/src/repository/mod.rs @@ -2,6 +2,7 @@ pub mod balance; pub mod chain; pub mod gas; pub mod governance; +pub mod ibc; pub mod pos; pub mod revealed_pk; pub mod tranasaction; diff --git a/webserver/src/response/ibc.rs b/webserver/src/response/ibc.rs new file mode 100644 index 000000000..2277c4c42 --- /dev/null +++ b/webserver/src/response/ibc.rs @@ -0,0 +1,16 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum IbcAckStatus { + Success, + Fail, + Timeout, + Unknown, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct IbcAck { + pub status: IbcAckStatus, +} diff --git a/webserver/src/response/mod.rs b/webserver/src/response/mod.rs index d5201da87..676be021e 100644 --- a/webserver/src/response/mod.rs +++ b/webserver/src/response/mod.rs @@ -4,6 +4,7 @@ pub mod chain; pub mod crawler_state; pub mod gas; pub mod governance; +pub mod ibc; pub mod pos; pub mod revealed_pk; pub mod transaction; diff --git a/webserver/src/service/ibc.rs b/webserver/src/service/ibc.rs new file mode 100644 index 000000000..a322b3601 --- /dev/null +++ b/webserver/src/service/ibc.rs @@ -0,0 +1,42 @@ +use orm::ibc::IbcAckStatusDb; + +use crate::appstate::AppState; +use crate::error::ibc::IbcError; +use crate::repository::ibc::{IbcRepository, IbcRepositoryTrait}; +use crate::response::ibc::{IbcAck, IbcAckStatus}; + +#[derive(Clone)] +pub struct IbcService { + pub ibc_repo: IbcRepository, +} + +impl IbcService { + pub fn new(app_state: AppState) -> Self { + Self { + ibc_repo: IbcRepository::new(app_state), + } + } + + pub async fn get_ack_by_tx_id( + &self, + tx_id: String, + ) -> Result { + self.ibc_repo + .find_ibc_ack(tx_id) + .await + .map_err(IbcError::Database) + .map(|ack| match ack { + Some(ack) => IbcAck { + status: match ack.status { + IbcAckStatusDb::Unknown => IbcAckStatus::Unknown, + IbcAckStatusDb::Timeout => IbcAckStatus::Timeout, + IbcAckStatusDb::Fail => IbcAckStatus::Fail, + IbcAckStatusDb::Success => IbcAckStatus::Success, + }, + }, + None => IbcAck { + status: IbcAckStatus::Unknown, + }, + }) + } +} diff --git a/webserver/src/service/mod.rs b/webserver/src/service/mod.rs index 3dda95f0c..a5566429a 100644 --- a/webserver/src/service/mod.rs +++ b/webserver/src/service/mod.rs @@ -3,6 +3,7 @@ pub mod chain; pub mod crawler_state; pub mod gas; pub mod governance; +pub mod ibc; pub mod pos; pub mod revealed_pk; pub mod transaction; diff --git a/webserver/src/state/common.rs b/webserver/src/state/common.rs index f50fafb06..8c2ff5703 100644 --- a/webserver/src/state/common.rs +++ b/webserver/src/state/common.rs @@ -7,6 +7,7 @@ use crate::service::chain::ChainService; use crate::service::crawler_state::CrawlerStateService; use crate::service::gas::GasService; use crate::service::governance::GovernanceService; +use crate::service::ibc::IbcService; use crate::service::pos::PosService; use crate::service::revealed_pk::RevealedPkService; use crate::service::transaction::TransactionService; @@ -21,6 +22,7 @@ pub struct CommonState { pub gas_service: GasService, pub transaction_service: TransactionService, pub crawler_state_service: CrawlerStateService, + pub ibc_service: IbcService, pub client: HttpClient, pub config: AppConfig, } @@ -36,6 +38,7 @@ impl CommonState { gas_service: GasService::new(data.clone()), transaction_service: TransactionService::new(data.clone()), crawler_state_service: CrawlerStateService::new(data.clone()), + ibc_service: IbcService::new(data.clone()), client, config, } From 1e15f3bb582feaf36c78281a66caec357349dc1c Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli Date: Fri, 13 Dec 2024 16:56:05 +0100 Subject: [PATCH 4/6] update swagger --- swagger.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/swagger.yml b/swagger.yml index ec99692d1..99d7f7164 100644 --- a/swagger.yml +++ b/swagger.yml @@ -621,6 +621,30 @@ paths: application/json: schema: $ref: '#/components/schemas/InnerTransaction' + /api/v1/ibc/{tx_id}/status: + get: + summary: Get the status of an IBC transfer by tx id + parameters: + - in: path + name: tx_id + schema: + type: string + required: true + description: Tx id hash + responses: + '200': + description: Status of the IBC transfer + content: + application/json: + schema: + type: array + items: + type: object + required: [status] + properties: + name: + type: string + enum: [unknown, timeout, success, fail] /api/v1/crawlers/timestamps: get: summary: Get timestamps of the last activity of the crawlers From 510b829aa01304075887acfc0848e2bd0c192030 Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli Date: Mon, 16 Dec 2024 14:38:38 +0100 Subject: [PATCH 5/6] minors --- orm/src/schema.rs | 72 ++++++++++++++++++++++++++------ rustfmt.toml | 1 - shared/src/block_result.rs | 4 +- transactions/src/main.rs | 4 +- transactions/src/services/mod.rs | 2 +- webserver/src/app.rs | 2 +- 6 files changed, 66 insertions(+), 19 deletions(-) diff --git a/orm/src/schema.rs b/orm/src/schema.rs index 9ff237292..7437f41fe 100644 --- a/orm/src/schema.rs +++ b/orm/src/schema.rs @@ -1,51 +1,99 @@ // @generated automatically by Diesel CLI. pub mod sql_types { - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "crawler_name"))] pub struct CrawlerName; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "governance_kind"))] pub struct GovernanceKind; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "governance_result"))] pub struct GovernanceResult; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "governance_tally_type"))] pub struct GovernanceTallyType; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "ibc_status"))] pub struct IbcStatus; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "payment_kind"))] pub struct PaymentKind; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "payment_recurrence"))] pub struct PaymentRecurrence; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "token_type"))] pub struct TokenType; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "transaction_kind"))] pub struct TransactionKind; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "transaction_result"))] pub struct TransactionResult; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "validator_state"))] pub struct ValidatorState; - #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] + #[derive( + diesel::query_builder::QueryId, + std::fmt::Debug, + diesel::sql_types::SqlType, + )] #[diesel(postgres_type(name = "vote_kind"))] pub struct VoteKind; } diff --git a/rustfmt.toml b/rustfmt.toml index f7a0911e7..6bfc04d34 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -24,7 +24,6 @@ format_macro_matchers = true format_strings = true group_imports = "StdExternalCrate" hard_tabs = false -show_parse_errors = true ignore = [] imports_granularity = "Module" imports_indent = "Block" diff --git a/shared/src/block_result.rs b/shared/src/block_result.rs index ac5f91a69..e6c17483b 100644 --- a/shared/src/block_result.rs +++ b/shared/src/block_result.rs @@ -131,7 +131,7 @@ pub struct SendPacket { #[derive(Debug, Clone)] pub enum TxAttributesType { TxApplied(TxApplied), - SendPacket(SendPacket) + SendPacket(SendPacket), } impl TxAttributesType { @@ -309,4 +309,4 @@ impl BlockResult { }); exit_status.unwrap_or(TransactionExitStatus::Rejected) } -} \ No newline at end of file +} diff --git a/transactions/src/main.rs b/transactions/src/main.rs index 5249deb98..abd728c7d 100644 --- a/transactions/src/main.rs +++ b/transactions/src/main.rs @@ -147,8 +147,8 @@ async fn crawling_fn( let ibc_ack_packet = tx_service::get_ibc_ack_packet(&inner_txs); tracing::info!( - "Deserialized {} wrappers, {} inners, {} ibc sequence numbers and {} ibc acks \ - events...", + "Deserialized {} wrappers, {} inners, {} ibc sequence numbers and {} \ + ibc acks events...", wrapper_txs.len(), inner_txs.len(), ibc_sequence_packet.len(), diff --git a/transactions/src/services/mod.rs b/transactions/src/services/mod.rs index 5c0b9ddc2..233652f55 100644 --- a/transactions/src/services/mod.rs +++ b/transactions/src/services/mod.rs @@ -1,4 +1,4 @@ pub mod db; pub mod namada; pub mod tendermint; -pub mod tx; \ No newline at end of file +pub mod tx; diff --git a/webserver/src/app.rs b/webserver/src/app.rs index 935eb0d57..fdbda7c5c 100644 --- a/webserver/src/app.rs +++ b/webserver/src/app.rs @@ -22,7 +22,7 @@ use crate::handler::{ balance as balance_handlers, chain as chain_handlers, crawler_state as crawler_state_handlers, gas as gas_handlers, governance as gov_handlers, ibc as ibc_handler, pk as pk_handlers, - pos as pos_handlers, transaction as transaction_handlers + pos as pos_handlers, transaction as transaction_handlers, }; use crate::state::common::CommonState; From 1b9153930633f6f11d10900dfcda7f3936213be3 Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli Date: Tue, 17 Dec 2024 10:23:06 +0100 Subject: [PATCH 6/6] minors --- chain/src/repository/balance.rs | 10 +++++----- shared/src/block.rs | 3 +++ transactions/src/main.rs | 1 + 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/chain/src/repository/balance.rs b/chain/src/repository/balance.rs index c09bbdb5c..a9ee0af01 100644 --- a/chain/src/repository/balance.rs +++ b/chain/src/repository/balance.rs @@ -133,7 +133,7 @@ mod tests { insert_tokens(conn, vec![token.clone()])?; - seed_blocks_from_balances(conn, &vec![balance.clone()])?; + seed_blocks_from_balances(conn, &[balance.clone()])?; insert_balances(conn, vec![balance.clone()])?; @@ -180,7 +180,7 @@ mod tests { ..(balance.clone()) }; - seed_blocks_from_balances(conn, &vec![new_balance.clone()])?; + seed_blocks_from_balances(conn, &[new_balance.clone()])?; insert_balances(conn, vec![new_balance])?; let queried_balance = @@ -418,7 +418,7 @@ mod tests { insert_tokens(conn, vec![token.clone()])?; - seed_blocks_from_balances(conn, &vec![balance.clone()])?; + seed_blocks_from_balances(conn, &[balance.clone()])?; insert_balances(conn, vec![balance.clone()])?; let queried_balance = query_balance_by_address(conn, owner, token)?; @@ -515,10 +515,10 @@ mod tests { fn seed_blocks_from_balances( conn: &mut PgConnection, - balances: &Vec, + balances: &[Balance], ) -> anyhow::Result<()> { for height in balances - .into_iter() + .iter() .map(|balance| balance.height as i32) .collect::>() { diff --git a/shared/src/block.rs b/shared/src/block.rs index ccfb0c563..efd3fb4be 100644 --- a/shared/src/block.rs +++ b/shared/src/block.rs @@ -104,6 +104,7 @@ pub struct Block { pub hash: Id, pub header: BlockHeader, pub transactions: Vec<(WrapperTransaction, Vec)>, + pub epoch: Epoch } impl Block { @@ -112,6 +113,7 @@ impl Block { block_results: &BlockResult, proposer_address_namada: &Option, // Provide the namada address of the proposer, if available checksums: Checksums, + epoch: Epoch, block_height: BlockHeight, ) -> Self { let transactions = block_response @@ -152,6 +154,7 @@ impl Block { app_hash: Id::from(&block_response.block.header.app_hash), }, transactions, + epoch } } diff --git a/transactions/src/main.rs b/transactions/src/main.rs index abd728c7d..fbaf35a5e 100644 --- a/transactions/src/main.rs +++ b/transactions/src/main.rs @@ -136,6 +136,7 @@ async fn crawling_fn( &block_results, &proposer_address_namada, checksums, + 1_u32, // placeholder, we dont need the epoch here block_height, );