diff --git a/lib/ain-dftx/src/types/account.rs b/lib/ain-dftx/src/types/account.rs index 93c29dbcb6..af1fd78c40 100644 --- a/lib/ain-dftx/src/types/account.rs +++ b/lib/ain-dftx/src/types/account.rs @@ -6,31 +6,31 @@ use super::{ common::CompactVec, }; -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct UtxosToAccount { pub to: CompactVec, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct AccountToUtxos { pub from: ScriptBuf, pub balances: CompactVec, pub minting_outputs_start: VarInt, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct AccountToAccount { pub from: ScriptBuf, pub to: CompactVec, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct AnyAccountsToAccounts { pub from: CompactVec, pub to: CompactVec, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct TransferDomainItem { pub address: ScriptBuf, pub amount: TokenBalanceVarInt, @@ -38,18 +38,18 @@ pub struct TransferDomainItem { pub data: Vec, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct TransferDomainPair { pub src: TransferDomainItem, pub dst: TransferDomainItem, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct TransferDomain { pub items: CompactVec, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct SetFutureSwap { pub owner: ScriptBuf, pub source: TokenBalanceVarInt, diff --git a/lib/ain-dftx/src/types/balance.rs b/lib/ain-dftx/src/types/balance.rs index 566bc68d79..44f2ca22d0 100644 --- a/lib/ain-dftx/src/types/balance.rs +++ b/lib/ain-dftx/src/types/balance.rs @@ -4,20 +4,20 @@ use bitcoin::{io, ScriptBuf, VarInt}; use super::common::CompactVec; // CBalances -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct TokenBalanceUInt32 { pub token: u32, pub amount: i64, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct ScriptBalances { pub script: ScriptBuf, pub balances: CompactVec, } // CTokenAmount -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct TokenBalanceVarInt { pub token: VarInt, pub amount: i64, diff --git a/lib/ain-dftx/src/types/common.rs b/lib/ain-dftx/src/types/common.rs index 32acc7d730..26db0bf23d 100644 --- a/lib/ain-dftx/src/types/common.rs +++ b/lib/ain-dftx/src/types/common.rs @@ -5,7 +5,7 @@ use bitcoin::{ io::{self, ErrorKind}, }; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct CompactVec(Vec); impl Encodable for CompactVec { @@ -46,7 +46,7 @@ impl AsRef> for CompactVec { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct Maybe(pub Option); impl Encodable for Maybe { fn consensus_encode( @@ -82,7 +82,7 @@ impl From> for Maybe { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct RawBytes(pub Vec); impl Encodable for RawBytes { @@ -110,7 +110,7 @@ impl Decodable for RawBytes { /// In the rust-bitcoin library, variable-length integers are implemented as CompactSize. /// See [issue #1016](https://github.com/rust-bitcoin/rust-bitcoin/issues/1016) -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct VarInt(pub u64); impl Encodable for VarInt { diff --git a/lib/ain-dftx/src/types/evmtx.rs b/lib/ain-dftx/src/types/evmtx.rs index 35508fcda6..c1d368649a 100644 --- a/lib/ain-dftx/src/types/evmtx.rs +++ b/lib/ain-dftx/src/types/evmtx.rs @@ -1,7 +1,7 @@ use ain_macros::ConsensusEncoding; use bitcoin::io; -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct EvmTx { pub raw: Vec, } diff --git a/lib/ain-dftx/src/types/governance.rs b/lib/ain-dftx/src/types/governance.rs index 388dc8753d..d49597db36 100644 --- a/lib/ain-dftx/src/types/governance.rs +++ b/lib/ain-dftx/src/types/governance.rs @@ -8,43 +8,43 @@ use bitcoin::{ use super::common::CompactVec; use crate::{common::RawBytes, types::common::Maybe}; -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct LiqPoolSplit { pub token_id: u32, pub value: i64, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct LpDailyReward { pub key: String, pub value: i64, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct LpSplits { pub key: String, pub value: CompactVec, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct LpUnmapped { pub key: String, pub value: RawBytes, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct LoanTokenSplit { pub token_id: VarInt, pub value: i64, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct LpLoanTokenSplits { pub key: String, pub value: CompactVec, } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum GovernanceVar { LpDailyReward(LpDailyReward), LpSplits(LpSplits), @@ -89,7 +89,7 @@ impl Decodable for GovernanceVar { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct SetGovernance { pub governance_vars: Vec, } @@ -117,7 +117,7 @@ impl Decodable for SetGovernance { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct SetGovernanceHeight { pub var: GovernanceVar, pub activation_height: u32, @@ -150,7 +150,7 @@ impl Decodable for SetGovernanceHeight { } } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct CreateProposal { pub r#type: u8, pub address: ScriptBuf, @@ -162,7 +162,7 @@ pub struct CreateProposal { pub options: u8, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct Vote { pub proposal_id: Txid, pub masternode_id: Txid, diff --git a/lib/ain-dftx/src/types/icxorderbook.rs b/lib/ain-dftx/src/types/icxorderbook.rs index f64549d3bb..48f76e0b15 100644 --- a/lib/ain-dftx/src/types/icxorderbook.rs +++ b/lib/ain-dftx/src/types/icxorderbook.rs @@ -3,7 +3,7 @@ use bitcoin::{io, ScriptBuf, Txid}; use crate::common::{CompactVec, Maybe, VarInt}; -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct ICXCreateOrder { pub order_type: u8, pub token_id: VarInt, @@ -15,7 +15,7 @@ pub struct ICXCreateOrder { pub expiry: u32, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct ICXMakeOffer { pub order_tx: Txid, pub amount: i64, @@ -25,7 +25,7 @@ pub struct ICXMakeOffer { pub taker_fee: u64, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct ICXSubmitDFCHTLC { pub offer_tx: Txid, pub amount: i64, @@ -33,7 +33,7 @@ pub struct ICXSubmitDFCHTLC { pub timeout: u32, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct ICXSubmitEXTHTLC { pub offer_tx: Txid, pub amount: i64, @@ -43,18 +43,18 @@ pub struct ICXSubmitEXTHTLC { pub timeout: u32, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct ICXClaimDFCHTLC { pub dfc_htlc_tx: Txid, pub seed: Vec, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct ICXCloseOrder { pub order_tx: Txid, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct ICXCloseOffer { pub offer_tx: Txid, } diff --git a/lib/ain-dftx/src/types/loans.rs b/lib/ain-dftx/src/types/loans.rs index d2259d14cd..1d0da6fe3f 100644 --- a/lib/ain-dftx/src/types/loans.rs +++ b/lib/ain-dftx/src/types/loans.rs @@ -3,7 +3,7 @@ use bitcoin::{io, ScriptBuf, Txid, VarInt}; use super::{balance::TokenBalanceUInt32, common::CompactVec, price::CurrencyPair}; -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct SetLoanScheme { pub ratio: u32, pub rate: i64, @@ -11,18 +11,18 @@ pub struct SetLoanScheme { pub update: i64, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct DestroyLoanScheme { pub identifier: String, pub height: i64, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct SetDefaultLoanScheme { pub identifier: String, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct SetCollateralToken { pub token: VarInt, pub factor: i64, @@ -30,7 +30,7 @@ pub struct SetCollateralToken { pub activate_after_block: u32, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct SetLoanToken { pub symbol: String, pub name: String, @@ -39,7 +39,7 @@ pub struct SetLoanToken { pub interest: i64, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct UpdateLoanToken { pub symbol: String, pub name: String, @@ -49,27 +49,27 @@ pub struct UpdateLoanToken { pub token_tx: Txid, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct TakeLoan { pub vault_id: Txid, pub to: ScriptBuf, pub token_amounts: CompactVec, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct PaybackLoan { pub vault_id: Txid, pub from: ScriptBuf, pub token_amounts: CompactVec, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct TokenPayback { pub d_token: VarInt, pub amounts: CompactVec, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct PaybackLoanV2 { pub vault_id: Txid, pub from: ScriptBuf, diff --git a/lib/ain-dftx/src/types/masternode.rs b/lib/ain-dftx/src/types/masternode.rs index f3fdab10e3..7855c2bf67 100644 --- a/lib/ain-dftx/src/types/masternode.rs +++ b/lib/ain-dftx/src/types/masternode.rs @@ -7,19 +7,19 @@ use bitcoin::{ use super::common::{CompactVec, Maybe}; -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct CreateMasternode { pub operator_type: u8, pub operator_pub_key_hash: PubkeyHash, pub timelock: Maybe, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct ResignMasternode { pub node_id: Txid, } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct UpdateMasternodeAddress { pub r#type: u8, pub address_pub_key_hash: Option, @@ -59,13 +59,13 @@ impl Decodable for UpdateMasternodeAddress { } } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct UpdateMasternodeData { pub r#type: u8, pub address: UpdateMasternodeAddress, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct UpdateMasternode { pub node_id: Txid, pub updates: CompactVec, diff --git a/lib/ain-dftx/src/types/mod.rs b/lib/ain-dftx/src/types/mod.rs index 69d09799eb..17ac12ea49 100644 --- a/lib/ain-dftx/src/types/mod.rs +++ b/lib/ain-dftx/src/types/mod.rs @@ -28,7 +28,7 @@ pub type Token = String; pub type Currency = String; pub type Weightage = u8; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum DfTx { AccountToAccount(AccountToAccount), AccountToUtxos(AccountToUtxos), diff --git a/lib/ain-dftx/src/types/oracles.rs b/lib/ain-dftx/src/types/oracles.rs index 45e6cd0116..36eb9c62fd 100644 --- a/lib/ain-dftx/src/types/oracles.rs +++ b/lib/ain-dftx/src/types/oracles.rs @@ -7,26 +7,26 @@ use super::{ Weightage, }; -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct SetOracleData { pub oracle_id: Txid, pub timestamp: i64, pub token_prices: CompactVec, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct RemoveOracle { pub oracle_id: Txid, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct AppointOracle { pub script: ScriptBuf, pub weightage: Weightage, pub price_feeds: CompactVec, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct UpdateOracle { pub oracle_id: Txid, pub script: ScriptBuf, diff --git a/lib/ain-dftx/src/types/pool.rs b/lib/ain-dftx/src/types/pool.rs index d9550fef9b..9fc8620087 100644 --- a/lib/ain-dftx/src/types/pool.rs +++ b/lib/ain-dftx/src/types/pool.rs @@ -9,13 +9,13 @@ use super::{ }; use crate::common::VarInt; -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct MaxPrice { integer: i64, fraction: i64, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct PoolSwap { pub from_script: ScriptBuf, pub from_token_id: VarInt, @@ -25,30 +25,30 @@ pub struct PoolSwap { pub max_price: MaxPrice, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct PoolId { pub id: VarInt, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct CompositeSwap { pub pool_swap: PoolSwap, pub pools: CompactVec, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct PoolAddLiquidity { pub from: CompactVec, pub share_address: ScriptBuf, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct PoolRemoveLiquidity { pub script: ScriptBuf, pub amount: TokenBalanceVarInt, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct CreatePoolPair { pub token_a: VarInt, pub token_b: VarInt, @@ -59,7 +59,7 @@ pub struct CreatePoolPair { pub custom_rewards: Maybe>, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct PoolUpdatePair { pub pool_id: u32, pub status: u8, diff --git a/lib/ain-dftx/src/types/price.rs b/lib/ain-dftx/src/types/price.rs index 2d5bc3bce5..bba56a5f8a 100644 --- a/lib/ain-dftx/src/types/price.rs +++ b/lib/ain-dftx/src/types/price.rs @@ -3,19 +3,19 @@ use bitcoin::io; use super::common::CompactVec; -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct CurrencyPair { pub token: String, pub currency: String, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct TokenAmount { pub currency: String, pub amount: i64, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct TokenPrice { pub token: String, pub prices: CompactVec, diff --git a/lib/ain-dftx/src/types/token.rs b/lib/ain-dftx/src/types/token.rs index fd9e125b8f..c9a5614cc2 100644 --- a/lib/ain-dftx/src/types/token.rs +++ b/lib/ain-dftx/src/types/token.rs @@ -8,14 +8,14 @@ use bitflags::bitflags; use super::{balance::TokenBalanceUInt32, common::CompactVec}; use crate::common::Maybe; -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct MintToken { pub balances: CompactVec, pub to: Maybe, } bitflags! { - #[derive(Debug, PartialEq, Eq)] + #[derive(Debug, PartialEq, Eq, Clone)] struct TokenFlags: u8 { const NONE = 0; const MINTABLE = 0x01; @@ -49,7 +49,7 @@ impl Decodable for TokenFlags { } } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct CreateToken { pub symbol: String, pub name: String, @@ -58,25 +58,25 @@ pub struct CreateToken { pub flags: u8, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct UpdateToken { pub creation_tx: Txid, pub is_dat: bool, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct UpdateTokenAny { pub creation_tx: Txid, pub token: CreateToken, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct VariantScript { pub r#type: u32, pub context: ScriptBuf, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct BurnToken { pub amounts: CompactVec, pub from: ScriptBuf, diff --git a/lib/ain-dftx/src/types/vault.rs b/lib/ain-dftx/src/types/vault.rs index a0c01eb283..48bd863810 100644 --- a/lib/ain-dftx/src/types/vault.rs +++ b/lib/ain-dftx/src/types/vault.rs @@ -3,40 +3,40 @@ use bitcoin::{io, ScriptBuf, Txid}; use super::balance::TokenBalanceVarInt; -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct CreateVault { pub owner_address: ScriptBuf, pub scheme_id: String, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct UpdateVault { pub vault_id: Txid, pub owner_address: ScriptBuf, pub scheme_id: String, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct DepositToVault { pub vault_id: Txid, pub from: ScriptBuf, pub token_amount: TokenBalanceVarInt, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct WithdrawFromVault { pub vault_id: Txid, pub to: ScriptBuf, pub token_amount: TokenBalanceVarInt, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct CloseVault { pub vault_id: Txid, pub to: ScriptBuf, } -#[derive(ConsensusEncoding, Debug, PartialEq, Eq)] +#[derive(ConsensusEncoding, Debug, PartialEq, Eq, Clone)] pub struct PlaceAuctionBid { pub vault_id: Txid, pub index: u32, diff --git a/lib/ain-ocean/src/api/common.rs b/lib/ain-ocean/src/api/common.rs index e6912d918e..64d79f0d9f 100644 --- a/lib/ain-ocean/src/api/common.rs +++ b/lib/ain-ocean/src/api/common.rs @@ -1,7 +1,7 @@ use std::str::FromStr; use ain_dftx::{Currency, Token}; -use bitcoin::{Address, ScriptBuf}; +use bitcoin::{Address, ScriptBuf, Txid}; use defichain_rpc::json::token::TokenInfo; use rust_decimal::Decimal; use rust_decimal_macros::dec; @@ -10,8 +10,8 @@ use snafu::OptionExt; use super::query::PaginationQuery; use crate::{ error::{ - InvalidAmountSnafu, InvalidFixedIntervalPriceSnafu, InvalidPoolPairSymbolSnafu, - InvalidTokenCurrencySnafu, + Error::ToArrayError, InvalidAmountSnafu, InvalidFixedIntervalPriceSnafu, + InvalidPoolPairSymbolSnafu, InvalidTokenCurrencySnafu, }, hex_encoder::as_sha256, network::Network, @@ -114,6 +114,20 @@ pub fn parse_query_height_txno(item: &str) -> Result<(u32, usize)> { Ok((height, txno)) } +pub fn parse_query_height_txid(item: &str) -> Result<(u32, Txid)> { + let mut parts = item.split('-'); + let encoded_height = parts.next().context(InvalidAmountSnafu { item })?; + let txid = parts.next().context(InvalidAmountSnafu { item })?; + + let height_in_bytes: [u8; 4] = hex::decode(encoded_height)? + .try_into() + .map_err(|_| ToArrayError)?; + let height = u32::from_be_bytes(height_in_bytes); + let txid = txid.parse::()?; + + Ok((height, txid)) +} + #[must_use] pub fn format_number(v: Decimal) -> String { if v == dec!(0) { diff --git a/lib/ain-ocean/src/api/loan.rs b/lib/ain-ocean/src/api/loan.rs index 6acdb898ee..85731fc68b 100644 --- a/lib/ain-ocean/src/api/loan.rs +++ b/lib/ain-ocean/src/api/loan.rs @@ -1,8 +1,9 @@ use std::{str::FromStr, sync::Arc}; +use ain_dftx::COIN; use ain_macros::ocean_endpoint; use axum::{routing::get, Extension, Router}; -use bitcoin::{hashes::Hash, Txid}; +use bitcoin::{hashes::Hash, ScriptBuf, Txid}; use defichain_rpc::{ defichain_rpc_json::{ loan::{CollateralTokenDetail, LoanSchemeResult}, @@ -17,7 +18,8 @@ use defichain_rpc::{ }; use futures::future::try_join_all; use log::trace; -use serde::{Serialize, Serializer}; +use rust_decimal::Decimal; +use serde::{Deserialize, Serialize, Serializer}; use serde_with::skip_serializing_none; use snafu::OptionExt; @@ -25,7 +27,7 @@ use super::{ cache::{get_loan_scheme_cached, get_token_cached}, common::{ from_script, parse_amount, parse_display_symbol, parse_fixed_interval_price, - parse_query_height_txno, Paginate, + parse_query_height_txid, Paginate, }, path::Path, query::{PaginationQuery, Query}, @@ -35,8 +37,9 @@ use super::{ }; use crate::{ error::{ApiError, Error, NotFoundKind, NotFoundSnafu}, - model::{OraclePriceActive, VaultAuctionBatchHistory}, - storage::{RepositoryOps, SecondaryIndex, SortOrder}, + model::{BlockContext, OraclePriceActive, VaultAuctionBatchHistory}, + network::Network, + storage::{RepositoryOps, SortOrder}, Result, }; @@ -126,34 +129,24 @@ impl CollateralToken { } } -async fn get_active_price( +fn get_active_price( ctx: &Arc, fixed_interval_price_id: String, ) -> Result> { let (token, currency) = parse_fixed_interval_price(&fixed_interval_price_id)?; - let repo = &ctx.services.oracle_price_active; - let keys = repo - .by_key - .list(Some((token, currency)), SortOrder::Descending)? - .take(1) - .flatten() - .collect::>(); - - if keys.is_empty() { - return Ok(None); - } - - let Some((_, id)) = keys.first() else { - return Ok(None); - }; - - let price = repo.by_id.get(id)?; - - let Some(price) = price else { - return Ok(None); - }; + let price = ctx + .services + .oracle_price_active + .by_id + .list(Some((token, currency, u32::MAX)), SortOrder::Descending)? + .next() + .map(|item| { + let (_, v) = item?; + Ok::(v) + }) + .transpose()?; - Ok(Some(price)) + Ok(price) } #[ocean_endpoint] @@ -179,7 +172,7 @@ async fn list_collateral_token( id: v.token_id.clone(), }, })?; - let active_price = get_active_price(&ctx, v.fixed_interval_price_id.clone()).await?; + let active_price = get_active_price(&ctx, v.fixed_interval_price_id.clone())?; Ok::(CollateralToken::from_with_id(id, v, info, active_price)) }) .collect::>(); @@ -210,8 +203,7 @@ async fn get_collateral_token( id: collateral_token.token_id.clone(), }, })?; - let active_price = - get_active_price(&ctx, collateral_token.fixed_interval_price_id.clone()).await?; + let active_price = get_active_price(&ctx, collateral_token.fixed_interval_price_id.clone())?; Ok(Response::new(CollateralToken::from_with_id( id, @@ -268,13 +260,17 @@ async fn list_loan_token( let fixed_interval_price_id = flatten_token.fixed_interval_price_id.clone(); let (token, currency) = parse_fixed_interval_price(&fixed_interval_price_id)?; - let repo = &ctx.services.oracle_price_active; - let key = repo.by_key.get(&(token, currency))?; - let active_price = if let Some(key) = key { - repo.by_id.get(&key)? - } else { - None - }; + let active_price = ctx + .services + .oracle_price_active + .by_id + .list(Some((token, currency, u32::MAX)), SortOrder::Descending)? + .next() + .map(|item| { + let (_, v) = item?; + Ok::(v) + }) + .transpose()?; let token = LoanToken { token_id: flatten_token.data.creation_tx.clone(), @@ -311,15 +307,7 @@ async fn get_loan_token( .next() .map(|(id, info)| { let fixed_interval_price_id = loan_token_result.fixed_interval_price_id.clone(); - let (token, currency) = parse_fixed_interval_price(&fixed_interval_price_id)?; - - let repo = &ctx.services.oracle_price_active; - let key = repo.by_key.get(&(token, currency))?; - let active_price = if let Some(key) = key { - repo.by_id.get(&key)? - } else { - None - }; + let active_price = get_active_price(&ctx, fixed_interval_price_id.clone())?; Ok::(LoanToken { token_id: info.creation_tx.clone(), @@ -503,55 +491,111 @@ async fn get_vault( Ok(Response::new(res)) } +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct VaultAuctionBatchHistoryResponse { + pub id: String, + pub key: String, + pub sort: String, + pub vault_id: Txid, + pub index: u32, + pub from: ScriptBuf, + pub address: String, + pub amount: String, + pub token_id: u64, + pub block: BlockContext, +} + +impl VaultAuctionBatchHistoryResponse { + fn from( + data: ((Txid, [u8; 4], [u8; 4], Txid), VaultAuctionBatchHistory), + address: String, + ) -> Self { + let id = data.0; + let vault_id = id.0; + let batch_index = u32::from_be_bytes(id.1); + let block_height = id.2; + let txid = id.3; + let history = data.1; + let amount = Decimal::from(history.amount) / Decimal::from(COIN); + + Self { + id: format!( + "{}-{}-{}", + vault_id.clone(), + batch_index.clone(), + txid.clone() + ), + key: format!("{}-{}", vault_id.clone(), batch_index.clone()), + sort: format!("{}-{}", hex::encode(block_height), txid), + vault_id, + index: batch_index, + from: history.from, + address, + amount: amount.to_string(), + token_id: history.token_id, + block: history.block, + } + } +} + #[ocean_endpoint] async fn list_vault_auction_history( - Path((vault_id, height, batch_index)): Path<(Txid, u32, u32)>, + Path((vault_id, liquidation_height, batch_index)): Path<(Txid, u32, u32)>, Query(query): Query, Extension(ctx): Extension>, -) -> Result> { +) -> Result> { trace!( - "Auction history for vault id {}, height {}, batch index {}", + "Auction history for vault id {}, liquidation_height {}, batch index {}", vault_id, - height, + liquidation_height, batch_index ); - let next = query + let (liquidation_height, txid) = query .next + .clone() .map(|q| { - let (height, txno) = parse_query_height_txno(&q)?; - Ok::<(u32, usize), Error>((height, txno)) + let (height, txid) = parse_query_height_txid(&q)?; + Ok::<(u32, Txid), Error>((height, txid)) }) .transpose()? - .unwrap_or_default(); + .unwrap_or((liquidation_height, Txid::from_byte_array([0xffu8; 32]))); - let size = if query.size > 0 { query.size } else { 20 }; + let liquidation_block_expiry = match ctx.network { + Network::Regtest => 36, + _ => 720, + }; let auctions = ctx .services .auction - .by_height + .by_id .list( - Some((vault_id, batch_index, next.0, next.1)), + Some(( + vault_id, + batch_index.to_be_bytes(), + liquidation_height.to_be_bytes(), + txid, + )), SortOrder::Descending, )? - .take(size) .take_while(|item| match item { - Ok((k, _)) => k.0 == vault_id && k.1 == batch_index, + Ok((k, _)) => { + k.0 == vault_id + && k.1 == batch_index.to_be_bytes() + && u32::from_be_bytes(k.2) > liquidation_height - liquidation_block_expiry + } _ => true, }) + .take(query.size + usize::from(query.next.is_some())) + .skip(usize::from(query.next.is_some())) .map(|item| { - let (_, id) = item?; - - let auction = ctx - .services - .auction - .by_id - .get(&id)? - .context(NotFoundSnafu { - kind: NotFoundKind::Auction, - })?; - - Ok(auction) + let (id, history) = item?; + let address = from_script(&history.from, ctx.network)?; + Ok(VaultAuctionBatchHistoryResponse::from( + (id, history), + address, + )) }) .collect::>>()?; @@ -626,14 +670,17 @@ async fn map_liquidation_batches( }; let id = ( Txid::from_str(vault_id)?, - batch.index, + batch.index.to_be_bytes(), + [0xffu8, 0xffu8, 0xffu8, 0xffu8], Txid::from_byte_array([0xffu8; 32]), ); let bids = repo .by_id .list(Some(id), SortOrder::Descending)? .take_while(|item| match item { - Ok(((vid, bindex, _), _)) => vid.to_string() == vault_id && bindex == &batch.index, + Ok(((vid, bindex, _, _), _)) => { + vid.to_string() == vault_id && bindex == &batch.index.to_be_bytes() + } _ => true, }) .collect::>(); @@ -680,23 +727,25 @@ async fn map_token_amounts( log::error!("Token {token_symbol} not found"); continue; }; - let repo = &ctx.services.oracle_price_active; - let keys = repo - .by_key - .list(None, SortOrder::Descending)? - .collect::>(); - log::trace!("list_auctions keys: {:?}, token_id: {:?}", keys, id); - let active_price = repo - .by_key - .list(None, SortOrder::Descending)? - .take(1) + let active_price = ctx + .services + .oracle_price_active + .by_id + .list( + Some((token_info.symbol.clone(), "USD".to_string(), u32::MAX)), + SortOrder::Descending, + )? .take_while(|item| match item { - Ok((k, _)) => k.0 == id, + Ok((k, _)) => k.0 == token_info.symbol && k.1 == "USD", _ => true, }) - .map(|el| repo.by_key.retrieve_primary_value(el)) - .collect::>>()?; + .next() + .map(|item| { + let (_, v) = item?; + Ok::(v) + }) + .transpose()?; vault_token_amounts.push(VaultTokenAmountResponse { id, @@ -705,7 +754,7 @@ async fn map_token_amounts( symbol: token_info.symbol, symbol_key: token_info.symbol_key, name: token_info.name, - active_price: active_price.first().cloned(), + active_price, }); } diff --git a/lib/ain-ocean/src/api/mod.rs b/lib/ain-ocean/src/api/mod.rs index 0a0d8bbfb0..a975627739 100644 --- a/lib/ain-ocean/src/api/mod.rs +++ b/lib/ain-ocean/src/api/mod.rs @@ -75,19 +75,7 @@ async fn cors(request: Request, next: Next) -> Response { .append("Access-Control-Allow-Origin", HeaderValue::from_static("*")); response.headers_mut().append( "Access-Control-Allow-Methods", - HeaderValue::from_static("GET"), - ); - response.headers_mut().append( - "Access-Control-Allow-Methods", - HeaderValue::from_static("POST"), - ); - response.headers_mut().append( - "Access-Control-Allow-Methods", - HeaderValue::from_static("PUT"), - ); - response.headers_mut().append( - "Access-Control-Allow-Methods", - HeaderValue::from_static("DELETE"), + HeaderValue::from_static("GET,POST,PUT,DELETE"), ); response.headers_mut().append( "Access-Control-Allow-Headers", diff --git a/lib/ain-ocean/src/api/oracle.rs b/lib/ain-ocean/src/api/oracle.rs index e0ed721606..7a7a94f33f 100644 --- a/lib/ain-ocean/src/api/oracle.rs +++ b/lib/ain-ocean/src/api/oracle.rs @@ -107,17 +107,17 @@ async fn get_feed( let mut oracle_price_feeds = Vec::new(); - for ((token, currency, oracle_id, _), feed) in &price_feed_list { + for ((token, currency, oracle_id, txid), feed) in &price_feed_list { if key.0.eq(token) && key.1.eq(currency) && key.2.eq(oracle_id) { let amount = Decimal::from(feed.amount) / Decimal::from(COIN); oracle_price_feeds.push(OraclePriceFeedResponse { - id: format!("{}-{}-{}-{}", token, currency, oracle_id, feed.txid), + id: format!("{}-{}-{}-{}", token, currency, oracle_id, txid), key: format!("{}-{}-{}", token, currency, oracle_id), - sort: hex::encode(feed.block.height.to_string() + &feed.txid.to_string()), + sort: hex::encode(feed.block.height.to_string() + &txid.to_string()), token: token.to_owned(), currency: currency.to_owned(), oracle_id: oracle_id.to_owned(), - txid: feed.txid, + txid: *txid, time: feed.time, amount: amount.normalize().to_string(), block: feed.block.clone(), diff --git a/lib/ain-ocean/src/api/prices.rs b/lib/ain-ocean/src/api/prices.rs index 8b17022992..11fc582d30 100644 --- a/lib/ain-ocean/src/api/prices.rs +++ b/lib/ain-ocean/src/api/prices.rs @@ -22,9 +22,9 @@ use super::{ AppContext, }; use crate::{ - error::{ApiError, Error, OtherSnafu}, + error::{ApiError, OtherSnafu}, model::{ - BlockContext, OracleIntervalSeconds, OraclePriceActive, + BlockContext, OracleIntervalSeconds, OraclePriceActive, OraclePriceActiveNext, OraclePriceAggregatedIntervalAggregated, PriceTicker, }, storage::{RepositoryOps, SortOrder}, @@ -233,29 +233,58 @@ async fn get_feed( )) } +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct OraclePriceActiveResponse { + pub id: String, // token-currency-height + pub key: String, // token-currency + pub sort: String, // height + pub active: Option, + pub next: Option, + pub is_live: bool, + pub block: BlockContext, +} + +impl OraclePriceActiveResponse { + fn from_with_id(token: &String, currency: &String, v: OraclePriceActive) -> Self { + Self { + id: format!("{}-{}-{}", token, currency, v.block.height), + key: format!("{}-{}", token, currency), + sort: hex::encode(v.block.height.to_be_bytes()).to_string(), + active: v.active, + next: v.next, + is_live: v.is_live, + block: v.block, + } + } +} + #[ocean_endpoint] async fn get_feed_active( Path(key): Path, Query(query): Query, Extension(ctx): Extension>, -) -> Result> { +) -> Result> { let (token, currency) = parse_token_currency(&key)?; - let key = (token, currency); - let repo = &ctx.services.oracle_price_active; + let id = (token.clone(), currency.clone(), u32::MAX); let price_active = ctx .services .oracle_price_active - .by_key - .list(Some(key), SortOrder::Descending)? + .by_id + .list(Some(id), SortOrder::Descending)? + .take_while(|item| match item { + Ok(((t, c, _), _)) => t == &token && c == ¤cy, + _ => true, + }) .take(query.size) - .flat_map(|item| { - let (_, id) = item?; - let item = repo.by_id.get(&id)?; - Ok::, Error>(item) + .map(|item| { + let ((token, currency, _), v) = item?; + Ok(OraclePriceActiveResponse::from_with_id( + &token, ¤cy, v, + )) }) - .flatten() - .collect::>(); + .collect::>>()?; Ok(ApiPagedResponse::of(price_active, query.size, |price| { price.sort.to_string() @@ -265,9 +294,9 @@ async fn get_feed_active( #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct OraclePriceAggregatedIntervalResponse { - pub id: String, - pub key: String, - pub sort: String, + pub id: String, // token-currency-interval-height + pub key: String, // token-currency-interval + pub sort: String, // medianTime-height pub token: Token, pub currency: Currency, pub aggregated: OraclePriceAggregatedIntervalAggregated, @@ -304,34 +333,42 @@ async fn get_feed_with_interval( 86400 => OracleIntervalSeconds::OneDay, _ => return Err(From::from("Invalid oracle interval")), }; - let key = (token, currency, interval_type); - let repo = &ctx.services.oracle_price_aggregated_interval; + let id = ( + token.clone(), + currency.clone(), + interval_type.clone(), + u32::MAX, + ); - let keys = repo - .by_key - .list(Some(key), SortOrder::Descending)? + let items = ctx + .services + .oracle_price_aggregated_interval + .by_id + .list(Some(id), SortOrder::Descending)? .take(query.size) + .take_while(|item| match item { + Ok(((t, c, i, _), _)) => { + t == &token.clone() && c == ¤cy.clone() && i == &interval_type.clone() + } + _ => true, + }) .flatten() .collect::>(); let mut prices = Vec::new(); - for ((token, currency, _), id) in keys { - let item = repo.by_id.get(&id)?; - - let Some(item) = item else { continue }; - + for (id, item) in items { let start = item.block.median_time - (item.block.median_time % interval); let price = OraclePriceAggregatedIntervalResponse { - id: format!("{}-{}-{:?}", id.0, id.1, id.2), - key: format!("{}-{}", id.0, id.1), + id: format!("{}-{}-{:?}-{}", id.0, id.1, id.2, id.3), + key: format!("{}-{}-{:?}", id.0, id.1, id.2), sort: format!( "{}{}", hex::encode(item.block.median_time.to_be_bytes()), hex::encode(item.block.height.to_be_bytes()), ), - token, - currency, + token: token.clone(), + currency: currency.clone(), aggregated: OraclePriceAggregatedIntervalAggregated { amount: item.aggregated.amount, weightage: item.aggregated.weightage, @@ -408,7 +445,6 @@ async fn list_price_oracles( )), SortOrder::Descending, )? - // .take(1) .take_while(|item| match item { Ok((k, _)) => k.0 == token && k.1 == currency && k.2 == oracle_id, _ => true, @@ -432,11 +468,11 @@ async fn list_price_oracles( OraclePriceFeedResponse { id: format!("{}-{}-{}-{}", token, currency, oracle_id, txid), key: format!("{}-{}-{}", token, currency, oracle_id), - sort: hex::encode(f.block.height.to_string() + &f.txid.to_string()), + sort: hex::encode(f.block.height.to_string() + &txid.to_string()), token: token.clone(), currency: currency.clone(), oracle_id, - txid: f.txid, + txid, time: f.time, amount: f.amount.to_string(), block: f.block, diff --git a/lib/ain-ocean/src/api/rawtx.rs b/lib/ain-ocean/src/api/rawtx.rs index 002f2f9538..6b2f9017de 100644 --- a/lib/ain-ocean/src/api/rawtx.rs +++ b/lib/ain-ocean/src/api/rawtx.rs @@ -1,6 +1,6 @@ use std::{result::Result as StdResult, str::FromStr, sync::Arc}; -use ain_dftx::{deserialize, DfTx, COIN}; +use ain_dftx::{deserialize, DfTx, Stack, COIN}; use ain_macros::ocean_endpoint; use axum::{ extract::{Json, Path}, @@ -9,7 +9,6 @@ use axum::{ }; use bitcoin::{Transaction, Txid}; use defichain_rpc::{PoolPairRPC, RpcApi}; -use log::trace; use rust_decimal::prelude::ToPrimitive; use serde::{Deserialize, Serialize, Serializer}; use snafu::location; @@ -36,7 +35,8 @@ async fn send_raw_tx( Extension(ctx): Extension>, Json(raw_tx_dto): Json, ) -> Result { - validate(ctx.clone(), raw_tx_dto.hex.clone()).await?; + validate_composite_swap_tx(&ctx, &raw_tx_dto.hex).await?; + let max_fee = match raw_tx_dto.max_fee_rate { Some(fee_rate) => { let fee_in_satoshis = fee_rate.checked_mul(COIN.into()); @@ -162,54 +162,57 @@ async fn get_raw_tx( } } -async fn validate(ctx: Arc, hex: String) -> Result<()> { +async fn validate_composite_swap_tx(ctx: &Arc, hex: &String) -> Result<()> { if !hex.starts_with("040000000001") { return Ok(()); } let data = hex::decode(hex)?; - let trx = deserialize::(&data)?; - let bytes = trx.output[0].clone().script_pubkey.into_bytes(); - let tx: Option = if bytes.len() > 2 && bytes[0] == 0x6a && bytes[1] <= 0x4e { - let offset = 1 + match bytes[1] { - 0x4c => 2, - 0x4d => 3, - 0x4e => 4, - _ => 1, - }; + let tx = deserialize::(&data)?; - let raw_tx = &bytes[offset..]; - Some(deserialize::(raw_tx)?) - } else { + let bytes = tx.output[0].script_pubkey.as_bytes(); + if bytes.len() <= 6 || bytes[0] != 0x6a || bytes[1] > 0x4e { return Ok(()); + } + + let offset = 1 + match bytes[1] { + 0x4c => 2, + 0x4d => 3, + 0x4e => 4, + _ => 1, }; + let raw_tx = &bytes[offset..]; + let dftx = match deserialize::(raw_tx) { + Err(bitcoin::consensus::encode::Error::ParseFailed("Invalid marker")) => None, + Err(e) => return Err(e.into()), + Ok(Stack { dftx, .. }) => Some(dftx), + }; + + let Some(dftx) = dftx else { return Ok(()) }; + + if let DfTx::CompositeSwap(swap) = dftx { + let Some(last_pool_id) = swap.pools.iter().last() else { + return Ok(()); + }; - if let Some(tx) = tx { - if let DfTx::CompositeSwap(composite_swap) = tx { - if composite_swap.pools.as_ref().is_empty() { + let pool_pair = ctx + .client + .get_pool_pair(last_pool_id.to_string(), Some(true)) + .await?; + + let to_token_id = swap.pool_swap.to_token_id.0.to_string(); + + for (_, info) in pool_pair.0 { + if info.id_token_a == to_token_id || info.id_token_b == to_token_id { 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) - { - trace!("Found a match: {pool_pair_info:?}"); - } - } - Ok(()) - } else { - Err(Error::BadRequest { - msg: "Transaction is not a composite swap".to_string(), - }) } - } else { - Ok(()) - } + + return Err(Error::BadRequest { + msg: "Transaction is not a composite swap".to_string(), + }); + }; + + Ok(()) } pub fn router(ctx: Arc) -> Router { diff --git a/lib/ain-ocean/src/error.rs b/lib/ain-ocean/src/error.rs index 907d9dd6fc..2e32aca456 100644 --- a/lib/ain-ocean/src/error.rs +++ b/lib/ain-ocean/src/error.rs @@ -192,7 +192,11 @@ pub enum Error { #[snafu(implicit)] location: Location, }, - SecondaryIndex, + #[snafu(display("Secondary index error"))] + SecondaryIndex { + #[snafu(implicit)] + location: Location, + }, BadRequest { msg: String, }, @@ -226,6 +230,7 @@ pub enum Error { #[snafu(implicit)] location: Location, }, + ToArrayError, #[snafu(display("{}", msg))] Other { msg: String, diff --git a/lib/ain-ocean/src/indexer/auction.rs b/lib/ain-ocean/src/indexer/auction.rs index 0cc7cfb88f..6aaf2d5225 100644 --- a/lib/ain-ocean/src/indexer/auction.rs +++ b/lib/ain-ocean/src/indexer/auction.rs @@ -16,11 +16,6 @@ impl Index for PlaceAuctionBid { trace!("[PlaceAuctionBid] Indexing..."); let auction = VaultAuctionBatchHistory { - id: format!("{}-{}-{}", self.vault_id, self.index, ctx.tx.txid), - key: format!("{}-{}", self.vault_id, self.index), - sort: format!("{}-{}", ctx.block.height, ctx.tx_idx), - vault_id: self.vault_id, - index: ctx.tx_idx, from: self.from, amount: self.token_amount.amount, token_id: self.token_amount.token.0, @@ -28,25 +23,22 @@ impl Index for PlaceAuctionBid { }; trace!("auction : {:?}", auction); - let key = (self.vault_id, self.index, ctx.tx.txid); - services.auction.by_id.put(&key, &auction)?; - services.auction.by_height.put( - &(self.vault_id, self.index, ctx.block.height, ctx.tx_idx), - &key, - ) + let id = ( + self.vault_id, + self.index.to_be_bytes(), + ctx.block.height.to_be_bytes(), + ctx.tx.txid, + ); + services.auction.by_id.put(&id, &auction) } fn invalidate(&self, services: &Arc, ctx: &Context) -> Result<()> { trace!("[PlaceAuctionBid] Invalidating..."); - services - .auction - .by_id - .delete(&(self.vault_id, self.index, ctx.tx.txid))?; - services.auction.by_height.delete(&( + services.auction.by_id.delete(&( self.vault_id, - self.index, - ctx.block.height, - ctx.tx_idx, + self.index.to_be_bytes(), + ctx.block.height.to_be_bytes(), + ctx.tx.txid, )) } } diff --git a/lib/ain-ocean/src/indexer/loan_token.rs b/lib/ain-ocean/src/indexer/loan_token.rs index c4eee9d338..4c6dafe2bf 100644 --- a/lib/ain-ocean/src/indexer/loan_token.rs +++ b/lib/ain-ocean/src/indexer/loan_token.rs @@ -1,12 +1,12 @@ use std::{str::FromStr, sync::Arc}; -use ain_dftx::loans::SetLoanToken; +use ain_dftx::{loans::SetLoanToken, Currency, Token}; use log::trace; use rust_decimal::{prelude::Zero, Decimal}; use rust_decimal_macros::dec; use crate::{ - indexer::{Context, Index, Result}, + indexer::{Context, Index, IndexBlockEnd, Result}, model::{BlockContext, OraclePriceActive, OraclePriceActiveNext, OraclePriceAggregated}, network::Network, storage::{RepositoryOps, SortOrder}, @@ -32,6 +32,16 @@ impl Index for SetLoanToken { } } +impl IndexBlockEnd for SetLoanToken { + fn index_block_end(self, services: &Arc, block: &BlockContext) -> Result<()> { + index_active_price(services, block) + } + + fn invalidate_block_end(self, services: &Arc, block: &BlockContext) -> Result<()> { + invalidate_active_price(services, block) + } +} + fn is_aggregate_valid(aggregate: &OraclePriceAggregated, block: &BlockContext) -> bool { if (aggregate.block.time - block.time).abs() >= 3600 { return false; @@ -42,7 +52,7 @@ fn is_aggregate_valid(aggregate: &OraclePriceAggregated, block: &BlockContext) - return false; } - if aggregate.aggregated.weightage == dec!(0) { + if aggregate.aggregated.weightage <= dec!(0) { return false; } @@ -93,8 +103,8 @@ pub fn index_active_price(services: &Arc, block: &BlockContext) -> Res .flatten() .collect::>(); - for pt in price_tickers { - perform_active_price_tick(services, pt.0, block)?; + for (ticker_id, _) in price_tickers { + perform_active_price_tick(services, ticker_id, block)?; } } Ok(()) @@ -102,9 +112,8 @@ pub fn index_active_price(services: &Arc, block: &BlockContext) -> Res fn map_active_price( block: &BlockContext, - ticker_id: (String, String), aggregated_price: OraclePriceAggregated, - prev_price: OraclePriceActive, + prev_price: Option, ) -> OraclePriceActive { let next_price = if is_aggregate_valid(&aggregated_price, block) { Some(aggregated_price.aggregated) @@ -112,16 +121,17 @@ fn map_active_price( None }; - let active_price = if let Some(next) = prev_price.next { - Some(next) + let active_price = if let Some(prev_price) = prev_price { + if let Some(next) = prev_price.next { + Some(next) + } else { + prev_price.active + } } else { - prev_price.active + None }; OraclePriceActive { - id: (ticker_id.0.clone(), ticker_id.1.clone(), block.height), - key: ticker_id, - sort: hex::encode(block.height.to_be_bytes()), active: active_price.clone(), next: next_price.clone(), is_live: is_live(active_price, next_price), @@ -143,9 +153,7 @@ pub fn invalidate_active_price(services: &Arc, block: &BlockContext) - .flatten() .collect::>(); - for pt in price_tickers { - let token = pt.0 .0; - let currency = pt.0 .1; + for ((token, currency), _) in price_tickers { services .oracle_price_active .by_id @@ -158,58 +166,38 @@ pub fn invalidate_active_price(services: &Arc, block: &BlockContext) - pub fn perform_active_price_tick( services: &Arc, - ticker_id: (String, String), + ticker_id: (Token, Currency), block: &BlockContext, ) -> Result<()> { - let repo = &services.oracle_price_aggregated; - let prev_keys = repo - .by_key - .list(Some(ticker_id.clone()), SortOrder::Descending)? - .take(1) - .flatten() // return empty vec if none - .collect::>(); - - if prev_keys.is_empty() { - return Ok(()); - } - - let Some((_, prev_id)) = prev_keys.first() else { - return Ok(()); - }; + let id = (ticker_id.0, ticker_id.1, u32::MAX); - let aggregated_price = repo.by_id.get(prev_id)?; + let prev = services + .oracle_price_aggregated + .by_id + .list(Some(id.clone()), SortOrder::Descending)? + .next() + .transpose()?; - let Some(aggregated_price) = aggregated_price else { + let Some((_, aggregated_price)) = prev else { return Ok(()); }; let repo = &services.oracle_price_active; - let prev_keys = repo - .by_key - .list(Some(ticker_id.clone()), SortOrder::Descending)? - .take(1) - .flatten() - .collect::>(); - - if prev_keys.is_empty() { - return Ok(()); - } - - let Some((_, prev_id)) = prev_keys.first() else { - return Ok(()); - }; - - let prev_price = repo.by_id.get(prev_id)?; - - let Some(prev_price) = prev_price else { - return Ok(()); + let prev = repo + .by_id + .list(Some(id.clone()), SortOrder::Descending)? + .next() + .transpose()?; + + let prev_price = if let Some((_, prev)) = prev { + Some(prev) + } else { + None }; - let active_price = map_active_price(block, ticker_id, aggregated_price, prev_price); - - repo.by_id.put(&active_price.id, &active_price)?; + let active_price = map_active_price(block, aggregated_price, prev_price); - repo.by_key.put(&active_price.key, &active_price.id)?; + repo.by_id.put(&(id.0, id.1, block.height), &active_price)?; Ok(()) } diff --git a/lib/ain-ocean/src/indexer/mod.rs b/lib/ain-ocean/src/indexer/mod.rs index b991f22c5e..050033d0bd 100644 --- a/lib/ain-ocean/src/indexer/mod.rs +++ b/lib/ain-ocean/src/indexer/mod.rs @@ -18,31 +18,41 @@ use ain_dftx::{deserialize, is_skipped_tx, DfTx, Stack}; use defichain_rpc::json::blockchain::{Block, Transaction, Vin, VinStandard, Vout}; use helper::check_if_evm_tx; use log::trace; -pub use poolswap::{PoolSwapAggregatedInterval, AGGREGATED_INTERVALS}; +pub use poolswap::PoolSwapAggregatedInterval; use crate::{ error::{Error, IndexAction}, hex_encoder::as_sha256, index_transaction, invalidate_transaction, model::{ - Block as BlockMapper, BlockContext, PoolSwapAggregated, PoolSwapAggregatedAggregated, - ScriptActivity, ScriptActivityScript, ScriptActivityType, ScriptActivityTypeHex, - ScriptActivityVin, ScriptActivityVout, ScriptAggregation, ScriptAggregationAmount, - ScriptAggregationScript, ScriptAggregationStatistic, ScriptUnspent, ScriptUnspentScript, - ScriptUnspentVout, TransactionVout, TransactionVoutScript, + Block as BlockMapper, BlockContext, ScriptActivity, ScriptActivityScript, + ScriptActivityType, ScriptActivityTypeHex, ScriptActivityVin, ScriptActivityVout, + ScriptAggregation, ScriptAggregationAmount, ScriptAggregationScript, + ScriptAggregationStatistic, ScriptUnspent, ScriptUnspentScript, ScriptUnspentVout, + TransactionVout, TransactionVoutScript, }, - storage::{RepositoryOps, SecondaryIndex, SortOrder}, + storage::{RepositoryOps, SortOrder}, Result, Services, }; pub trait Index { fn index(self, services: &Arc, ctx: &Context) -> Result<()>; - // TODO: allow dead_code at the moment - #[allow(dead_code)] fn invalidate(&self, services: &Arc, ctx: &Context) -> Result<()>; } +pub trait IndexBlockStart: Index { + fn index_block_start(self, services: &Arc, block: &BlockContext) -> Result<()>; + + fn invalidate_block_start(self, services: &Arc, block: &BlockContext) -> Result<()>; +} + +pub trait IndexBlockEnd: Index { + fn index_block_end(self, services: &Arc, block: &BlockContext) -> Result<()>; + + fn invalidate_block_end(self, services: &Arc, block: &BlockContext) -> Result<()>; +} + #[derive(Debug)] pub struct Context { block: BlockContext, @@ -55,83 +65,6 @@ fn log_elapsed + std::fmt::Display>(previous: Instant, msg: S) { trace!("{} in {} ms", msg, now.duration_since(previous).as_millis()); } -fn get_bucket(block: &Block, interval: i64) -> i64 { - block.mediantime - (block.mediantime % interval) -} - -fn index_block_start(services: &Arc, block: &Block) -> Result<()> { - let mut pool_pairs = ain_cpp_imports::get_pool_pairs(); - pool_pairs.sort_by(|a, b| b.creation_height.cmp(&a.creation_height)); - - for interval in AGGREGATED_INTERVALS { - for pool_pair in &pool_pairs { - let repository = &services.pool_swap_aggregated; - - let prevs = repository - .by_key - .list( - Some((pool_pair.id, interval, i64::MAX)), - SortOrder::Descending, - )? - .take(1) - .take_while(|item| match item { - Ok((k, _)) => k.0 == pool_pair.id && k.1 == interval, - _ => true, - }) - .map(|e| repository.by_key.retrieve_primary_value(e)) - .collect::>>()?; - - let bucket = get_bucket(block, i64::from(interval)); - - if prevs.len() == 1 && prevs[0].bucket >= bucket { - break; - } - - let aggregated = PoolSwapAggregated { - bucket, - aggregated: PoolSwapAggregatedAggregated { - amounts: Default::default(), - }, - block: BlockContext { - hash: block.hash, - height: block.height, - time: block.time, - median_time: block.mediantime, - }, - }; - - let pool_swap_aggregated_key = (pool_pair.id, interval, bucket); - let pool_swap_aggregated_id = (pool_pair.id, interval, block.hash); - - repository - .by_key - .put(&pool_swap_aggregated_key, &pool_swap_aggregated_id)?; - repository - .by_id - .put(&pool_swap_aggregated_id, &aggregated)?; - } - } - - Ok(()) -} - -fn invalidate_block_start(services: &Arc, block: &Block) -> Result<()> { - let mut pool_pairs = ain_cpp_imports::get_pool_pairs(); - pool_pairs.sort_by(|a, b| b.creation_height.cmp(&a.creation_height)); - - for interval in AGGREGATED_INTERVALS { - for pool_pair in &pool_pairs { - let pool_swap_aggregated_id = (pool_pair.id, interval, block.hash); - services - .pool_swap_aggregated - .by_id - .delete(&pool_swap_aggregated_id)?; - } - } - - Ok(()) -} - fn get_vin_standard(vin: &Vin) -> Option { match vin { Vin::Coinbase(_vin) => None, @@ -237,7 +170,7 @@ fn index_script_unspent_vin( vin: &VinStandard, ctx: &Context, ) -> Result<()> { - let key = (ctx.block.height, vin.txid, vin.vout); + let key = (ctx.block.height.to_be_bytes(), vin.txid, vin.vout); let id = services.script_unspent.by_key.get(&key)?; if let Some(id) = id { services.script_unspent.by_id.delete(&id)?; @@ -333,7 +266,7 @@ fn index_script_unspent_vout(services: &Arc, vout: &Vout, ctx: &Contex }; let id = (hid, block.height.to_be_bytes(), tx.txid, vout.n); - let key = (block.height, tx.txid, vout.n); + let key = (block.height.to_be_bytes(), tx.txid, vout.n); services.script_unspent.by_key.put(&key, &id)?; services.script_unspent.by_id.put(&id, &script_unspent)?; Ok(()) @@ -537,7 +470,11 @@ fn invalidate_script_unspent_vin( transaction.txid, vout.n, ); - let key = (transaction.block.height, transaction.txid, vout.n); + let key = ( + transaction.block.height.to_be_bytes(), + transaction.txid, + vout.n, + ); services.script_unspent.by_key.put(&key, &id)?; services.script_unspent.by_id.put(&id, &script_unspent)?; @@ -570,7 +507,9 @@ fn invalidate_script_unspent_vout( ) -> Result<()> { let hid = as_sha256(&vout.script_pub_key.hex); let id = (hid, ctx.block.height.to_be_bytes(), ctx.tx.txid, vout.n); + let key = (ctx.block.height.to_be_bytes(), ctx.tx.txid, vout.n); services.script_unspent.by_id.delete(&id)?; + services.script_unspent.by_key.delete(&key)?; Ok(()) } @@ -591,16 +530,6 @@ fn invalidate_script_activity_vout( Ok(()) } -fn index_block_end(services: &Arc, block: &BlockContext) -> Result<()> { - loan_token::index_active_price(services, block)?; - Ok(()) -} - -fn invalidate_block_end(services: &Arc, block: &BlockContext) -> Result<()> { - loan_token::invalidate_active_price(services, block)?; - Ok(()) -} - pub fn index_block(services: &Arc, block: Block) -> Result<()> { trace!("[index_block] Indexing block..."); let start = Instant::now(); @@ -613,13 +542,13 @@ pub fn index_block(services: &Arc, block: Block) -> Resul median_time: block.mediantime, }; - index_block_start(services, &block)?; + let mut dftxs = Vec::new(); for (tx_idx, tx) in block.tx.clone().into_iter().enumerate() { if is_skipped_tx(&tx.txid) { continue; } - let start = Instant::now(); + let ctx = Context { block: block_ctx.clone(), tx, @@ -645,23 +574,42 @@ pub fn index_block(services: &Arc, block: Block) -> Resul match deserialize::(raw_tx) { Err(bitcoin::consensus::encode::Error::ParseFailed("Invalid marker")) => (), Err(e) => return Err(e.into()), - Ok(Stack { dftx, .. }) => { - match dftx { - DfTx::CreateMasternode(data) => data.index(services, &ctx)?, - DfTx::UpdateMasternode(data) => data.index(services, &ctx)?, - DfTx::ResignMasternode(data) => data.index(services, &ctx)?, - DfTx::AppointOracle(data) => data.index(services, &ctx)?, - DfTx::RemoveOracle(data) => data.index(services, &ctx)?, - DfTx::UpdateOracle(data) => data.index(services, &ctx)?, - DfTx::SetOracleData(data) => data.index(services, &ctx)?, - DfTx::PoolSwap(data) => data.index(services, &ctx)?, - DfTx::SetLoanToken(data) => data.index(services, &ctx)?, - DfTx::CompositeSwap(data) => data.index(services, &ctx)?, - DfTx::PlaceAuctionBid(data) => data.index(services, &ctx)?, - _ => (), - } - log_elapsed(start, "Indexed dftx"); - } + Ok(Stack { dftx, .. }) => dftxs.push((dftx, ctx)), + } + } + + // index_block_start + for (dftx, _) in &dftxs { + if let DfTx::PoolSwap(data) = dftx.clone() { + data.index_block_start(services, &block_ctx)? + } + } + + // index_dftx + for (dftx, ctx) in &dftxs { + let start = Instant::now(); + + match dftx.clone() { + DfTx::CreateMasternode(data) => data.index(services, ctx)?, + DfTx::UpdateMasternode(data) => data.index(services, ctx)?, + DfTx::ResignMasternode(data) => data.index(services, ctx)?, + DfTx::AppointOracle(data) => data.index(services, ctx)?, + DfTx::RemoveOracle(data) => data.index(services, ctx)?, + DfTx::UpdateOracle(data) => data.index(services, ctx)?, + DfTx::SetOracleData(data) => data.index(services, ctx)?, + DfTx::PoolSwap(data) => data.index(services, ctx)?, + DfTx::SetLoanToken(data) => data.index(services, ctx)?, + DfTx::CompositeSwap(data) => data.index(services, ctx)?, + DfTx::PlaceAuctionBid(data) => data.index(services, ctx)?, + _ => (), + } + log_elapsed(start, "Indexed dftx"); + } + + // index_block_end + for (dftx, _) in dftxs { + if let DfTx::SetLoanToken(data) = dftx { + data.index_block_end(services, &block_ctx)? } } @@ -685,8 +633,6 @@ pub fn index_block(services: &Arc, block: Block) -> Resul weight: block.weight, }; - index_block_end(services, &block_ctx)?; - // services.block.raw.put(&ctx.hash, &encoded_block)?; TODO services.block.by_id.put(&block_ctx.hash, &block_mapper)?; services @@ -707,14 +653,12 @@ pub fn invalidate_block(services: &Arc, block: Block) -> median_time: block.mediantime, }; - invalidate_block_end(services, &block_ctx)?; + let mut dftxs = Vec::new(); - // invalidate_dftx for (tx_idx, tx) in block.tx.clone().into_iter().enumerate() { if is_skipped_tx(&tx.txid) { continue; } - let start = Instant::now(); let ctx = Context { block: block_ctx.clone(), tx, @@ -742,27 +686,43 @@ pub fn invalidate_block(services: &Arc, block: Block) -> println!("Discarding invalid marker"); } Err(e) => return Err(e.into()), - Ok(Stack { dftx, .. }) => { - match dftx { - DfTx::CreateMasternode(data) => data.invalidate(services, &ctx)?, - DfTx::UpdateMasternode(data) => data.invalidate(services, &ctx)?, - DfTx::ResignMasternode(data) => data.invalidate(services, &ctx)?, - DfTx::AppointOracle(data) => data.invalidate(services, &ctx)?, - DfTx::RemoveOracle(data) => data.invalidate(services, &ctx)?, // check - DfTx::UpdateOracle(data) => data.invalidate(services, &ctx)?, // check - DfTx::SetOracleData(data) => data.invalidate(services, &ctx)?, - DfTx::PoolSwap(data) => data.invalidate(services, &ctx)?, // check - DfTx::SetLoanToken(data) => data.invalidate(services, &ctx)?, - DfTx::CompositeSwap(data) => data.invalidate(services, &ctx)?, - DfTx::PlaceAuctionBid(data) => data.invalidate(services, &ctx)?, - _ => (), - } - log_elapsed(start, "Invalidate dftx"); - } + Ok(Stack { dftx, .. }) => dftxs.push((dftx, ctx)), + } + } + + // invalidate_block_end + for (dftx, _) in &dftxs { + if let DfTx::SetLoanToken(data) = dftx.clone() { + data.invalidate_block_end(services, &block_ctx)? + } + } + + // invalidate_dftx + for (dftx, ctx) in &dftxs { + let start = Instant::now(); + match dftx { + DfTx::CreateMasternode(data) => data.invalidate(services, ctx)?, + DfTx::UpdateMasternode(data) => data.invalidate(services, ctx)?, + DfTx::ResignMasternode(data) => data.invalidate(services, ctx)?, + DfTx::AppointOracle(data) => data.invalidate(services, ctx)?, + DfTx::RemoveOracle(data) => data.invalidate(services, ctx)?, + DfTx::UpdateOracle(data) => data.invalidate(services, ctx)?, + DfTx::SetOracleData(data) => data.invalidate(services, ctx)?, + DfTx::PoolSwap(data) => data.invalidate(services, ctx)?, + DfTx::SetLoanToken(data) => data.invalidate(services, ctx)?, + DfTx::CompositeSwap(data) => data.invalidate(services, ctx)?, + DfTx::PlaceAuctionBid(data) => data.invalidate(services, ctx)?, + _ => (), } + log_elapsed(start, "Invalidate dftx"); } - invalidate_block_start(services, &block)?; + // invalidate_block_start + for (dftx, _) in &dftxs { + if let DfTx::PoolSwap(data) = dftx.clone() { + data.invalidate_block_start(services, &block_ctx)? + } + } // invalidate_block services.block.by_height.delete(&block.height)?; diff --git a/lib/ain-ocean/src/indexer/oracle.rs b/lib/ain-ocean/src/indexer/oracle.rs index 931bf38a28..1e65935dfc 100644 --- a/lib/ain-ocean/src/indexer/oracle.rs +++ b/lib/ain-ocean/src/indexer/oracle.rs @@ -12,8 +12,7 @@ use snafu::OptionExt; use crate::{ error::{ - ArithmeticOverflowSnafu, ArithmeticUnderflowSnafu, Error, IndexAction, OtherSnafu, - ToPrimitiveSnafu, + ArithmeticOverflowSnafu, ArithmeticUnderflowSnafu, Error, IndexAction, ToPrimitiveSnafu, }, indexer::{Context, Index, Result}, model::{ @@ -248,11 +247,10 @@ impl Index for UpdateOracle { fn map_price_aggregated( services: &Arc, context: &Context, - pair: &(String, String), + pair: &(Token, Currency), ) -> Result> { let (token, currency) = pair; let oracle_repo = &services.oracle_token_currency; - let feed_repo = &services.oracle_price_feed; let oracles = oracle_repo .by_id @@ -282,13 +280,17 @@ fn map_price_aggregated( continue; } - let feed_id = feed_repo.by_key.get(&(id))?; - - let Some(feed_id) = feed_id else { continue }; - - let feed = feed_repo.by_id.get(&feed_id)?; + let feed = services + .oracle_price_feed + .by_id + .list( + Some((id.0, id.1, id.2, Txid::from_byte_array([0xffu8; 32]))), + SortOrder::Descending, + )? + .next() + .transpose()?; - let Some(feed) = feed else { continue }; + let Some((_, feed)) = feed else { continue }; let time_diff = Decimal::from(feed.time) - Decimal::from(context.block.time); if Decimal::abs(&time_diff) < dec!(3600) { @@ -306,7 +308,9 @@ fn map_price_aggregated( let weighted_amount = Decimal::from(feed.amount) .checked_mul(Decimal::from(oracle.weightage)) .context(ArithmeticOverflowSnafu)?; - aggregated_total += weighted_amount; + aggregated_total = aggregated_total + .checked_add(weighted_amount) + .context(ArithmeticOverflowSnafu)?; } } @@ -355,7 +359,6 @@ fn index_set_oracle_data( currency.clone(), price_aggregated.block.height, ); - oracle_repo.by_key.put(pair, &id)?; oracle_repo.by_id.put(&id, &price_aggregated)?; let key = ( @@ -408,18 +411,13 @@ fn index_set_oracle_data_interval( impl Index for SetOracleData { fn index(self, services: &Arc, context: &Context) -> Result<()> { - let feed_repo = &services.oracle_price_feed; - let mut pairs = HashSet::new(); let feeds = map_price_feeds(&self, context); for (id, feed) in &feeds { let token = id.0.clone(); let currency = id.1.clone(); - let oracle_id = id.2; - let key = (token.clone(), currency.clone(), oracle_id); pairs.insert((token, currency)); - feed_repo.by_key.put(&key, id)?; - feed_repo.by_id.put(id, feed)?; + services.oracle_price_feed.by_id.put(id, feed)?; } index_set_oracle_data(services, context, &pairs)?; @@ -479,7 +477,6 @@ fn map_price_feeds( amount: token_amount.amount, block: ctx.block.clone(), time: data.timestamp as i32, - txid: ctx.tx.txid, }; feeds.push((id, oracle_price_feed)); } @@ -495,10 +492,8 @@ fn start_new_bucket( aggregated: &OraclePriceAggregated, interval: OracleIntervalSeconds, ) -> Result<()> { - let key = (token.clone(), currency.clone(), interval.clone()); let id = (token, currency, interval, block.height); - let repo = &services.oracle_price_aggregated_interval; - repo.by_id.put( + services.oracle_price_aggregated_interval.by_id.put( &id, &OraclePriceAggregatedInterval { aggregated: OraclePriceAggregatedIntervalAggregated { @@ -513,7 +508,6 @@ fn start_new_bucket( block: block.clone(), }, )?; - repo.by_key.put(&key, &id)?; Ok(()) } @@ -528,30 +522,30 @@ pub fn index_interval_mapper( ) -> Result<()> { let repo = &services.oracle_price_aggregated_interval; let previous = repo - .by_key + .by_id .list( - Some((token.clone(), currency.clone(), interval.clone())), + Some((token.clone(), currency.clone(), interval.clone(), u32::MAX)), SortOrder::Descending, )? - .take(1) - .flatten() - .collect::>(); + .take_while(|item| match item { + Ok(((t, c, i, _), _)) => { + t == &token.clone() && c == ¤cy.clone() && i == &interval.clone() + } + _ => true, + }) + .next() + .transpose()?; - if previous.is_empty() { + let Some(previous) = previous else { return start_new_bucket(services, block, token, currency, aggregated, interval); - } - - for (_, id) in previous { - let aggregated_interval = repo.by_id.get(&id)?; - if let Some(aggregated_interval) = aggregated_interval { - if block.median_time - aggregated.block.median_time > interval.clone() as i64 { - return start_new_bucket(services, block, token, currency, aggregated, interval); - } + }; - forward_aggregate(services, (id, &aggregated_interval), aggregated)?; - } + if block.median_time - aggregated.block.median_time > interval.clone() as i64 { + return start_new_bucket(services, block, token, currency, aggregated, interval); } + forward_aggregate(services, previous, aggregated)?; + Ok(()) } @@ -565,29 +559,29 @@ pub fn invalidate_oracle_interval( ) -> Result<()> { let repo = &services.oracle_price_aggregated_interval; let previous = repo - .by_key + .by_id .list( - Some((token.to_string(), currency.to_string(), interval.clone())), + Some(( + token.to_string(), + currency.to_string(), + interval.clone(), + u32::MAX, + )), SortOrder::Descending, )? - .take(1) - .map(|item| { - let (_, id) = item?; - let price = services - .oracle_price_aggregated_interval - .by_id - .get(&id)? - .context(OtherSnafu { - msg: "Missing oracle price aggregated interval index", - })?; - Ok((id, price)) - }) - .collect::>>()?; + .next() + .transpose()?; - let (prev_id, previous) = &previous[0]; + let Some((prev_id, previous)) = previous else { + return Err(Error::NotFoundIndex { + action: IndexAction::Invalidate, + r#type: "Invalidate oracle price aggregated interval".to_string(), + id: format!("{}-{}-{:?}", token, currency, interval), + }); + }; if previous.aggregated.count == 1 { - return repo.by_id.delete(prev_id); + return repo.by_id.delete(&prev_id); } let last_price = previous.aggregated.clone(); @@ -631,11 +625,7 @@ pub fn invalidate_oracle_interval( }, block: previous.block.clone(), }; - repo.by_id.put(prev_id, &aggregated_interval)?; - repo.by_key.put( - &(prev_id.0.clone(), prev_id.1.clone(), prev_id.2.clone()), - prev_id, - )?; + repo.by_id.put(&prev_id, &aggregated_interval)?; Ok(()) } @@ -643,7 +633,7 @@ fn forward_aggregate( services: &Arc, previous: ( OraclePriceAggregatedIntervalId, - &OraclePriceAggregatedInterval, + OraclePriceAggregatedInterval, ), aggregated: &OraclePriceAggregated, ) -> Result<()> { @@ -693,10 +683,6 @@ fn forward_aggregate( .oracle_price_aggregated_interval .by_id .put(&prev_id, &aggregated_interval)?; - services.oracle_price_aggregated_interval.by_key.put( - &(prev_id.0.clone(), prev_id.1.clone(), prev_id.2.clone()), - &prev_id, - )?; Ok(()) } diff --git a/lib/ain-ocean/src/indexer/poolswap.rs b/lib/ain-ocean/src/indexer/poolswap.rs index 844e28b358..e5f70f3e33 100644 --- a/lib/ain-ocean/src/indexer/poolswap.rs +++ b/lib/ain-ocean/src/indexer/poolswap.rs @@ -7,13 +7,16 @@ use rust_decimal::Decimal; use rust_decimal_macros::dec; use snafu::OptionExt; -use super::Context; +use super::{Context, IndexBlockStart}; use crate::{ error::{ArithmeticOverflowSnafu, ArithmeticUnderflowSnafu}, indexer::{tx_result, Index, Result}, - model::{self, PoolSwapResult, TxResult}, + model::{ + self, BlockContext, PoolSwapAggregated, PoolSwapAggregatedAggregated, PoolSwapResult, + TxResult, + }, storage::{RepositoryOps, SortOrder}, - Services, + PoolSwapAggregatedService, Services, }; pub const AGGREGATED_INTERVALS: [u32; 2] = [ @@ -35,7 +38,7 @@ fn index_swap_aggregated( txid: Txid, ) -> Result<()> { for interval in AGGREGATED_INTERVALS { - let repo: &crate::PoolSwapAggregatedService = &services.pool_swap_aggregated; + let repo = &services.pool_swap_aggregated; let prevs = repo .by_key .list(Some((pool_id, interval, i64::MAX)), SortOrder::Descending)? @@ -147,6 +150,99 @@ fn invalidate_swap_aggregated( Ok(()) } +fn create_new_bucket( + repo: &PoolSwapAggregatedService, + bucket: i64, + pool_pair_id: u32, + interval: u32, + block: &BlockContext, +) -> Result<()> { + let aggregated = PoolSwapAggregated { + bucket, + aggregated: PoolSwapAggregatedAggregated { + amounts: Default::default(), + }, + block: BlockContext { + hash: block.hash, + height: block.height, + time: block.time, + median_time: block.median_time, + }, + }; + + let pool_swap_aggregated_key = (pool_pair_id, interval, bucket); + let pool_swap_aggregated_id = (pool_pair_id, interval, block.hash); + + repo.by_key + .put(&pool_swap_aggregated_key, &pool_swap_aggregated_id)?; + repo.by_id.put(&pool_swap_aggregated_id, &aggregated)?; + + Ok(()) +} + +impl IndexBlockStart for PoolSwap { + fn index_block_start(self, services: &Arc, block: &BlockContext) -> Result<()> { + let mut pool_pairs = ain_cpp_imports::get_pool_pairs(); + pool_pairs.sort_by(|a, b| b.creation_height.cmp(&a.creation_height)); + + for interval in AGGREGATED_INTERVALS { + for pool_pair in &pool_pairs { + let repo = &services.pool_swap_aggregated; + + let prev = repo + .by_key + .list( + Some((pool_pair.id, interval, i64::MAX)), + SortOrder::Descending, + )? + .take_while(|item| match item { + Ok((k, _)) => k.0 == pool_pair.id && k.1 == interval, + _ => true, + }) + .next() + .transpose()?; + + let bucket = block.median_time - (block.median_time % interval as i64); + + let Some((_, prev_id)) = prev else { + create_new_bucket(repo, bucket, pool_pair.id, interval, block)?; + continue; + }; + + let Some(prev) = repo.by_id.get(&prev_id)? else { + create_new_bucket(repo, bucket, pool_pair.id, interval, block)?; + continue; + }; + + if prev.bucket >= bucket { + break; + } + + create_new_bucket(repo, bucket, pool_pair.id, interval, block)?; + } + } + + Ok(()) + } + + fn invalidate_block_start(self, services: &Arc, block: &BlockContext) -> Result<()> { + let mut pool_pairs = ain_cpp_imports::get_pool_pairs(); + pool_pairs.sort_by(|a, b| b.creation_height.cmp(&a.creation_height)); + + for interval in AGGREGATED_INTERVALS { + for pool_pair in &pool_pairs { + let pool_swap_aggregated_id = (pool_pair.id, interval, block.hash); + services + .pool_swap_aggregated + .by_id + .delete(&pool_swap_aggregated_id)?; + } + } + + Ok(()) + } +} + impl Index for PoolSwap { fn index(self, services: &Arc, ctx: &Context) -> Result<()> { trace!("[Poolswap] Indexing..."); diff --git a/lib/ain-ocean/src/lib.rs b/lib/ain-ocean/src/lib.rs index 4ab49ddccc..86ad1624a8 100644 --- a/lib/ain-ocean/src/lib.rs +++ b/lib/ain-ocean/src/lib.rs @@ -48,7 +48,6 @@ pub struct BlockService { pub struct AuctionService { by_id: VaultAuctionHistory, - by_height: VaultAuctionHistoryByHeight, } pub struct PoolService { @@ -71,19 +70,15 @@ pub struct OracleService { by_id: Oracle, } pub struct OraclePriceFeedService { - by_key: OraclePriceFeedKey, by_id: OraclePriceFeed, } pub struct OraclePriceActiveService { - by_key: OraclePriceActiveKey, by_id: OraclePriceActive, } pub struct OraclePriceAggregatedIntervalService { - by_key: OraclePriceAggregatedIntervalKey, by_id: OraclePriceAggregatedInterval, } pub struct OraclePriceAggregatedService { - by_key: OraclePriceAggregatedKey, by_id: OraclePriceAggregated, } @@ -161,7 +156,6 @@ impl Services { }, auction: AuctionService { by_id: VaultAuctionHistory::new(Arc::clone(&store)), - by_height: VaultAuctionHistoryByHeight::new(Arc::clone(&store)), }, result: TxResult::new(Arc::clone(&store)), pool: PoolService { @@ -181,19 +175,15 @@ impl Services { by_id: Oracle::new(Arc::clone(&store)), }, oracle_price_feed: OraclePriceFeedService { - by_key: OraclePriceFeedKey::new(Arc::clone(&store)), by_id: OraclePriceFeed::new(Arc::clone(&store)), }, oracle_price_active: OraclePriceActiveService { - by_key: OraclePriceActiveKey::new(Arc::clone(&store)), by_id: OraclePriceActive::new(Arc::clone(&store)), }, oracle_price_aggregated_interval: OraclePriceAggregatedIntervalService { - by_key: OraclePriceAggregatedIntervalKey::new(Arc::clone(&store)), by_id: OraclePriceAggregatedInterval::new(Arc::clone(&store)), }, oracle_price_aggregated: OraclePriceAggregatedService { - by_key: OraclePriceAggregatedKey::new(Arc::clone(&store)), by_id: OraclePriceAggregated::new(Arc::clone(&store)), }, oracle_token_currency: OracleTokenCurrencyService { diff --git a/lib/ain-ocean/src/model/oracle_price_active.rs b/lib/ain-ocean/src/model/oracle_price_active.rs index b913ea9d7c..3b9740f08e 100644 --- a/lib/ain-ocean/src/model/oracle_price_active.rs +++ b/lib/ain-ocean/src/model/oracle_price_active.rs @@ -4,13 +4,10 @@ use serde::{Deserialize, Serialize}; use super::BlockContext; pub type OraclePriceActiveId = (String, String, u32); //token-currency-height -pub type OraclePriceActiveKey = (String, String); //token-currency + #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct OraclePriceActive { - pub id: OraclePriceActiveId, - pub key: OraclePriceActiveKey, - pub sort: String, //height pub active: Option, pub next: Option, pub is_live: bool, diff --git a/lib/ain-ocean/src/model/oracle_price_aggregated.rs b/lib/ain-ocean/src/model/oracle_price_aggregated.rs index f56b26f986..0ee554ed8f 100644 --- a/lib/ain-ocean/src/model/oracle_price_aggregated.rs +++ b/lib/ain-ocean/src/model/oracle_price_aggregated.rs @@ -3,7 +3,6 @@ use serde::{Deserialize, Serialize}; use super::{BlockContext, OraclePriceActiveNext}; pub type OraclePriceAggregatedId = (String, String, u32); //token-currency-height -pub type OraclePriceAggregatedKey = (String, String); //token-currency #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] diff --git a/lib/ain-ocean/src/model/oracle_price_aggregated_interval.rs b/lib/ain-ocean/src/model/oracle_price_aggregated_interval.rs index 215c3876f7..6b5bb65d5b 100644 --- a/lib/ain-ocean/src/model/oracle_price_aggregated_interval.rs +++ b/lib/ain-ocean/src/model/oracle_price_aggregated_interval.rs @@ -4,13 +4,12 @@ use serde::{Deserialize, Serialize}; use super::BlockContext; pub type OraclePriceAggregatedIntervalId = (Token, Currency, OracleIntervalSeconds, u32); //token-currency-interval-height -pub type OraclePriceAggregatedIntervalKey = (Token, Currency, OracleIntervalSeconds); //token-currency-interval pub const FIFTEEN_MINUTES: isize = 15 * 60; pub const ONE_HOUR: isize = 60 * 60; pub const ONE_DAY: isize = 24 * 60 * 60; -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub enum OracleIntervalSeconds { FifteenMinutes = FIFTEEN_MINUTES, OneHour = ONE_HOUR, diff --git a/lib/ain-ocean/src/model/oracle_price_feed.rs b/lib/ain-ocean/src/model/oracle_price_feed.rs index 828e6bfa1b..0afcc65614 100644 --- a/lib/ain-ocean/src/model/oracle_price_feed.rs +++ b/lib/ain-ocean/src/model/oracle_price_feed.rs @@ -3,12 +3,10 @@ use serde::{Deserialize, Serialize}; use super::BlockContext; pub type OraclePriceFeedId = (String, String, Txid, Txid); // token-currency-oracle_id-txid -pub type OraclePriceFeedKey = (String, String, Txid); // token-currency-oracle_id #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct OraclePriceFeed { - pub txid: Txid, pub time: i32, pub amount: i64, pub block: BlockContext, diff --git a/lib/ain-ocean/src/model/script_unspent.rs b/lib/ain-ocean/src/model/script_unspent.rs index 3546b43895..c088751976 100644 --- a/lib/ain-ocean/src/model/script_unspent.rs +++ b/lib/ain-ocean/src/model/script_unspent.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use super::BlockContext; pub type ScriptUnspentId = ([u8; 32], [u8; 4], Txid, usize); // hid + block.height + txid + vout_index -pub type ScriptUnspentKey = (u32, Txid, usize); // block.height + txid + vout_index +pub type ScriptUnspentKey = ([u8; 4], Txid, usize); // block.height + txid + vout_index, ps: key is required in index_script_unspent_vin #[derive(Debug, Serialize, Deserialize)] pub struct ScriptUnspent { diff --git a/lib/ain-ocean/src/model/vault_auction_batch_history.rs b/lib/ain-ocean/src/model/vault_auction_batch_history.rs index 0ba11786ce..e3de09459d 100644 --- a/lib/ain-ocean/src/model/vault_auction_batch_history.rs +++ b/lib/ain-ocean/src/model/vault_auction_batch_history.rs @@ -3,17 +3,11 @@ use serde::{Deserialize, Serialize}; use super::BlockContext; -pub type AuctionHistoryKey = (Txid, u32, Txid); // (vault_id, auction_batch_index, txid) -pub type AuctionHistoryByHeightKey = (Txid, u32, u32, usize); // (vault_id, auction_batch_index, block_height, txid) +pub type AuctionHistoryKey = (Txid, [u8; 4], [u8; 4], Txid); // (vault_id, auction_batch_index, block_height, txid) #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct VaultAuctionBatchHistory { - pub id: String, - pub key: String, - pub sort: String, - pub vault_id: Txid, - pub index: usize, pub from: ScriptBuf, pub amount: i64, pub token_id: u64, diff --git a/lib/ain-ocean/src/storage/macros.rs b/lib/ain-ocean/src/storage/macros.rs index ca95ab1d29..bff8f3b706 100644 --- a/lib/ain-ocean/src/storage/macros.rs +++ b/lib/ain-ocean/src/storage/macros.rs @@ -91,7 +91,7 @@ macro_rules! define_table { fn retrieve_primary_value(&self, el: Self::ListItem) -> Result { let (_, id) = el?; let col = self.store.column::<$primary_column>(); - let value = col.get(&id)?.ok_or(Error::SecondaryIndex)?; + let value = col.get(&id)?.context(SecondaryIndexSnafu)?; Ok(value) } } diff --git a/lib/ain-ocean/src/storage/mod.rs b/lib/ain-ocean/src/storage/mod.rs index 6ecd284e6e..054f25d599 100644 --- a/lib/ain-ocean/src/storage/mod.rs +++ b/lib/ain-ocean/src/storage/mod.rs @@ -9,8 +9,9 @@ use ain_db::{Column, ColumnName, DBError, LedgerColumn, Result as DBResult, Type use bitcoin::{hashes::Hash, BlockHash, Txid}; pub use ocean_store::OceanStore; use rocksdb::Direction; +use snafu::OptionExt; -use crate::{define_table, model, Error, Result}; +use crate::{define_table, error::SecondaryIndexSnafu, model, Result}; #[derive(Debug, PartialEq, Eq, Clone)] pub enum SortOrder { @@ -102,7 +103,7 @@ impl SecondaryIndex<(u32, Txid), u8> for MasternodeByHeight { fn retrieve_primary_value(&self, el: Self::ListItem) -> Result { let ((_, id), _) = el?; let col = self.store.column::(); - let tx = col.get(&id)?.ok_or(Error::SecondaryIndex)?; + let tx = col.get(&id)?.context(SecondaryIndexSnafu)?; Ok(tx) } } @@ -163,15 +164,6 @@ define_table! { } } -define_table! { - #[derive(Debug)] - pub struct OraclePriceActiveKey { - key_type = model::OraclePriceActiveKey, - value_type = model::OraclePriceActiveId, - }, - SecondaryIndex = OraclePriceActive -} - define_table! { #[derive(Debug)] pub struct OraclePriceAggregated { @@ -180,15 +172,6 @@ define_table! { } } -define_table! { - #[derive(Debug)] - pub struct OraclePriceAggregatedKey { - key_type = model::OraclePriceAggregatedKey, - value_type = model::OraclePriceAggregatedId, - }, - SecondaryIndex = OraclePriceAggregated -} - define_table! { #[derive(Debug)] pub struct OraclePriceAggregatedInterval { @@ -197,15 +180,6 @@ define_table! { } } -define_table! { - #[derive(Debug)] - pub struct OraclePriceAggregatedIntervalKey { - key_type = model::OraclePriceAggregatedIntervalKey, - value_type = model::OraclePriceAggregatedIntervalId, - }, - SecondaryIndex = OraclePriceAggregatedInterval -} - define_table! { #[derive(Debug)] pub struct OraclePriceFeed { @@ -214,15 +188,6 @@ define_table! { } } -define_table! { - #[derive(Debug)] - pub struct OraclePriceFeedKey { - key_type = model::OraclePriceFeedKey, - value_type = model::OraclePriceFeedId, - }, - SecondaryIndex = OraclePriceFeed -} - define_table! { #[derive(Debug)] pub struct OracleTokenCurrency { @@ -443,16 +408,7 @@ define_table! { } } -define_table! { - #[derive(Debug)] - pub struct VaultAuctionHistoryByHeight { - key_type = model::AuctionHistoryByHeightKey, - value_type = model::AuctionHistoryKey, - }, - SecondaryIndex = VaultAuctionHistory -} - -pub const COLUMN_NAMES: [&str; 33] = [ +pub const COLUMN_NAMES: [&str; 28] = [ Block::NAME, BlockByHeight::NAME, MasternodeStats::NAME, @@ -461,13 +417,9 @@ pub const COLUMN_NAMES: [&str; 33] = [ Oracle::NAME, OracleHistory::NAME, OraclePriceActive::NAME, - OraclePriceActiveKey::NAME, OraclePriceAggregated::NAME, - OraclePriceAggregatedKey::NAME, OraclePriceAggregatedInterval::NAME, - OraclePriceAggregatedIntervalKey::NAME, OraclePriceFeed::NAME, - OraclePriceFeedKey::NAME, OracleTokenCurrency::NAME, PoolSwapAggregated::NAME, PoolSwapAggregatedKey::NAME, @@ -485,5 +437,4 @@ pub const COLUMN_NAMES: [&str; 33] = [ TransactionVout::NAME, TxResult::NAME, VaultAuctionHistory::NAME, - VaultAuctionHistoryByHeight::NAME, ];