Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tx): add new sign_raw_transaction rpc for UTXO and EVM coins #1930

Merged
merged 23 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f1eb95c
fix p2wpkh prev script check, rename build script fn to p2witness
dimxy Aug 2, 2023
da7c778
use sign_low_r version for signing (like in latest btc code)
dimxy Aug 2, 2023
a179ebf
add sign_raw_transaction rpc method for utxo coins
dimxy Aug 2, 2023
bc5e776
fix UtxoWithdraw prev script creation to add witness program
dimxy Aug 2, 2023
8a8b861
refactoring signrawtransaction: use async_trait, fix solana build err
dimxy Aug 14, 2023
285f384
refactor utxo_common::sign_raw_tx: func added, err conversion improved
dimxy Aug 14, 2023
90def41
fix fmt
dimxy Aug 14, 2023
c941aa5
refactor sign_raw_tx error return
dimxy Aug 24, 2023
1e00b53
refactor RawTransactionError
dimxy Aug 24, 2023
a91a500
Added sign_eth_transaction rpc implementation
dimxy Aug 24, 2023
546579f
added comments (unused field for signing), refactored var name
dimxy Aug 29, 2023
437891a
fixed building prev script for address formats
dimxy Aug 29, 2023
f36260e
Sync with dev and rebase
dimxy Sep 5, 2023
8b70e14
fix key policy for non-wasm
dimxy Sep 5, 2023
54f6ba5
refactor sign_eth_tx on review
dimxy Sep 14, 2023
337f898
added error to get_script_for_address fn result
dimxy Sep 14, 2023
ce8f5a4
refactor: remove sign_eth_tx from MarketCoinOps trait
dimxy Sep 15, 2023
c11d458
refactor on review: made single sign_raw_transaction rpc for both utx…
dimxy Oct 5, 2023
e559234
added wasm bindgen test to sign eth transaction
dimxy Oct 10, 2023
f312094
use test helpers for test sign raw txns
dimxy Oct 10, 2023
a699167
fix after rebase
dimxy Nov 28, 2023
eb0dfee
change value units to eth, fix parse eth data as hex in sign_raw_tran…
dimxy Nov 29, 2023
078385f
fix value in wasm_test_sign_eth_tx
dimxy Dec 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 86 additions & 18 deletions mm2src/coins/eth.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
/******************************************************************************
* Copyright © 2023 Pampex LTD and TillyHK LTD *
* Copyright © 2023 Pampex LTD and TillyHK LTD *
* *
* See the CONTRIBUTOR-LICENSE-AGREEMENT, COPYING, LICENSE-COPYRIGHT-NOTICE *
* and DEVELOPER-CERTIFICATE-OF-ORIGIN files in the LEGAL directory in *
* the top-level directory of this distribution for the individual copyright *
* holder information and the developer policies on copyright and licensing. *
* *
* Unless otherwise agreed in a custom licensing agreement, no part of the *
* Komodo DeFi Framework software, including this file may be copied, modified, propagated*
* Komodo DeFi Framework software, including this file may be copied, modified, propagated *
* or distributed except according to the terms contained in the *
* LICENSE-COPYRIGHT-NOTICE file. *
* *
Expand Down Expand Up @@ -89,17 +89,17 @@ use super::{coin_conf, lp_coinfind_or_err, AsyncMutex, BalanceError, BalanceFut,
PrivKeyBuildPolicy, PrivKeyPolicyNotAllowed, RawTransactionError, RawTransactionFut,
RawTransactionRequest, RawTransactionRes, RawTransactionResult, RefundError, RefundPaymentArgs,
RefundResult, RewardTarget, RpcClientType, RpcTransportEventHandler, RpcTransportEventHandlerShared,
SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignatureError,
SignatureResult, SpendPaymentArgs, SwapOps, TakerSwapMakerCoin, TradeFee, TradePreimageError,
TradePreimageFut, TradePreimageResult, TradePreimageValue, Transaction, TransactionDetails,
TransactionEnum, TransactionErr, TransactionFut, TransactionType, TxMarshalingErr,
UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr,
ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationError,
VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError,
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};
SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignEthTransactionParams,
SignRawTransactionEnum, SignRawTransactionRequest, SignatureError, SignatureResult, SpendPaymentArgs,
SwapOps, TakerSwapMakerCoin, TradeFee, TradePreimageError, TradePreimageFut, TradePreimageResult,
TradePreimageValue, Transaction, TransactionDetails, TransactionEnum, TransactionErr, TransactionFut,
TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs,
ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut,
ValidatePaymentInput, VerificationError, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps,
WatcherReward, WatcherRewardError, 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};
pub use rlp;

#[cfg(test)] mod eth_tests;
Expand Down Expand Up @@ -2058,6 +2058,7 @@ impl WatcherOps for EthCoin {
}
}

#[async_trait]
#[cfg_attr(test, mockable)]
impl MarketCoinOps for EthCoin {
fn ticker(&self) -> &str { &self.ticker[..] }
Expand Down Expand Up @@ -2169,6 +2170,14 @@ impl MarketCoinOps for EthCoin {
)
}

async fn sign_raw_tx(&self, args: &SignRawTransactionRequest) -> RawTransactionResult {
if let SignRawTransactionEnum::ETH(eth_args) = &args.tx {
sign_raw_eth_tx(self, eth_args).await
} else {
MmError::err(RawTransactionError::InvalidParam("eth type expected".to_string()))
}
}

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 @@ -2406,19 +2415,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 @@ -2440,7 +2449,29 @@ 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 @@ -2450,7 +2481,8 @@ 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 Expand Up @@ -2502,6 +2534,42 @@ async fn sign_and_send_transaction_with_metamask(
}
}

/// Sign eth transaction
async fn sign_raw_eth_tx(coin: &EthCoin, args: &SignEthTransactionParams) -> RawTransactionResult {
let ctx = MmArc::from_weak(&coin.ctx)
.ok_or("!ctx")
.map_to_mm(|err| RawTransactionError::TransactionError(err.to_string()))?;
let value = wei_from_big_decimal(args.value.as_ref().unwrap_or(&BigDecimal::from(0)), coin.decimals)?;
let action = if let Some(to) = &args.to {
Call(Address::from_str(to).map_to_mm(|err| RawTransactionError::InvalidParam(err.to_string()))?)
} else {
Create
};
let data = hex::decode(args.data.as_ref().unwrap_or(&String::from("")))?;
match coin.priv_key_policy {
// TODO: use zeroise for privkey
EthPrivKeyPolicy::Iguana(ref key_pair)
| EthPrivKeyPolicy::HDWallet {
activated_key: ref key_pair,
..
} => {
return sign_transaction_with_keypair(ctx, coin, key_pair, value, action, data, args.gas_limit)
.await
.map(|(signed_tx, _)| RawTransactionRes {
tx_hex: signed_tx.tx_hex().into(),
})
.map_to_mm(|err| RawTransactionError::TransactionError(err.get_plain_text_format()));
},
#[cfg(target_arch = "wasm32")]
EthPrivKeyPolicy::Metamask(_) => MmError::err(RawTransactionError::InvalidParam(
"sign raw eth tx not implemented for Metamask".into(),
)),
EthPrivKeyPolicy::Trezor => MmError::err(RawTransactionError::InvalidParam(
"sign raw eth tx not implemented for Trezor".into(),
)),
}
}

impl EthCoin {
/// Downloads and saves ETH transaction history of my_address, relies on Parity trace_filter API
/// https://wiki.parity.io/JSONRPC-trace-module#trace_filter, this requires tracing to be enabled
Expand Down
27 changes: 24 additions & 3 deletions mm2src/coins/eth/eth_wasm_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ async fn test_send() {
console::log_1(&format!("{:?}", block).into());
}

#[wasm_bindgen_test]
async fn test_init_eth_coin() {
async fn init_eth_coin_helper() -> Result<(MmArc, MmCoinEnum), String> {
let conf = json!({
"coins": [{
"coin": "ETH",
Expand All @@ -96,5 +95,27 @@ async fn test_init_eth_coin() {
"urls":[ETH_DEV_NODE],
"swap_contract_address":ETH_DEV_SWAP_CONTRACT
});
let _coin = lp_coininit(&ctx, "ETH", &req).await.unwrap();
Ok((ctx.clone(), lp_coininit(&ctx, "ETH", &req).await?))
}

#[wasm_bindgen_test]
async fn test_init_eth_coin() { let (_ctx, _coin) = init_eth_coin_helper().await.unwrap(); }

#[wasm_bindgen_test]
async fn wasm_test_sign_eth_tx() {
// we need to hold ref to _ctx until the end of the test (because of the weak ref to MmCtx in EthCoinImpl)
let (_ctx, coin) = init_eth_coin_helper().await.unwrap();
let sign_req = json::from_value(json!({
"coin": "ETH",
"type": "ETH",
"tx": {
"to": "0x7Bc1bBDD6A0a722fC9bffC49c921B685ECB84b94".to_string(),
"value": "1.234",
"gas_limit": "21000"
}
}))
.unwrap();
let res = coin.sign_raw_tx(&sign_req).await;
console::log_1(&format!("res={:?}", res).into());
assert!(res.is_ok());
}
28 changes: 18 additions & 10 deletions mm2src/coins/lightning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ use crate::utxo::{sat_from_big_decimal, utxo_common, BlockchainNetwork};
use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, ConfirmPaymentInput, DexFee,
FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MakerSwapTakerCoin, MarketCoinOps, MmCoin, MmCoinEnum,
NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr,
RawTransactionError, RawTransactionFut, RawTransactionRequest, RefundError, RefundPaymentArgs,
RefundResult, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs,
SignatureError, SignatureResult, SpendPaymentArgs, SwapOps, TakerSwapMakerCoin, TradeFee,
TradePreimageFut, TradePreimageResult, TradePreimageValue, Transaction, TransactionEnum, TransactionErr,
TransactionFut, TransactionResult, TxMarshalingErr, UnexpectedDerivationMethod, UtxoStandardCoin,
ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr,
ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, ValidateWatcherSpendInput,
VerificationError, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward,
WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput,
WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest};
RawTransactionError, RawTransactionFut, RawTransactionRequest, RawTransactionResult, RefundError,
RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput,
SendPaymentArgs, SignRawTransactionRequest, SignatureError, SignatureResult, SpendPaymentArgs, SwapOps,
TakerSwapMakerCoin, TradeFee, TradePreimageFut, TradePreimageResult, TradePreimageValue, Transaction,
TransactionEnum, TransactionErr, TransactionFut, TransactionResult, TxMarshalingErr,
UnexpectedDerivationMethod, UtxoStandardCoin, ValidateAddressResult, ValidateFeeArgs,
ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut,
ValidatePaymentInput, ValidateWatcherSpendInput, VerificationError, VerificationResult,
WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput,
WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest};
use async_trait::async_trait;
use bitcoin::bech32::ToBase32;
use bitcoin::hashes::Hash;
Expand Down Expand Up @@ -1028,6 +1028,7 @@ impl WatcherOps for LightningCoin {
}
}

#[async_trait]
impl MarketCoinOps for LightningCoin {
fn ticker(&self) -> &str { &self.conf.ticker }

Expand Down Expand Up @@ -1106,6 +1107,13 @@ impl MarketCoinOps for LightningCoin {
))
}

#[inline(always)]
async fn sign_raw_tx(&self, _args: &SignRawTransactionRequest) -> RawTransactionResult {
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
7 changes: 5 additions & 2 deletions mm2src/coins/lightning/ln_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use super::*;
use crate::lightning::ln_db::{DBChannelDetails, HTLCStatus, LightningDB, PaymentType};
use crate::lightning::ln_errors::{SaveChannelClosingError, SaveChannelClosingResult};
use crate::lightning::ln_sql::SqliteLightningDB;
use crate::utxo::UtxoCommonOps;
use bitcoin::blockdata::script::Script;
use bitcoin::blockdata::transaction::Transaction;
use bitcoin::consensus::encode::serialize_hex;
Expand Down Expand Up @@ -219,7 +220,9 @@ fn sign_funding_transaction(
.activated_key_or_err()
.map_err(|e| SignFundingTransactionError::Internal(e.to_string()))?;

let prev_script = Builder::build_p2pkh(&my_address.hash);
let prev_script = coin
.script_for_address(my_address)
.map_err(|e| SignFundingTransactionError::Internal(e.to_string()))?;
let signed = sign_tx(
unsigned,
key_pair,
Expand Down Expand Up @@ -529,7 +532,7 @@ impl LightningEventHandler {
let keys_manager = self.keys_manager.clone();

let fut = async move {
let change_destination_script = Builder::build_witness_script(&my_address.hash).to_bytes().take().into();
let change_destination_script = Builder::build_p2witness(&my_address.hash).to_bytes().take().into();
let feerate_sat_per_1000_weight = platform.get_est_sat_per_1000_weight(ConfirmationTarget::Normal);
let output_descriptors = outputs.iter().collect::<Vec<_>>();
let claiming_tx = match keys_manager.spend_spendable_outputs(
Expand Down
Loading
Loading