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
4 changes: 2 additions & 2 deletions lib/Cargo.lock

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

7 changes: 7 additions & 0 deletions lib/ain-dftx/src/types/pool.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::fmt;

use ain_macros::ConsensusEncoding;
use bitcoin::{io, ScriptBuf};

Expand Down Expand Up @@ -65,3 +67,8 @@ pub struct PoolUpdatePair {
pub owner_address: ScriptBuf,
pub custom_rewards: Maybe<CompactVec<TokenBalanceUInt32>>,
}
impl fmt::Display for PoolId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "PoolId: {:?}", self.id)
}
}
10 changes: 5 additions & 5 deletions lib/ain-ocean/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@ use axum::{extract::Request, http::StatusCode, response::IntoResponse, Json, Rou

// mod address;
mod block;
mod cache;
pub mod common;
mod fee;
mod governance;
mod loan;
mod masternode;
mod oracle;
mod path;
mod pool_pair;
pub mod prices;
// mod rawtx;
mod cache;
pub mod common;
mod path;
mod query;
mod rawtx;
mod response;
mod stats;
mod tokens;
Expand Down Expand Up @@ -81,7 +81,7 @@ pub async fn ocean_router(
.nest("/oracles", oracle::router(Arc::clone(&context)))
.nest("/poolpairs", pool_pair::router(Arc::clone(&context)))
.nest("/prices", prices::router(Arc::clone(&context)))
// .nest("/rawtx", rawtx::router(Arc::clone(&context)))
.nest("/rawtx", rawtx::router(Arc::clone(&context)))
.nest("/stats", stats::router(Arc::clone(&context)))
.nest("/tokens", tokens::router(Arc::clone(&context)))
.nest("/transactions", transactions::router(Arc::clone(&context)))
Expand Down
3 changes: 1 addition & 2 deletions lib/ain-ocean/src/api/pool_pair/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use path::{
SwapPathsResponse,
};
use petgraph::graphmap::UnGraphMap;
use price::DexPriceResponse;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use serde_json::json;
Expand All @@ -44,8 +45,6 @@ use crate::{
Result, TokenIdentifier,
};

use price::DexPriceResponse;

pub mod path;
pub mod price;
pub mod service;
Expand Down
3 changes: 1 addition & 2 deletions lib/ain-ocean/src/api/pool_pair/price.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use serde::Serialize;
use std::{collections::HashMap, sync::Arc};

use defichain_rpc::json::token::TokenInfo;
use serde::Serialize;

use super::{path::get_best_path, AppContext};

use crate::{
api::{
cache::{get_token_cached, list_token_cached},
Expand Down
2 changes: 1 addition & 1 deletion lib/ain-ocean/src/api/pool_pair/service.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{collections::HashMap, str::FromStr, sync::Arc};

use ain_dftx::{deserialize, pool::CompositeSwap, DfTx, Stack};
use anyhow::{format_err, Context};
use bitcoin::Txid;
use defichain_rpc::{json::poolpair::PoolPairInfo, AccountRPC, BlockchainRPC};
Expand All @@ -21,7 +22,6 @@ use crate::{
storage::SortOrder,
Result,
};
use ain_dftx::{deserialize, pool::CompositeSwap, DfTx, Stack};

#[allow(clippy::upper_case_acronyms)]
#[derive(Serialize, Deserialize, Debug, Clone)]
Expand Down
222 changes: 209 additions & 13 deletions lib/ain-ocean/src/api/rawtx.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,223 @@
use std::sync::Arc;
use std::{result::Result as StdResult, str::FromStr, sync::Arc};

use ain_dftx::{deserialize, DfTx};
use ain_macros::ocean_endpoint;
use axum::{
extract::{Json, Path},
routing::{get, post},
Router,
Extension, Router,
};
use defichain_rpc::{Client, RpcApi};
use super::path::Path;
use bitcoin::{Transaction, Txid};
use defichain_rpc::{PoolPairRPC, RpcApi};
use rust_decimal::prelude::ToPrimitive;
use rust_decimal_macros::dec;
use serde::{Deserialize, Serialize, Serializer};

async fn send_rawtx() -> String {
"Sending raw transaction".to_string()
use super::{query::Query, response::Response, AppContext};
use crate::{
error::{ApiError, NotFoundKind},
model::{default_max_fee_rate, MempoolAcceptResult, RawTransactionResult, RawTxDto},
Error, Result,
};

enum TransactionResponse {
HexString(String),
TransactionDetails(Box<RawTransactionResult>),
}

#[derive(Deserialize, Default)]
struct QueryParams {
verbose: bool,
}

async fn test_rawtx() -> String {
"Testing raw transaction".to_string()
#[ocean_endpoint]
async fn send_raw_tx(
Extension(ctx): Extension<Arc<AppContext>>,
Json(raw_tx_dto): Json<RawTxDto>,
) -> Result<String> {
validate(ctx.clone(), raw_tx_dto.hex.clone()).await?;
let max_fee = match raw_tx_dto.max_fee_rate {
Some(fee_rate) => {
let sat_per_bitcoin = dec!(100_000_000);
let fee_in_satoshis = fee_rate.checked_mul(sat_per_bitcoin);
match fee_in_satoshis {
Some(value) => Some(value.to_u64().unwrap_or_default()),
None => Some(default_max_fee_rate().to_sat()),
}
}
None => Some(default_max_fee_rate().to_sat()),
};
match ctx
.client
.send_raw_transaction(raw_tx_dto.hex, max_fee)
.await
{
Ok(tx_hash) => Ok(tx_hash.to_string()),
Err(e) => {
eprintln!("Failed to send raw transaction: {:?}", e);
if e.to_string().contains("TX decode failed") {
Err(Error::BadRequest("Transaction decode failed".to_string()))
} else {
Err(Error::RpcError(e))
}
}
}
}
#[ocean_endpoint]
async fn test_raw_tx(
Extension(ctx): Extension<Arc<AppContext>>,
Json(raw_tx_dto): Json<RawTxDto>,
) -> Result<Response<Vec<MempoolAcceptResult>>> {
let trx = defichain_rpc::RawTx::raw_hex(raw_tx_dto.hex);
let max_fee = match raw_tx_dto.max_fee_rate {
Some(fee_rate) => {
let sat_per_bitcoin = dec!(100_000_000);
let fee_in_satoshis = fee_rate.checked_mul(sat_per_bitcoin);
match fee_in_satoshis {
Some(value) => Some(value.to_u64().unwrap_or_default()),
None => Some(default_max_fee_rate().to_sat()),
}
}
None => Some(default_max_fee_rate().to_sat()),
};
match ctx.client.test_mempool_accept(&[trx], max_fee).await {
Ok(mempool_tx) => {
let results = mempool_tx
.into_iter()
.map(|tx_result| MempoolAcceptResult {
txid: tx_result.txid,
allowed: tx_result.allowed,
reject_reason: tx_result.reject_reason,
vsize: tx_result.vsize,
fees: tx_result.fees.map(|f| f.base),
})
.collect::<Vec<MempoolAcceptResult>>();
Ok(Response::new(results))
}
Err(e) => {
eprintln!("Failed to send raw transaction: {:?}", e);
if e.to_string().contains("TX decode failed") {
Err(Error::BadRequest("Transaction decode failed".to_string()))
} else {
Err(Error::RpcError(e))
}
}
}
}

async fn get_rawtx(Path(txid): Path<String>) -> String {
format!("Details of raw transaction with txid {}", txid)
impl Serialize for TransactionResponse {
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
where
S: Serializer,
{
match *self {
TransactionResponse::HexString(ref s) => serializer.serialize_str(s),
TransactionResponse::TransactionDetails(ref details) => details.serialize(serializer),
}
}
}

#[ocean_endpoint]
async fn get_raw_tx(
Comment thread
nagarajm22 marked this conversation as resolved.
Extension(ctx): Extension<Arc<AppContext>>,
Path(txid): Path<String>,
Query(QueryParams { verbose }): Query<QueryParams>,
) -> Result<TransactionResponse> {
let tx_hash = Txid::from_str(&txid)?;
if !verbose {
let tx_hex = ctx.client.get_raw_transaction_hex(&tx_hash, None).await.map_err(|e| {
if e.to_string().contains("No such mempool or blockchain transaction. Use gettransaction for wallet transactions.") {
Error::NotFound(NotFoundKind::RawTx)
} else {
Error::RpcError(e)
}
})?;
Ok(TransactionResponse::HexString(tx_hex))
} else {
let tx_info = ctx
.client
.get_raw_transaction_info(&tx_hash, None)
.await
.map_err(|e| {
eprintln!("Failed to get raw transaction hex: {:?}", e);
Error::RpcError(e)
})?;
let result = RawTransactionResult {
in_active_chain: tx_info.in_active_chain,
hex: tx_info.hex,
txid: tx_info.txid,
hash: tx_info.hash,
size: tx_info.size,
vsize: tx_info.vsize,
version: tx_info.version,
locktime: tx_info.locktime,
vin: tx_info.vin,
vout: tx_info.vout,
blockhash: tx_info.blockhash,
confirmations: tx_info.confirmations,
time: tx_info.time,
blocktime: tx_info.blocktime,
};
Ok(TransactionResponse::TransactionDetails(Box::new(result)))
}
}

async fn validate(ctx: Arc<AppContext>, hex: String) -> Result<()> {
if !hex.starts_with("040000000001") {
return Ok(());
}
let data = hex::decode(hex)?;
println!("decode_hex {:?}", data);
let trx = deserialize::<Transaction>(&data)?;
let bytes = trx.output[0].clone().script_pubkey.into_bytes();
let tx: Option<DfTx> = if bytes.len() > 2 && bytes[0] == 0x6a && bytes[1] <= 0x4e {
let offset = 1 + match bytes[1] {
0x4c => 2,
0x4d => 3,
0x4e => 4,
_ => 1,
};

let raw_tx = &bytes[offset..];
Some(deserialize::<DfTx>(raw_tx)?)
} else {
return Ok(());
};

if let Some(tx) = tx {
if let DfTx::CompositeSwap(composite_swap) = tx {
if composite_swap.pools.as_ref().is_empty() {
return Ok(());
}
let pool_id = composite_swap.pools.iter().last().unwrap();
let tokio_id = composite_swap.pool_swap.to_token_id.0.to_string();
let pool_pair = ctx
.client
.get_pool_pair(pool_id.to_string(), Some(true))
.await?;
for (_, pool_pair_info) in pool_pair.0 {
if pool_pair_info.id_token_a.eq(&tokio_id)
|| pool_pair_info.id_token_b.eq(&tokio_id)
{
println!("Found a match: {:?}", pool_pair_info);
}
}
Ok(())
} else {
Err(Error::BadRequest(
"Transaction is not a composite swap".to_string(),
))
}
} else {
Ok(())
}
}

pub fn router(ctx: Arc<AppContext>) -> Router {
println!("{:?}", ctx.network);
Router::new()
.route("/send", post(send_rawtx))
.route("/test", get(test_rawtx))
.route("/:txid", get(get_rawtx))
.route("/send", post(send_raw_tx))
.route("/test", post(test_raw_tx))
.route("/:txid", get(get_raw_tx))
.layer(Extension(ctx))
}
7 changes: 7 additions & 0 deletions lib/ain-ocean/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ pub enum NotFoundKind {
Token,
#[error("poolpair")]
PoolPair,
#[error("rawtx")]
RawTx,
}

#[derive(Error, Debug)]
Expand Down Expand Up @@ -70,6 +72,10 @@ pub enum Error {
TryFromIntError(#[from] std::num::TryFromIntError),
#[error(transparent)]
Other(#[from] anyhow::Error),
#[error("Validation error: {0}")]
ValidationError(String),
#[error("{0}")]
BadRequest(String),
}

#[derive(Serialize)]
Expand Down Expand Up @@ -146,6 +152,7 @@ impl Error {
)
}
Error::NotFound(_) => (StatusCode::NOT_FOUND, format!("{self}")),
Error::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg.clone()),
_ => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()),
};
(code, reason)
Expand Down
2 changes: 2 additions & 0 deletions lib/ain-ocean/src/model/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod poolswap;
mod poolswap_aggregated;
mod price_ticker;
mod raw_block;
mod raw_tx;
mod script_activity;
mod script_aggregation;
mod script_unspent;
Expand All @@ -33,6 +34,7 @@ pub use oracle_token_currency::*;
pub use poolswap::*;
pub use poolswap_aggregated::*;
pub use price_ticker::*;
pub use raw_tx::*;
// pub use raw_block::*;
// pub use script_activity::*;
// pub use script_aggregation::*;
Expand Down
Loading