swap tip consensus fix
This commit is contained in:
parent
a85e9aab4c
commit
fe83bd50dc
|
|
@ -17,6 +17,23 @@ use crate::verifications::async_funcs::checks::verify_db::{
|
|||
use crate::wallets::structures::Wallet;
|
||||
use crate::Utc;
|
||||
|
||||
fn validate_swap_tip(value: u64, tip: u64, is_nft: bool, sender: &str) -> Result<(), String> {
|
||||
if is_nft {
|
||||
if tip != 0 {
|
||||
return Err(format!("Swap {sender} NFT tip must be zero."));
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let minimum_tip = value.div_ceil(100);
|
||||
if tip < minimum_tip {
|
||||
return Err(format!(
|
||||
"Swap {sender} tip must be at least 1% of the offered amount."
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl SwapTransaction {
|
||||
pub async fn verify(&self, db: &Db) -> Result<String, String> {
|
||||
// Transactions already present in the mempool can short-circuit
|
||||
|
|
@ -77,12 +94,13 @@ impl SwapTransaction {
|
|||
);
|
||||
}
|
||||
let network_base_coin_padded = block_extension_and_paths().1;
|
||||
if !is_canonical_padded_asset_or_base(&self.unsigned_swap.ticker1, &network_base_coin_padded)
|
||||
|| !is_canonical_padded_asset_or_base(
|
||||
if !is_canonical_padded_asset_or_base(
|
||||
&self.unsigned_swap.ticker1,
|
||||
&network_base_coin_padded,
|
||||
) || !is_canonical_padded_asset_or_base(
|
||||
&self.unsigned_swap.ticker2,
|
||||
&network_base_coin_padded,
|
||||
)
|
||||
{
|
||||
) {
|
||||
return Err(
|
||||
"Swap assets must be the base coin or canonical alphanumeric asset ids."
|
||||
.to_string(),
|
||||
|
|
@ -100,33 +118,43 @@ impl SwapTransaction {
|
|||
|
||||
// Numbered NFT swaps are single-item transfers, while non-series
|
||||
// assets must already exist as a token, NFT, or base coin.
|
||||
if self.unsigned_swap.nft_series1 > 0 {
|
||||
let ticker1_is_nft = if self.unsigned_swap.nft_series1 > 0 {
|
||||
if self.unsigned_swap.value1 != 1 {
|
||||
return Err("Series NFTs must swap exactly 1 item.".to_string());
|
||||
}
|
||||
if !db_bytes_verification(db, "nfts", &asset1).await {
|
||||
return Err("Ticker1 NFT item does not exist.".to_string());
|
||||
}
|
||||
} else if !(self.unsigned_swap.ticker1.trim().to_lowercase() == network_base_coin
|
||||
|| db_bytes_verification(db, "tokens", &self.unsigned_swap.ticker1).await
|
||||
|| db_bytes_verification(db, "nfts", &self.unsigned_swap.ticker1).await)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
let is_base_coin =
|
||||
self.unsigned_swap.ticker1.trim().to_lowercase() == network_base_coin;
|
||||
let is_token = db_bytes_verification(db, "tokens", &self.unsigned_swap.ticker1).await;
|
||||
let is_nft = db_bytes_verification(db, "nfts", &self.unsigned_swap.ticker1).await;
|
||||
if !(is_base_coin || is_token || is_nft) {
|
||||
return Err("Ticker1 does not exist.".to_string());
|
||||
}
|
||||
is_nft
|
||||
};
|
||||
|
||||
if self.unsigned_swap.nft_series2 > 0 {
|
||||
let ticker2_is_nft = if self.unsigned_swap.nft_series2 > 0 {
|
||||
if self.unsigned_swap.value2 != 1 {
|
||||
return Err("Series NFTs must swap exactly 1 item.".to_string());
|
||||
}
|
||||
if !db_bytes_verification(db, "nfts", &asset2).await {
|
||||
return Err("Ticker2 NFT item does not exist.".to_string());
|
||||
}
|
||||
} else if !(self.unsigned_swap.ticker2.trim().to_lowercase() == network_base_coin
|
||||
|| db_bytes_verification(db, "tokens", &self.unsigned_swap.ticker2).await
|
||||
|| db_bytes_verification(db, "nfts", &self.unsigned_swap.ticker2).await)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
let is_base_coin =
|
||||
self.unsigned_swap.ticker2.trim().to_lowercase() == network_base_coin;
|
||||
let is_token = db_bytes_verification(db, "tokens", &self.unsigned_swap.ticker2).await;
|
||||
let is_nft = db_bytes_verification(db, "nfts", &self.unsigned_swap.ticker2).await;
|
||||
if !(is_base_coin || is_token || is_nft) {
|
||||
return Err("Ticker2 does not exist.".to_string());
|
||||
}
|
||||
is_nft
|
||||
};
|
||||
|
||||
// Each signer pays the fixed swap fee for their side of the trade.
|
||||
if self.unsigned_swap.txfee1 < SWAP_FEE {
|
||||
|
|
@ -141,6 +169,22 @@ impl SwapTransaction {
|
|||
));
|
||||
}
|
||||
|
||||
// Fungible swap sides pay an asset-denominated miner tip of at
|
||||
// least 1% of the offered amount. NFT sides cannot pay a
|
||||
// fractional NFT tip, so their tip must be zero.
|
||||
validate_swap_tip(
|
||||
self.unsigned_swap.value1,
|
||||
self.unsigned_swap.tip1,
|
||||
ticker1_is_nft,
|
||||
"sender1",
|
||||
)?;
|
||||
validate_swap_tip(
|
||||
self.unsigned_swap.value2,
|
||||
self.unsigned_swap.tip2,
|
||||
ticker2_is_nft,
|
||||
"sender2",
|
||||
)?;
|
||||
|
||||
// Each side must be able to cover the offered amount plus any
|
||||
// asset-denominated tip and the base-coin transaction fee.
|
||||
let full_value1 = self.unsigned_swap.value1 + self.unsigned_swap.tip1;
|
||||
|
|
@ -211,3 +255,21 @@ impl SwapTransaction {
|
|||
Ok(sign.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::validate_swap_tip;
|
||||
|
||||
#[test]
|
||||
fn fungible_swap_tip_requires_one_percent_rounded_up() {
|
||||
assert!(validate_swap_tip(100, 1, false, "sender1").is_ok());
|
||||
assert!(validate_swap_tip(101, 2, false, "sender1").is_ok());
|
||||
assert!(validate_swap_tip(101, 1, false, "sender1").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nft_swap_tip_must_be_zero() {
|
||||
assert!(validate_swap_tip(1, 0, true, "sender1").is_ok());
|
||||
assert!(validate_swap_tip(1, 1, true, "sender1").is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue