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(tendermint): implement better sequence resolving logic #2164

Merged
merged 5 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
72 changes: 56 additions & 16 deletions mm2src/coins/tendermint/tendermint_coin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ use mm2_git::{FileMetadata, GitController, GithubClient, RepositoryOperations, G
use mm2_number::MmNumber;
use parking_lot::Mutex as PaMutex;
use primitives::hash::H256;
use regex::Regex;
use rpc::v1::types::Bytes as BytesJson;
use serde_json::{self as json, Value as Json};
use std::collections::HashMap;
Expand Down Expand Up @@ -105,7 +106,11 @@ pub(crate) const TX_DEFAULT_MEMO: &str = "";
const MAX_TIME_LOCK: i64 = 34560;
const MIN_TIME_LOCK: i64 = 50;

const ACCOUNT_SEQUENCE_ERR: &str = "incorrect account sequence";
const ACCOUNT_SEQUENCE_ERR: &str = "account sequence mismatch";

lazy_static! {
static ref SEQUENCE_PARSER_REGEX: Regex = Regex::new(r"expected (\d+)").unwrap();
}

pub struct SerializedUnsignedTx {
tx_json: Json,
Expand Down Expand Up @@ -748,7 +753,7 @@ impl TendermintCoin {
// Therefore, we can call SimulateRequest or CheckTx(doesn't work with using Abci interface) to get used gas or fee itself.
pub(super) fn gen_simulated_tx(
&self,
account_info: BaseAccount,
account_info: &BaseAccount,
priv_key: &Secp256k1Secret,
tx_payload: Any,
timeout_height: u64,
Expand Down Expand Up @@ -835,10 +840,11 @@ impl TendermintCoin {
timeout_height: u64,
memo: String,
) -> Result<(String, Raw), TransactionErr> {
let mut account_info = try_tx_s!(self.account_info(&self.account_id).await);
let (tx_id, tx_raw) = loop {
let tx_raw = try_tx_s!(self.any_to_signed_raw_tx(
try_tx_s!(self.activation_policy.activated_key_or_err()),
try_tx_s!(self.account_info(&self.account_id).await),
&account_info,
tx_payload.clone(),
fee.clone(),
timeout_height,
Expand All @@ -849,6 +855,7 @@ impl TendermintCoin {
Ok(tx_id) => break (tx_id, tx_raw),
Err(e) => {
if e.contains(ACCOUNT_SEQUENCE_ERR) {
account_info.sequence = try_tx_s!(parse_expected_sequence_number(&e));
debug!("Got wrong account sequence, trying again.");
continue;
}
Expand Down Expand Up @@ -878,9 +885,9 @@ impl TendermintCoin {

let account_info = try_tx_s!(self.account_info(&self.account_id).await);
let SerializedUnsignedTx { tx_json, body_bytes } = if self.is_keplr_from_ledger {
try_tx_s!(self.any_to_legacy_amino_json(account_info, tx_payload, fee, timeout_height, memo))
try_tx_s!(self.any_to_legacy_amino_json(&account_info, tx_payload, fee, timeout_height, memo))
} else {
try_tx_s!(self.any_to_serialized_sign_doc(account_info, tx_payload, fee, timeout_height, memo))
try_tx_s!(self.any_to_serialized_sign_doc(&account_info, tx_payload, fee, timeout_height, memo))
};

let data: TxHashData = try_tx_s!(ctx
Expand Down Expand Up @@ -925,11 +932,11 @@ impl TendermintCoin {
return Ok(Fee::from_amount_and_gas(fee_amount, gas_limit));
};

let mut account_info = self.account_info(&self.account_id).await?;
let (response, raw_response) = loop {
let account_info = self.account_info(&self.account_id).await?;
let tx_bytes = self
.gen_simulated_tx(
account_info,
&account_info,
activated_priv_key,
msg.clone(),
timeout_height,
Expand All @@ -946,7 +953,9 @@ impl TendermintCoin {

let raw_response = self.rpc_client().await?.perform(request).await?;

if raw_response.response.log.to_string().contains(ACCOUNT_SEQUENCE_ERR) {
let log = raw_response.response.log.to_string();
if log.contains(ACCOUNT_SEQUENCE_ERR) {
account_info.sequence = parse_expected_sequence_number(&log)?;
debug!("Got wrong account sequence, trying again.");
continue;
}
Expand Down Expand Up @@ -1001,10 +1010,10 @@ impl TendermintCoin {
return Ok(((GAS_WANTED_BASE_VALUE * 1.5) * gas_price).ceil() as u64);
};

let mut account_info = self.account_info(account_id).await?;
let (response, raw_response) = loop {
let account_info = self.account_info(account_id).await?;
let tx_bytes = self
.gen_simulated_tx(account_info, &priv_key, msg.clone(), timeout_height, memo.clone())
.gen_simulated_tx(&account_info, &priv_key, msg.clone(), timeout_height, memo.clone())
.map_to_mm(|e| TendermintCoinRpcError::InternalError(format!("{}", e)))?;

let request = AbciRequest::new(
Expand All @@ -1016,7 +1025,9 @@ impl TendermintCoin {

let raw_response = self.rpc_client().await?.perform(request).await?;

if raw_response.response.log.to_string().contains(ACCOUNT_SEQUENCE_ERR) {
let log = raw_response.response.log.to_string();
if log.contains(ACCOUNT_SEQUENCE_ERR) {
account_info.sequence = parse_expected_sequence_number(&log)?;
mariocynicys marked this conversation as resolved.
Show resolved Hide resolved
debug!("Got wrong account sequence, trying again.");
continue;
}
Expand Down Expand Up @@ -1155,7 +1166,7 @@ impl TendermintCoin {
&self,
maybe_pk: Option<H256>,
message: Any,
account_info: BaseAccount,
account_info: &BaseAccount,
fee: Fee,
timeout_height: u64,
memo: String,
Expand Down Expand Up @@ -1239,7 +1250,7 @@ impl TendermintCoin {
pub(super) fn any_to_signed_raw_tx(
&self,
priv_key: &Secp256k1Secret,
account_info: BaseAccount,
account_info: &BaseAccount,
tx_payload: Any,
fee: Fee,
timeout_height: u64,
Expand All @@ -1254,7 +1265,7 @@ impl TendermintCoin {

pub(super) fn any_to_serialized_sign_doc(
&self,
account_info: BaseAccount,
account_info: &BaseAccount,
tx_payload: Any,
fee: Fee,
timeout_height: u64,
Expand Down Expand Up @@ -1286,7 +1297,7 @@ impl TendermintCoin {
/// Visit https://docs.cosmos.network/main/build/architecture/adr-050-sign-mode-textual#context for more context.
pub(super) fn any_to_legacy_amino_json(
&self,
account_info: BaseAccount,
account_info: &BaseAccount,
tx_payload: Any,
fee: Fee,
timeout_height: u64,
Expand Down Expand Up @@ -2264,7 +2275,7 @@ impl MmCoin for TendermintCoin {
let account_info = coin.account_info(&account_id).await?;

let tx = coin
.any_to_transaction_data(maybe_pk, msg_payload, account_info, fee, timeout_height, memo.clone())
.any_to_transaction_data(maybe_pk, msg_payload, &account_info, fee, timeout_height, memo.clone())
.map_to_mm(|e| WithdrawError::InternalError(e.to_string()))?;

let internal_id = {
Expand Down Expand Up @@ -3256,6 +3267,20 @@ pub async fn get_ibc_transfer_channels(
})
}

fn parse_expected_sequence_number(e: &str) -> MmResult<u64, TendermintCoinRpcError> {
if let Some(sequence) = SEQUENCE_PARSER_REGEX.captures(e).and_then(|c| c.get(1)) {
let account_sequence =
u64::from_str(sequence.as_str()).map_to_mm(|e| TendermintCoinRpcError::InternalError(e.to_string()))?;

return Ok(account_sequence);
}

MmError::err(TendermintCoinRpcError::InternalError(format!(
"Could not parse the expected sequence number from this error message: '{}'",
e
)))
}

#[cfg(test)]
pub mod tendermint_coin_tests {
use super::*;
Expand Down Expand Up @@ -4165,4 +4190,19 @@ pub mod tendermint_coin_tests {
// Public and private keys are from the same keypair, account ids must be equal.
assert_eq!(pk_account_id, pb_account_id);
}

#[test]
fn test_parse_expected_sequence_number() {
assert_eq!(
13,
parse_expected_sequence_number("check_tx log: account sequence mismatch, expected 13").unwrap()
);
assert_eq!(
5,
parse_expected_sequence_number("check_tx log: account sequence mismatch, expected 5, got...").unwrap()
);
assert_eq!(17, parse_expected_sequence_number("account sequence mismatch, expected. check_tx log: account sequence mismatch, expected 17, got 16: incorrect account sequence, deliver_tx log...").unwrap());
assert!(parse_expected_sequence_number("").is_err());
assert!(parse_expected_sequence_number("check_tx log: account sequence mismatch, expected").is_err());
}
}
2 changes: 1 addition & 1 deletion mm2src/coins/tendermint/tendermint_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ impl MmCoin for TendermintToken {
let account_info = platform.account_info(&account_id).await?;

let tx = platform
.any_to_transaction_data(maybe_pk, msg_payload, account_info, fee, timeout_height, memo.clone())
.any_to_transaction_data(maybe_pk, msg_payload, &account_info, fee, timeout_height, memo.clone())
.map_to_mm(|e| WithdrawError::InternalError(e.to_string()))?;

let internal_id = {
Expand Down
12 changes: 0 additions & 12 deletions mm2src/mm2_main/tests/docker_tests/tendermint_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -667,21 +667,13 @@ mod swap {
wait_check_stats_swap_status, DOC_ELECTRUM_ADDRS};
use std::convert::TryFrom;
use std::str::FromStr;
use std::sync::Mutex;
use std::{env, thread};

const BOB_PASSPHRASE: &str = "iris test seed";
const ALICE_PASSPHRASE: &str = "iris test2 seed";

lazy_static! {
// Simple lock used for running the swap tests sequentially.
static ref SWAP_LOCK: Mutex<()> = Mutex::new(());
}

#[test]
fn swap_nucleus_with_doc() {
let _lock = SWAP_LOCK.lock().unwrap();

let bob_passphrase = String::from(BOB_PASSPHRASE);
let alice_passphrase = String::from(ALICE_PASSPHRASE);

Expand Down Expand Up @@ -760,8 +752,6 @@ mod swap {

#[test]
fn swap_nucleus_with_eth() {
let _lock = SWAP_LOCK.lock().unwrap();

let bob_passphrase = String::from(BOB_PASSPHRASE);
let alice_passphrase = String::from(ALICE_PASSPHRASE);
const BOB_ETH_ADDRESS: &str = "0x7b338250f990954E3Ab034ccD32a917c2F607C2d";
Expand Down Expand Up @@ -868,8 +858,6 @@ mod swap {

#[test]
fn swap_doc_with_iris_ibc_nucleus() {
let _lock = SWAP_LOCK.lock().unwrap();

let bob_passphrase = String::from(BOB_PASSPHRASE);
let alice_passphrase = String::from(ALICE_PASSPHRASE);

Expand Down
Loading