Skip to content

Commit

Permalink
feat(trading-proto-upgrade): sql storage wip + protocol enhancement (#…
Browse files Browse the repository at this point in the history
…1980)

This commit:
- Removes access by index for UTXO transactions handling
- Changes OP_CHECKMULTISIG to <pubkey0> OP_CHECKSIGVERIFY <pubkey1> OP_CHECKSIG
- Stores upgraded swaps data to SQLite DB (still WIP)
- Implements protocol enhancement for UTXO coins by adding one more funding tx for taker, which can be reclaimed immediately if maker back-outs.
- Adds CoinAssocTypes trait representing coin associated types.

#1895
  • Loading branch information
artemii235 authored Oct 30, 2023
1 parent 1224c03 commit 8d0a583
Show file tree
Hide file tree
Showing 21 changed files with 2,585 additions and 832 deletions.
230 changes: 175 additions & 55 deletions mm2src/coins/lp_coins.rs

Large diffs are not rendered by default.

123 changes: 102 additions & 21 deletions mm2src/coins/test_coin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@

use super::{CoinBalance, HistorySyncState, MarketCoinOps, MmCoin, RawTransactionFut, RawTransactionRequest, SwapOps,
TradeFee, TransactionEnum, TransactionFut};
use crate::ValidateWatcherSpendInput;
use crate::{coin_errors::MyAddressError, BalanceFut, CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinFutSpawner,
ConfirmPaymentInput, FeeApproxStage, FoundSwapTxSpend, GenTakerPaymentSpendArgs,
GenTakerPaymentSpendResult, MakerSwapTakerCoin, MmCoinEnum, NegotiateSwapContractAddrErr,
PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, RefundPaymentArgs, RefundResult,
SearchForSwapTxSpendInput, SendCombinedTakerPaymentArgs, SendMakerPaymentSpendPreimageInput,
SendPaymentArgs, SignatureResult, SpendPaymentArgs, SwapOpsV2, TakerSwapMakerCoin, TradePreimageFut,
TradePreimageResult, TradePreimageValue, TransactionResult, TxMarshalingErr, TxPreimageWithSig,
use crate::{coin_errors::MyAddressError, BalanceFut, CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinAssocTypes,
CoinFutSpawner, ConfirmPaymentInput, FeeApproxStage, FoundSwapTxSpend, GenPreimageResult,
GenTakerFundingSpendArgs, GenTakerPaymentSpendArgs, MakerSwapTakerCoin, MmCoinEnum,
NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr,
RefundFundingSecretArgs, RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput,
SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SendTakerFundingArgs, SignatureResult,
SpendPaymentArgs, SwapOpsV2, TakerSwapMakerCoin, TradePreimageFut, TradePreimageResult,
TradePreimageValue, Transaction, TransactionErr, TransactionResult, TxMarshalingErr, TxPreimageWithSig,
UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr,
ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput,
ValidateTakerPaymentArgs, ValidateTakerPaymentResult, ValidateTakerPaymentSpendPreimageResult,
VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError,
WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut,
WithdrawRequest};
ValidateTakerFundingArgs, ValidateTakerFundingResult, ValidateTakerFundingSpendPreimageResult,
ValidateTakerPaymentSpendPreimageResult, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps,
WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput,
WatcherValidateTakerFeeInput, WithdrawFut, WithdrawRequest};
use crate::{ToBytes, ValidateWatcherSpendInput};
use async_trait::async_trait;
use common::executor::AbortedError;
use futures01::Future;
Expand Down Expand Up @@ -387,42 +388,122 @@ impl MmCoin for TestCoin {
fn on_token_deactivated(&self, _ticker: &str) { () }
}

pub struct TestPubkey {}

impl ToBytes for TestPubkey {
fn to_bytes(&self) -> Vec<u8> { vec![] }
}

#[derive(Debug)]
pub struct TestTx {}

impl Transaction for TestTx {
fn tx_hex(&self) -> Vec<u8> { todo!() }

fn tx_hash(&self) -> BytesJson { todo!() }
}

pub struct TestPreimage {}

impl ToBytes for TestPreimage {
fn to_bytes(&self) -> Vec<u8> { vec![] }
}

pub struct TestSig {}

impl ToBytes for TestSig {
fn to_bytes(&self) -> Vec<u8> { vec![] }
}

impl CoinAssocTypes for TestCoin {
type Pubkey = TestPubkey;
type PubkeyParseError = String;
type Tx = TestTx;
type TxParseError = String;
type Preimage = TestPreimage;
type PreimageParseError = String;
type Sig = TestSig;
type SigParseError = String;

fn parse_pubkey(&self, pubkey: &[u8]) -> Result<Self::Pubkey, Self::PubkeyParseError> { unimplemented!() }

fn parse_tx(&self, tx: &[u8]) -> Result<Self::Tx, Self::TxParseError> { unimplemented!() }

fn parse_preimage(&self, preimage: &[u8]) -> Result<Self::Preimage, Self::PreimageParseError> { todo!() }

fn parse_signature(&self, sig: &[u8]) -> Result<Self::Sig, Self::SigParseError> { todo!() }
}

#[async_trait]
#[mockable]
impl SwapOpsV2 for TestCoin {
async fn send_combined_taker_payment(&self, args: SendCombinedTakerPaymentArgs<'_>) -> TransactionResult {
async fn send_taker_funding(&self, args: SendTakerFundingArgs<'_>) -> Result<Self::Tx, TransactionErr> { todo!() }

async fn validate_taker_funding(&self, args: ValidateTakerFundingArgs<'_, Self>) -> ValidateTakerFundingResult {
unimplemented!()
}

async fn validate_combined_taker_payment(&self, args: ValidateTakerPaymentArgs<'_>) -> ValidateTakerPaymentResult {
unimplemented!()
async fn refund_taker_funding_timelock(&self, args: RefundPaymentArgs<'_>) -> TransactionResult { todo!() }

async fn refund_taker_funding_secret(
&self,
args: RefundFundingSecretArgs<'_, Self>,
) -> Result<Self::Tx, TransactionErr> {
todo!()
}

async fn gen_taker_funding_spend_preimage(
&self,
args: &GenTakerFundingSpendArgs<'_, Self>,
swap_unique_data: &[u8],
) -> GenPreimageResult<Self> {
todo!()
}

async fn validate_taker_funding_spend_preimage(
&self,
gen_args: &GenTakerFundingSpendArgs<'_, Self>,
preimage: &TxPreimageWithSig<Self>,
) -> ValidateTakerFundingSpendPreimageResult {
todo!()
}

async fn sign_and_send_taker_funding_spend(
&self,
preimage: &TxPreimageWithSig<Self>,
args: &GenTakerFundingSpendArgs<'_, Self>,
swap_unique_data: &[u8],
) -> Result<Self::Tx, TransactionErr> {
todo!()
}

async fn refund_combined_taker_payment(&self, args: RefundPaymentArgs<'_>) -> TransactionResult { unimplemented!() }

async fn gen_taker_payment_spend_preimage(
&self,
args: &GenTakerPaymentSpendArgs<'_>,
args: &GenTakerPaymentSpendArgs<'_, Self>,
swap_unique_data: &[u8],
) -> GenTakerPaymentSpendResult {
) -> GenPreimageResult<Self> {
unimplemented!()
}

async fn validate_taker_payment_spend_preimage(
&self,
gen_args: &GenTakerPaymentSpendArgs<'_>,
preimage: &TxPreimageWithSig,
gen_args: &GenTakerPaymentSpendArgs<'_, Self>,
preimage: &TxPreimageWithSig<Self>,
) -> ValidateTakerPaymentSpendPreimageResult {
unimplemented!()
}

async fn sign_and_broadcast_taker_payment_spend(
&self,
preimage: &TxPreimageWithSig,
gen_args: &GenTakerPaymentSpendArgs<'_>,
preimage: &TxPreimageWithSig<Self>,
gen_args: &GenTakerPaymentSpendArgs<'_, Self>,
secret: &[u8],
swap_unique_data: &[u8],
) -> TransactionResult {
unimplemented!()
}

fn derive_htlc_pubkey_v2(&self, swap_unique_data: &[u8]) -> Self::Pubkey { todo!() }
}
44 changes: 43 additions & 1 deletion mm2src/coins/utxo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ use futures::compat::Future01CompatExt;
use futures::lock::{Mutex as AsyncMutex, MutexGuard as AsyncMutexGuard};
use futures01::Future;
use keys::bytes::Bytes;
use keys::Signature;
pub use keys::{Address, AddressFormat as UtxoAddressFormat, AddressHashEnum, KeyPair, Private, Public, Secret,
Type as ScriptType};
#[cfg(not(target_arch = "wasm32"))]
Expand All @@ -74,8 +75,9 @@ use num_traits::ToPrimitive;
use primitives::hash::{H160, H256, H264};
use rpc::v1::types::{Bytes as BytesJson, Transaction as RpcTransaction, H256 as H256Json};
use script::{Builder, Script, SignatureVersion, TransactionInputSigner};
use secp256k1::Signature as SecpSignature;
use serde_json::{self as json, Value as Json};
use serialization::{serialize, serialize_with_flags, Error as SerError, SERIALIZE_TRANSACTION_WITNESS};
use serialization::{deserialize, serialize, serialize_with_flags, Error as SerError, SERIALIZE_TRANSACTION_WITNESS};
use spv_validation::conf::SPVConf;
use spv_validation::helpers_validation::SPVError;
use spv_validation::storage::BlockHeaderStorageError;
Expand Down Expand Up @@ -110,6 +112,7 @@ use crate::hd_wallet::{HDAccountOps, HDAccountsMutex, HDAddress, HDAddressId, HD
InvalidBip44ChainError};
use crate::hd_wallet_storage::{HDAccountStorageItem, HDWalletCoinStorage, HDWalletStorageError, HDWalletStorageResult};
use crate::utxo::tx_cache::UtxoVerboseCacheShared;
use crate::{CoinAssocTypes, ToBytes};

pub mod tx_cache;

Expand Down Expand Up @@ -1011,6 +1014,45 @@ pub trait UtxoCommonOps:
}
}

impl ToBytes for UtxoTx {
fn to_bytes(&self) -> Vec<u8> { self.tx_hex() }
}

impl ToBytes for Signature {
fn to_bytes(&self) -> Vec<u8> { self.to_vec() }
}

impl<T: UtxoCommonOps> CoinAssocTypes for T {
type Pubkey = Public;
type PubkeyParseError = MmError<keys::Error>;
type Tx = UtxoTx;
type TxParseError = MmError<serialization::Error>;
type Preimage = UtxoTx;
type PreimageParseError = MmError<serialization::Error>;
type Sig = Signature;
type SigParseError = MmError<secp256k1::Error>;

#[inline]
fn parse_pubkey(&self, pubkey: &[u8]) -> Result<Self::Pubkey, Self::PubkeyParseError> {
Ok(Public::from_slice(pubkey)?)
}

#[inline]
fn parse_tx(&self, tx: &[u8]) -> Result<Self::Tx, Self::TxParseError> {
let mut tx: UtxoTx = deserialize(tx)?;
tx.tx_hash_algo = self.as_ref().tx_hash_algo;
Ok(tx)
}

#[inline]
fn parse_preimage(&self, tx: &[u8]) -> Result<Self::Preimage, Self::PreimageParseError> { self.parse_tx(tx) }

fn parse_signature(&self, sig: &[u8]) -> Result<Self::Sig, Self::SigParseError> {
SecpSignature::from_der(sig)?;
Ok(sig.into())
}
}

#[async_trait]
#[cfg_attr(test, mockable)]
pub trait GetUtxoListOps {
Expand Down
70 changes: 56 additions & 14 deletions mm2src/coins/utxo/swap_proto_v2_scripts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,79 @@ use bitcrypto::ripemd160;
use keys::Public;
use script::{Builder, Opcode, Script};

/// Builds a script for refundable dex_fee + premium taker transaction
pub fn taker_payment_script(time_lock: u32, secret_hash: &[u8], pub_0: &Public, pub_1: &Public) -> Script {
/// Builds a script for taker funding transaction
pub fn taker_funding_script(
time_lock: u32,
taker_secret_hash: &[u8],
taker_pub: &Public,
maker_pub: &Public,
) -> Script {
let mut builder = Builder::default()
// Dex fee refund path, same lock time as for taker payment
.push_opcode(Opcode::OP_IF)
.push_bytes(&time_lock.to_le_bytes())
.push_opcode(Opcode::OP_CHECKLOCKTIMEVERIFY)
.push_opcode(Opcode::OP_DROP)
.push_bytes(pub_0)
.push_bytes(taker_pub)
.push_opcode(Opcode::OP_CHECKSIG)
.push_opcode(Opcode::OP_ELSE)
.push_opcode(Opcode::OP_IF)
.push_bytes(taker_pub)
.push_opcode(Opcode::OP_CHECKSIGVERIFY)
.push_bytes(maker_pub)
.push_opcode(Opcode::OP_CHECKSIG)
// Dex fee redeem path, Maker needs to reveal the secret to prevent case of getting
// the premium but not proceeding with spending the taker payment
.push_opcode(Opcode::OP_ELSE)
.push_opcode(Opcode::OP_SIZE)
.push_bytes(&[32])
.push_opcode(Opcode::OP_EQUALVERIFY)
.push_opcode(Opcode::OP_HASH160);

if secret_hash.len() == 32 {
builder = builder.push_bytes(ripemd160(secret_hash).as_slice());
if taker_secret_hash.len() == 32 {
builder = builder.push_bytes(ripemd160(taker_secret_hash).as_slice());
} else {
builder = builder.push_bytes(secret_hash);
builder = builder.push_bytes(taker_secret_hash);
}

builder
.push_opcode(Opcode::OP_EQUALVERIFY)
.push_opcode(Opcode::OP_2)
.push_bytes(pub_0)
.push_bytes(pub_1)
.push_opcode(Opcode::OP_2)
.push_opcode(Opcode::OP_CHECKMULTISIG)
.push_bytes(taker_pub)
.push_opcode(Opcode::OP_CHECKSIG)
.push_opcode(Opcode::OP_ENDIF)
.push_opcode(Opcode::OP_ENDIF)
.into_script()
}

/// Builds a script for combined trading_volume + dex_fee + premium taker transaction
pub fn taker_payment_script(
time_lock: u32,
maker_secret_hash: &[u8],
taker_pub: &Public,
maker_pub: &Public,
) -> Script {
let mut builder = Builder::default()
.push_opcode(Opcode::OP_IF)
.push_bytes(&time_lock.to_le_bytes())
.push_opcode(Opcode::OP_CHECKLOCKTIMEVERIFY)
.push_opcode(Opcode::OP_DROP)
.push_bytes(taker_pub)
.push_opcode(Opcode::OP_CHECKSIG)
.push_opcode(Opcode::OP_ELSE)
.push_opcode(Opcode::OP_SIZE)
.push_bytes(&[32])
.push_opcode(Opcode::OP_EQUALVERIFY)
.push_opcode(Opcode::OP_HASH160);

if maker_secret_hash.len() == 32 {
builder = builder.push_bytes(ripemd160(maker_secret_hash).as_slice());
} else {
builder = builder.push_bytes(maker_secret_hash);
}

builder
.push_opcode(Opcode::OP_EQUALVERIFY)
.push_bytes(taker_pub)
.push_opcode(Opcode::OP_CHECKSIGVERIFY)
.push_bytes(maker_pub)
.push_opcode(Opcode::OP_CHECKSIG)
.push_opcode(Opcode::OP_ENDIF)
.into_script()
}
Loading

0 comments on commit 8d0a583

Please sign in to comment.