Skip to content

Commit

Permalink
improve wc tx_signing codes
Browse files Browse the repository at this point in the history
  • Loading branch information
borngraced committed Oct 9, 2024
1 parent 8d749de commit 8b1b6c3
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 89 deletions.
162 changes: 101 additions & 61 deletions mm2src/coins/tendermint/tendermint_coin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,14 @@ impl TendermintCoin {
)
},
TendermintActivationPolicy::PublicKey(_) => {
if let TendermintWalletConnectionType::WalletConnect = self.wallet_connection_type {
return try_tx_s!(
self.seq_safe_send_raw_tx_bytes(tx_payload, fee, timeout_height, memo)
.timeout(expiration)
.await
);
};

try_tx_s!(
self.send_unsigned_tx_externally(tx_payload, fee, timeout_height, memo, expiration)
.timeout(expiration)
Expand All @@ -870,6 +878,58 @@ impl TendermintCoin {
}
}

async fn request_wc_tx_signing(&self, tx_json: serde_json::Value) -> Result<Raw, TransactionErr> {
let ctx = try_tx_s!(MmArc::from_weak(&self.ctx).ok_or(ERRL!("ctx must be initialized already")));
let wallet_connect = try_tx_s!(WalletConnectCtx::from_ctx(&ctx).map_err(|e| ERRL!("{}", e)));

let response = try_tx_s!(
wallet_connect
.cosmos_send_sign_tx_request(tx_json, self.chain_id.as_ref())
.await
);
let signature = try_tx_s!(general_purpose::STANDARD
.decode(response.signature.signature)
.map_err(|e| ERRL!("{}", e)));

Ok(TxRaw {
body_bytes: response.signed.body_bytes,
auth_info_bytes: response.signed.auth_info_bytes,
signatures: vec![signature],
}
.into())
}

async fn get_tx_raw(
&self,
account_info: &BaseAccount,
tx_payload: Any,
fee: Fee,
timeout_height: u64,
memo: String,
) -> Result<Raw, TransactionErr> {
match self.wallet_connection_type {
TendermintWalletConnectionType::WalletConnect => {
// Handle WalletConnect signing
let SerializedUnsignedTx { tx_json, body_bytes: _ } =
try_tx_s!(self.any_to_serialized_sign_doc(&account_info, tx_payload, fee, timeout_height, memo));

self.request_wc_tx_signing(tx_json).await
},
_ => {
// Handle local signing
let tx_raw = try_tx_s!(self.any_to_signed_raw_tx(
try_tx_s!(self.activation_policy.activated_key_or_err()),
&account_info,
tx_payload,
fee,
timeout_height,
memo,
));
Ok(tx_raw)
},
}
}

async fn seq_safe_send_raw_tx_bytes(
&self,
tx_payload: Any,
Expand All @@ -878,31 +938,36 @@ impl TendermintCoin {
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()),
&account_info,
tx_payload.clone(),
fee.clone(),
timeout_height,
memo.clone(),
));

match self.send_raw_tx_bytes(&try_tx_s!(tx_raw.to_bytes())).compat().await {
Ok(tx_id) => break (tx_id, tx_raw),
loop {
let tx_raw = try_tx_s!(
self.get_tx_raw(
&account_info,
tx_payload.clone(),
fee.clone(),
timeout_height,
memo.clone(),
)
.await
);

// Attempt to send the transaction bytes
match self.send_raw_tx_bytes(try_tx_s!(&tx_raw.to_bytes())).compat().await {
Ok(tx_id) => {
return Ok((tx_id, tx_raw));
},
Err(e) => {
// Handle sequence number mismatch and retry
if e.contains(ACCOUNT_SEQUENCE_ERR) {
account_info.sequence = try_tx_s!(parse_expected_sequence_number(&e));
debug!("Got wrong account sequence, trying again.");
debug!("Account sequence mismatch, retrying...");
continue;
}

return Err(crate::TransactionErr::Plain(ERRL!("{}", e)));
return Err(TransactionErr::Plain(ERRL!("Transaction failed: {}", e)));
},
};
};

Ok((tx_id, tx_raw))
}
}
}

async fn send_unsigned_tx_externally(
Expand Down Expand Up @@ -932,37 +997,12 @@ impl TendermintCoin {
.await
.map_err(|e| ERRL!("{}", e)));

let tx_raw = match self.wallet_connection_type {
TendermintWalletConnectionType::WalletConnect => {
let wallet_connect = try_tx_s!(WalletConnectCtx::from_ctx(&ctx).map_err(|e| ERRL!("{}", e)));
let response = try_tx_s!(
wallet_connect
.cosmos_send_sign_tx_request(tx_json, self.chain_id.as_ref())
.await
);
let signature = try_tx_s!(general_purpose::STANDARD
.decode(response.signature.signature)
.map_err(|e| ERRL!("{}", e)));
let body_bytes = try_tx_s!(general_purpose::STANDARD
.decode(response.signed.body_bytes)
.map_err(|e| ERRL!("{}", e)));
let auth_info_bytes = try_tx_s!(general_purpose::STANDARD
.decode(response.signed.auth_info_bytes)
.map_err(|e| ERRL!("{}", e)));
TxRaw {
body_bytes,
auth_info_bytes,
signatures: vec![signature],
}
},
_ => {
let tx = try_tx_s!(self.request_tx(data.hash.clone()).await.map_err(|e| ERRL!("{}", e)));
TxRaw {
body_bytes: tx.body.as_ref().map(Message::encode_to_vec).unwrap_or_default(),
auth_info_bytes: tx.auth_info.as_ref().map(Message::encode_to_vec).unwrap_or_default(),
signatures: tx.signatures,
}
},
let tx = try_tx_s!(self.request_tx(data.hash.clone()).await.map_err(|e| ERRL!("{}", e)));

let tx_raw = TxRaw {
body_bytes: tx.body.as_ref().map(Message::encode_to_vec).unwrap_or_default(),
auth_info_bytes: tx.auth_info.as_ref().map(Message::encode_to_vec).unwrap_or_default(),
signatures: tx.signatures,
};

if body_bytes != tx_raw.body_bytes {
Expand Down Expand Up @@ -1368,9 +1408,18 @@ impl TendermintCoin {
let sign_doc = SignDoc::new(&tx_body, &auth_info, &self.chain_id, account_info.account_number)?;

let my_address = self.my_address().unwrap();
let tx_json = if self.wallet_connection_type == TendermintWalletConnectionType::WalletConnect {
//todo:: maybe convert body_bytes, auth_info_bytes to base64.
json!({
let mut tx_json = json!({
"sign_doc": {
"body_bytes": &sign_doc.body_bytes,
"auth_info_bytes": sign_doc.auth_info_bytes,
"chain_id": sign_doc.chain_id,
"account_number": sign_doc.account_number,
}
});

// if wallet_connection_type is WalletConnect, update tx_json to use WalletConnect type.
if self.wallet_connection_type == TendermintWalletConnectionType::WalletConnect {
tx_json = json!({
"signerAddress": my_address,
"signDoc": {
"accountNumber": sign_doc.account_number.to_string(),
Expand All @@ -1379,16 +1428,7 @@ impl TendermintCoin {
"authInfoBytes": hex::encode(&sign_doc.auth_info_bytes)
}
})
} else {
json!({
"sign_doc": {
"body_bytes": &sign_doc.body_bytes,
"auth_info_bytes": sign_doc.auth_info_bytes,
"chain_id": sign_doc.chain_id,
"account_number": sign_doc.account_number,
}
})
};
}

Ok(SerializedUnsignedTx {
tx_json,
Expand Down
44 changes: 20 additions & 24 deletions mm2src/kdf_walletconnect/src/chain/tendermint.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use std::collections::HashMap;

use crate::{error::WalletConnectCtxError, WalletConnectCtx};

use base64::{engine::general_purpose, Engine};
use chrono::Utc;
use common::log::info;
use futures::StreamExt;
use mm2_err_handle::prelude::{MmError, MmResult};
use relay_rpc::rpc::params::{session_request::{Request as SessionRequest, SessionRequestRequest},
RequestParams, ResponseParamsSuccess};
use serde::{Deserialize, Deserializer, Serialize};
use serde::{Deserialize, Serialize};
use serde_json::Value;

use super::WcRequestMethods;
Expand Down Expand Up @@ -60,15 +59,28 @@ where
})
.collect(),
Value::String(data) => {
let data = general_purpose::STANDARD
.decode(data)
.map_err(|err| serde::de::Error::custom(err.to_string()))?;
let data = decode_data(&data).map_err(|err| serde::de::Error::custom(err.to_string()))?;
Ok(data)
},
_ => Err(serde::de::Error::custom("Pubkey must be an string, object or array")),
}
}

fn decode_data(encoded: &str) -> Result<Vec<u8>, &'static str> {
// Check if the string is base64 or hex
if encoded.contains('=') || encoded.contains('/') || encoded.contains('+') {
// Try to decode as base64
general_purpose::STANDARD
.decode(encoded)
.map_err(|_| "Invalid base64 encoding")
} else if encoded.chars().all(|c| c.is_ascii_hexdigit()) && encoded.len() % 2 == 0 {
// Try to decode as hex
hex::decode(encoded).map_err(|_| "Invalid hex encoding")
} else {
Err("Unknown encoding format")
}
}

pub async fn cosmos_get_accounts_impl(
ctx: &WalletConnectCtx,
chain_id: &str,
Expand Down Expand Up @@ -134,7 +146,7 @@ pub struct CosmosTxPublicKey {
#[serde(rename_all = "camelCase")]
pub struct CosmosSignData {
pub chain_id: String,
pub account_number: AccountNumber,
pub account_number: String,
#[serde(deserialize_with = "deserialize_vec_field")]
pub auth_info_bytes: Vec<u8>,
#[serde(deserialize_with = "deserialize_vec_field")]
Expand All @@ -148,7 +160,7 @@ pub struct AccountNumber {
unsigned: bool,
}

pub async fn cosmos_sign_tx_direct_impl(
pub async fn cosmos_sign_direct_impl(
ctx: &WalletConnectCtx,
sign_doc: Value,
chain_id: &str,
Expand Down Expand Up @@ -179,7 +191,6 @@ pub async fn cosmos_sign_tx_direct_impl(

let mut session_handler = ctx.session_request_handler.lock().await;
if let Some((message_id, data)) = session_handler.next().await {
println!("DATA: {data:?}");
let result = serde_json::from_value::<CosmosTxSignedData>(data)?;
let response = ResponseParamsSuccess::SessionEvent(true);
ctx.publish_response_ok(&topic, response, &message_id).await?;
Expand All @@ -191,18 +202,3 @@ pub async fn cosmos_sign_tx_direct_impl(
"No response from wallet".to_string(),
))
}

//{
// "id": 1,
// "jsonrpc": "2.0",
// "method": "cosmos_signDirect",
// "params": {
// "signerAddress": "cosmos1sguafvgmel6f880ryvq8efh9522p8zvmrzlcrq",
// "signDoc": {
// "chainId": "cosmoshub-4",
// "accountNumber": "1"
// "authInfoBytes": "CgoKABIECgIIARgBEhMKDQoFdWNvc20SBDIwMDAQwJoM",
// "bodyBytes": "CpABChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnAKLWNvc21vczFwa3B0cmU3ZmRrbDZnZnJ6bGVzamp2aHhobGMzcjRnbW1rOHJzNhItY29zbW9zMXF5cHF4cHE5cWNyc3N6ZzJwdnhxNnJzMHpxZzN5eWM1bHp2N3h1GhAKBXVjb3NtEgcxMjM0NTY3"
// }
// }
// }
6 changes: 3 additions & 3 deletions mm2src/kdf_walletconnect/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub mod session;
mod storage;

use chain::{build_required_namespaces,
tendermint::{cosmos_get_accounts_impl, cosmos_sign_tx_direct_impl, CosmosAccount, CosmosTxSignedData},
tendermint::{cosmos_get_accounts_impl, cosmos_sign_direct_impl, CosmosAccount, CosmosTxSignedData},
SUPPORTED_CHAINS};
use common::executor::SpawnFuture;
use common::log::info;
Expand Down Expand Up @@ -233,7 +233,7 @@ impl WalletConnectCtx {
sign_doc: Value,
chain_id: &str,
) -> MmResult<CosmosTxSignedData, WalletConnectCtxError> {
cosmos_sign_tx_direct_impl(self, sign_doc, chain_id).await
cosmos_sign_direct_impl(self, sign_doc, chain_id).await
}

async fn sym_key(&self, topic: &Topic) -> MmResult<Vec<u8>, WalletConnectCtxError> {
Expand Down Expand Up @@ -262,7 +262,7 @@ impl WalletConnectCtx {
self.publish_payload(topic, irn_metadata, Payload::Request(request))
.await?;

info!("Outbound request sent!\n");
deb!("Outbound request sent!\n");

Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion mm2src/kdf_walletconnect/src/session/rpc/propose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use mm2_err_handle::prelude::*;
use relay_rpc::{domain::{MessageId, Topic},
rpc::params::{session::ProposeNamespaces,
session_propose::{Proposer, SessionProposeRequest, SessionProposeResponse},
Metadata, RequestParams, ResponseParamsSuccess}};
RequestParams, ResponseParamsSuccess}};

/// Creates a new session proposal form topic and metadata.
pub(crate) async fn send_proposal_request(
Expand Down

0 comments on commit 8b1b6c3

Please sign in to comment.