Skip to content

Commit

Permalink
Added sign_eth_transaction rpc implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
dimxy committed Aug 24, 2023
1 parent 6b11193 commit 5d422ab
Show file tree
Hide file tree
Showing 16 changed files with 274 additions and 22 deletions.
65 changes: 58 additions & 7 deletions mm2src/coins/eth.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/******************************************************************************
* Copyright © 2022 Atomic Private Limited and its contributors *
* Copyright © 2023 Atomic Private Limited and its contributors *
* *
* See the CONTRIBUTOR-LICENSE-AGREEMENT, COPYING, LICENSE-COPYRIGHT-NOTICE *
* and DEVELOPER-CERTIFICATE-OF-ORIGIN files in the LEGAL directory in *
Expand Down Expand Up @@ -98,7 +98,7 @@ use super::{coin_conf, lp_coinfind_or_err, AsyncMutex, BalanceError, BalanceFut,
WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput,
WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult, EARLY_CONFIRMATION_ERR_LOG,
INVALID_CONTRACT_ADDRESS_ERR_LOG, INVALID_PAYMENT_STATE_ERR_LOG, INVALID_RECEIVER_ERR_LOG,
INVALID_SENDER_ERR_LOG, INVALID_SWAP_ID_ERR_LOG};
INVALID_SENDER_ERR_LOG, INVALID_SWAP_ID_ERR_LOG, SignEthTransactionRequest, SignEthTransactionResponse, SignEthTransactionResult};
pub use rlp;

#[cfg(test)] mod eth_tests;
Expand Down Expand Up @@ -1915,6 +1915,39 @@ impl MarketCoinOps for EthCoin {
})
}

/// Stub for sign eth tx
#[inline(always)]
async fn sign_eth_tx(&self, args: &SignEthTransactionRequest) -> SignEthTransactionResult {
let ctx = MmArc::from_weak(&self.ctx).ok_or("!ctx")
.map_to_mm(|err| RawTransactionError::TransactionError(err.to_string()))?;
let coin = self.clone();
let value = if let Some(value) = args.value { value } else { U256::from(0) };

let action = if let Some(to) = &args.to {
Action::Call(
Address::from_str(to).map_to_mm(|err| RawTransactionError::InvalidParam(err.to_string()))?
)
} else {
Action::Create
};
let data = if let Some(data) = &args.data { data.clone() } else { vec![] };
let gas_limit = args.gas_limit;
match coin.priv_key_policy {
// TODO: use zeroise for privkey
EthPrivKeyPolicy::KeyPair(ref key_pair) => {
return sign_transaction_with_keypair(ctx, &coin, key_pair, value, action, data, gas_limit).await
.map(|(signed_tx, _)| SignEthTransactionResponse {
tx_hex: signed_tx.tx_hex().into(),
})
.map_to_mm(|err| RawTransactionError::TransactionError(err.get_plain_text_format()));
},
#[cfg(target_arch = "wasm32")]
EthPrivKeyPolicy::Metamask(_) => {
unimplemented!()
},
}
}

fn wait_for_confirmations(&self, input: ConfirmPaymentInput) -> Box<dyn Future<Item = (), Error = String> + Send> {
macro_rules! update_status_with_error {
($status: ident, $error: ident) => {
Expand Down Expand Up @@ -2145,19 +2178,19 @@ lazy_static! {

type EthTxFut = Box<dyn Future<Item = SignedEthTx, Error = TransactionErr> + Send + 'static>;

async fn sign_and_send_transaction_with_keypair(
async fn sign_transaction_with_keypair(
ctx: MmArc,
coin: &EthCoin,
key_pair: &KeyPair,
value: U256,
action: Action,
data: Vec<u8>,
gas: U256,
) -> Result<SignedEthTx, TransactionErr> {
) -> Result<(SignedEthTx, Vec<Web3Instance>), TransactionErr> {
let mut status = ctx.log.status_handle();
macro_rules! tags {
() => {
&[&"sign-and-send"]
&[&"sign"]
};
}
let _nonce_lock = coin.nonce_lock.lock().await;
Expand All @@ -2179,7 +2212,25 @@ async fn sign_and_send_transaction_with_keypair(
data,
};

let signed = tx.sign(key_pair.secret(), coin.chain_id);
Ok((tx.sign(key_pair.secret(), coin.chain_id), web3_instances_with_latest_nonce))
}

async fn sign_and_send_transaction_with_keypair(
ctx: MmArc,
coin: &EthCoin,
key_pair: &KeyPair,
value: U256,
action: Action,
data: Vec<u8>,
gas: U256,
) -> Result<SignedEthTx, TransactionErr> {
let mut status = ctx.log.status_handle();
macro_rules! tags {
() => {
&[&"sign-and-send"]
};
}
let (signed, web3_instances_with_latest_nonce) = sign_transaction_with_keypair(ctx, coin, key_pair, value, action, data, gas).await?;
let bytes = Bytes(rlp::encode(&signed).to_vec());
status.status(tags!(), "send_raw_transaction…");

Expand All @@ -2189,7 +2240,7 @@ async fn sign_and_send_transaction_with_keypair(
try_tx_s!(select_ok(futures).await.map_err(|e| ERRL!("{}", e)), signed);

status.status(tags!(), "get_addr_nonce…");
coin.wait_for_addr_nonce_increase(coin.my_address, nonce).await;
coin.wait_for_addr_nonce_increase(coin.my_address, signed.transaction.unsigned.nonce).await;
Ok(signed)
}

Expand Down
11 changes: 10 additions & 1 deletion mm2src/coins/lightning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, C
UtxoStandardCoin, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr,
ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationError, VerificationResult,
WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput,
WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest};
WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest,
SignEthTransactionRequest, SignEthTransactionResult};
use async_trait::async_trait;
use bitcoin::bech32::ToBase32;
use bitcoin::hashes::Hash;
Expand Down Expand Up @@ -1103,6 +1104,14 @@ impl MarketCoinOps for LightningCoin {
})
}

/// Stub for sign eth tx
#[inline(always)]
async fn sign_eth_tx(&self, _args: &SignEthTransactionRequest) -> SignEthTransactionResult {
MmError::err(RawTransactionError::NotImplemented {
coin: self.ticker().to_string(),
})
}

// Todo: Add waiting for confirmations logic for the case of if the channel is closed and the htlc can be claimed on-chain
// Todo: The above is postponed and might not be needed after this issue is resolved https://github.com/lightningdevkit/rust-lightning/issues/2017
fn wait_for_confirmations(&self, input: ConfirmPaymentInput) -> Box<dyn Future<Item = (), Error = String> + Send> {
Expand Down
39 changes: 38 additions & 1 deletion mm2src/coins/lp_coins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ pub mod eth;
use eth::GetValidEthWithdrawAddError;
use eth::{eth_coin_from_conf_and_request, get_eth_address, EthCoin, EthGasDetailsErr, EthTxFeeDetails,
GetEthAddressError, SignedEthTx};
use ethereum_types::U256;

pub mod hd_confirm_address;
pub mod hd_pubkey;
Expand Down Expand Up @@ -310,6 +311,7 @@ pub type RawTransactionResult = Result<RawTransactionRes, MmError<RawTransaction
pub type RawTransactionFut<'a> =
Box<dyn Future<Item = RawTransactionRes, Error = MmError<RawTransactionError>> + Send + 'a>;
pub type SignRawTransactionResult = Result<SignRawTransactionResponse, MmError<RawTransactionError>>;
pub type SignEthTransactionResult = Result<SignEthTransactionResponse, MmError<RawTransactionError>>;
pub type RefundResult<T> = Result<T, MmError<RefundError>>;

pub type IguanaPrivKey = Secp256k1Secret;
Expand Down Expand Up @@ -348,6 +350,8 @@ pub enum RawTransactionError {
SigningError(String),
#[display(fmt = "Not implemented for this coin {}", coin)]
NotImplemented { coin: String },
#[display(fmt = "Transaction error {}", _0)]
TransactionError(String),
}

impl HttpStatusCode for RawTransactionError {
Expand All @@ -361,7 +365,8 @@ impl HttpStatusCode for RawTransactionError {
| RawTransactionError::HashNotExist(_)
| RawTransactionError::DecodeError(_)
| RawTransactionError::InvalidParam(_)
| RawTransactionError::NonExistentPrevOutputError(_) => StatusCode::BAD_REQUEST,
| RawTransactionError::NonExistentPrevOutputError(_)
| RawTransactionError::TransactionError(_) => StatusCode::BAD_REQUEST,
RawTransactionError::NotImplemented { .. } => StatusCode::NOT_IMPLEMENTED,
}
}
Expand Down Expand Up @@ -455,6 +460,30 @@ pub struct SignRawTransactionResponse {
pub tx_hex: BytesJson,
}

/// RPC request with unsigned eth transaction and params for signing
#[derive(Clone, Debug, Deserialize)]
pub struct SignEthTransactionRequest {
/// Coin ticker name
pub coin: String,
/// Eth transfer value
value: Option<U256>,
/// Eth to address
to: Option<String>,
/// Eth contract data
data: Option<Vec<u8>>,
/// Eth gas use limit
gas_limit: U256,
}

/// RPC response with signed eth transaction
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct SignEthTransactionResponse {
/// Raw bytes of signed transaction in hexadecimal string which is the response from the sign_eth_transaction request
pub tx_hex: BytesJson,
}



#[derive(Debug, Deserialize)]
pub struct MyAddressReq {
coin: String,
Expand Down Expand Up @@ -1086,6 +1115,9 @@ pub trait MarketCoinOps {
/// Signs raw utxo transaction in hexadecimal format as input and returns signed transaction in hexadecimal format
async fn sign_raw_tx(&self, args: &SignRawTransactionRequest) -> SignRawTransactionResult;

/// Signs ethereum transaction in hexadecimal format as input and returns signed transaction in hexadecimal format
async fn sign_eth_tx(&self, args: &SignEthTransactionRequest) -> SignEthTransactionResult;

fn wait_for_confirmations(&self, input: ConfirmPaymentInput) -> Box<dyn Future<Item = (), Error = String> + Send>;

fn wait_for_htlc_tx_spend(&self, args: WaitForHTLCTxSpendArgs<'_>) -> TransactionFut;
Expand Down Expand Up @@ -3370,6 +3402,11 @@ pub async fn sign_raw_transaction(ctx: MmArc, req: SignRawTransactionRequest) ->
coin.sign_raw_tx(&req).await
}

pub async fn sign_eth_transaction(ctx: MmArc, req: SignEthTransactionRequest) -> SignEthTransactionResult {
let coin = lp_coinfind_or_err(&ctx, &req.coin).await?;
coin.sign_eth_tx(&req).await
}

pub async fn remove_delegation(ctx: MmArc, req: RemoveDelegateRequest) -> DelegationResult {
let coin = lp_coinfind_or_err(&ctx, &req.coin).await?;
match coin {
Expand Down
11 changes: 10 additions & 1 deletion mm2src/coins/qrc20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, Coi
ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr,
ValidatePaymentFut, ValidatePaymentInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps,
WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput,
WatcherValidateTakerFeeInput, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult};
WatcherValidateTakerFeeInput, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult,
RawTransactionError, SignEthTransactionRequest, SignEthTransactionResult};
use async_trait::async_trait;
use bitcrypto::{dhash160, sha256};
use chain::TransactionOutput;
Expand Down Expand Up @@ -1238,6 +1239,14 @@ impl MarketCoinOps for Qrc20Coin {
async fn sign_raw_tx(&self, args: &SignRawTransactionRequest) -> SignRawTransactionResult {
utxo_common::sign_raw_tx(self, args).await
}

/// Stub for sign eth tx
#[inline(always)]
async fn sign_eth_tx(&self, _args: &SignEthTransactionRequest) -> SignEthTransactionResult {
MmError::err(RawTransactionError::NotImplemented {
coin: self.ticker().to_string(),
})
}

fn wait_for_confirmations(&self, input: ConfirmPaymentInput) -> Box<dyn Future<Item = (), Error = String> + Send> {
let tx: UtxoTx = try_fus!(deserialize(input.payment_tx.as_slice()).map_err(|e| ERRL!("{:?}", e)));
Expand Down
11 changes: 10 additions & 1 deletion mm2src/coins/solana.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinFutSpawner,
ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError,
ValidatePaymentFut, ValidatePaymentInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherReward,
WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput,
WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult};
WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult,
SignEthTransactionRequest, SignEthTransactionResult};
use async_trait::async_trait;
use base58::ToBase58;
use bincode::{deserialize, serialize};
Expand Down Expand Up @@ -447,6 +448,14 @@ impl MarketCoinOps for SolanaCoin {
})
}

/// Stub for sign eth tx
#[inline(always)]
async fn sign_eth_tx(&self, _args: &SignEthTransactionRequest) -> SignEthTransactionResult {
MmError::err(RawTransactionError::NotImplemented {
coin: self.ticker().to_string(),
})
}

fn wait_for_confirmations(&self, _input: ConfirmPaymentInput) -> Box<dyn Future<Item = (), Error = String> + Send> {
unimplemented!()
}
Expand Down
11 changes: 10 additions & 1 deletion mm2src/coins/solana/spl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinFutSpawner, ConfirmPayment
ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput,
VerificationResult, WaitForHTLCTxSpendArgs, WatcherReward, WatcherRewardError,
WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput,
WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult};
WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult,
SignEthTransactionRequest, SignEthTransactionResult};
use async_trait::async_trait;
use bincode::serialize;
use common::executor::{abortable_queue::AbortableQueue, AbortableSystem, AbortedError};
Expand Down Expand Up @@ -274,6 +275,14 @@ impl MarketCoinOps for SplToken {
coin: self.ticker().to_string(),
})
}

/// Stub for sign eth tx
#[inline(always)]
async fn sign_eth_tx(&self, _args: &SignEthTransactionRequest) -> SignEthTransactionResult {
MmError::err(RawTransactionError::NotImplemented {
coin: self.ticker().to_string(),
})
}

fn wait_for_confirmations(&self, _input: ConfirmPaymentInput) -> Box<dyn Future<Item = (), Error = String> + Send> {
unimplemented!()
Expand Down
10 changes: 9 additions & 1 deletion mm2src/coins/tendermint/tendermint_coin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use crate::{big_decimal_from_sat_unsigned, BalanceError, BalanceFut, BigDecimal,
ValidateOtherPubKeyErr, ValidatePaymentFut, ValidatePaymentInput, VerificationError, VerificationResult,
WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput,
WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFee, WithdrawFut,
WithdrawRequest};
WithdrawRequest, SignEthTransactionRequest, SignEthTransactionResult};
use async_std::prelude::FutureExt as AsyncStdFutureExt;
use async_trait::async_trait;
use bitcrypto::{dhash160, sha256};
Expand Down Expand Up @@ -2192,6 +2192,14 @@ impl MarketCoinOps for TendermintCoin {
coin: self.ticker().to_string(),
})
}

/// Stub for sign eth tx
#[inline(always)]
async fn sign_eth_tx(&self, _args: &SignEthTransactionRequest) -> SignEthTransactionResult {
MmError::err(RawTransactionError::NotImplemented {
coin: self.ticker().to_string(),
})
}

fn wait_for_confirmations(&self, input: ConfirmPaymentInput) -> Box<dyn Future<Item = (), Error = String> + Send> {
// Sanity check
Expand Down
13 changes: 11 additions & 2 deletions mm2src/coins/tendermint/tendermint_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ use crate::{big_decimal_from_sat_unsigned, utxo::sat_from_big_decimal, BalanceFu
ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr,
ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationResult,
WaitForHTLCTxSpendArgs, WatcherOps, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput,
WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest};
use crate::{MmCoinEnum, PaymentInstructionArgs, RawTransactionError, WatcherReward, WatcherRewardError};
WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest,
SignEthTransactionRequest, SignEthTransactionResult, MmCoinEnum, PaymentInstructionArgs,
RawTransactionError, WatcherReward, WatcherRewardError};
use async_trait::async_trait;
use bitcrypto::sha256;
use common::executor::abortable_queue::AbortableQueue;
Expand Down Expand Up @@ -545,6 +546,14 @@ impl MarketCoinOps for TendermintToken {
coin: self.ticker().to_string(),
})
}

/// Stub for sign eth tx
#[inline(always)]
async fn sign_eth_tx(&self, _args: &SignEthTransactionRequest) -> SignEthTransactionResult {
MmError::err(RawTransactionError::NotImplemented {
coin: self.ticker().to_string(),
})
}

fn wait_for_confirmations(&self, input: ConfirmPaymentInput) -> Box<dyn Future<Item = (), Error = String> + Send> {
self.platform_coin.wait_for_confirmations(input)
Expand Down
5 changes: 4 additions & 1 deletion mm2src/coins/test_coin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{coin_errors::MyAddressError, BalanceFut, CanRefundHtlc, CheckIfMyPay
ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput,
VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError,
WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut,
WithdrawRequest};
WithdrawRequest, SignEthTransactionRequest, SignEthTransactionResult};
use async_trait::async_trait;
use common::executor::AbortedError;
use futures01::Future;
Expand Down Expand Up @@ -80,6 +80,9 @@ impl MarketCoinOps for TestCoin {

#[inline(always)]
async fn sign_raw_tx(&self, _args: &SignRawTransactionRequest) -> SignRawTransactionResult { unimplemented!() }

#[inline(always)]
async fn sign_eth_tx(&self, _args: &SignEthTransactionRequest) -> SignEthTransactionResult { unimplemented!() }

fn wait_for_confirmations(&self, _input: ConfirmPaymentInput) -> Box<dyn Future<Item = (), Error = String> + Send> {
unimplemented!()
Expand Down
11 changes: 10 additions & 1 deletion mm2src/coins/utxo/bch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ use crate::{BlockHeightAndTime, CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinBal
UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr,
ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput,
VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError,
WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut};
WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut,
RawTransactionError, SignEthTransactionRequest, SignEthTransactionResult};
use common::executor::{AbortableSystem, AbortedError};
use common::log::warn;
use derive_more::Display;
Expand Down Expand Up @@ -1182,6 +1183,14 @@ impl MarketCoinOps for BchCoin {
async fn sign_raw_tx(&self, args: &SignRawTransactionRequest) -> SignRawTransactionResult {
utxo_common::sign_raw_tx(self, args).await
}

/// Stub for sign eth tx
#[inline(always)]
async fn sign_eth_tx(&self, _args: &SignEthTransactionRequest) -> SignEthTransactionResult {
MmError::err(RawTransactionError::NotImplemented {
coin: self.ticker().to_string(),
})
}

fn wait_for_confirmations(&self, input: ConfirmPaymentInput) -> Box<dyn Future<Item = (), Error = String> + Send> {
utxo_common::wait_for_confirmations(&self.utxo_arc, input)
Expand Down
Loading

0 comments on commit 5d422ab

Please sign in to comment.