-
Notifications
You must be signed in to change notification settings - Fork 94
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
[r2r] MetaMask PoC #1551
[r2r] MetaMask PoC #1551
Changes from 6 commits
2761a7c
f32a6df
203f415
3180771
dfe7915
cb4aaba
e357096
bf3fa88
3ef732e
82020b1
3e162b5
6e39a11
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,9 @@ use common::executor::{abortable_queue::AbortableQueue, AbortableSystem, Timer}; | |
use common::log::{error, info, warn}; | ||
use common::{get_utc_timestamp, now_ms, small_rng, DEX_FEE_ADDR_RAW_PUBKEY}; | ||
use crypto::privkey::key_pair_from_secret; | ||
use crypto::{CryptoCtx, CryptoCtxError, GlobalHDAccountArc, KeyPairPolicy}; | ||
#[cfg(target_arch = "wasm32")] | ||
use crypto::{MetamaskArc, MetamaskWeak}; | ||
use derive_more::Display; | ||
use ethabi::{Contract, Token}; | ||
pub use ethcore_transaction::SignedTransaction as SignedEthTx; | ||
|
@@ -49,6 +52,8 @@ use serde_json::{self as json, Value as Json}; | |
use serialization::{CompactInteger, Serializable, Stream}; | ||
use sha3::{Digest, Keccak256}; | ||
use std::collections::HashMap; | ||
use std::convert::TryFrom; | ||
use std::fmt; | ||
use std::ops::Deref; | ||
use std::path::PathBuf; | ||
use std::str::FromStr; | ||
|
@@ -57,30 +62,31 @@ use std::sync::{Arc, Mutex}; | |
use web3::types::{Action as TraceAction, BlockId, BlockNumber, Bytes, CallRequest, FilterBuilder, Log, Trace, | ||
TraceFilterBuilder, Transaction as Web3Transaction, TransactionId}; | ||
use web3::{self, Web3}; | ||
use web3_transport::{EthFeeHistoryNamespace, Web3Transport, Web3TransportNode}; | ||
use web3_transport::{http_transport::HttpTransportNode, EthFeeHistoryNamespace, Web3Transport}; | ||
|
||
use super::{coin_conf, AsyncMutex, BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, | ||
CoinProtocol, CoinTransportMetrics, CoinsContext, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, | ||
MarketCoinOps, MmCoin, MyAddressError, NegotiateSwapContractAddrErr, NumConversError, NumConversResult, | ||
PaymentInstructions, PaymentInstructionsErr, PrivKeyBuildPolicy, PrivKeyPolicyNotAllowed, | ||
RawTransactionError, RawTransactionFut, RawTransactionRequest, RawTransactionRes, RawTransactionResult, | ||
RpcClientType, RpcTransportEventHandler, RpcTransportEventHandlerShared, SearchForSwapTxSpendInput, | ||
SendMakerPaymentArgs, SendMakerRefundsPaymentArgs, SendMakerSpendsTakerPaymentArgs, SendTakerPaymentArgs, | ||
SendTakerRefundsPaymentArgs, SendTakerSpendsMakerPaymentArgs, SignatureError, SignatureResult, SwapOps, | ||
TradeFee, TradePreimageError, TradePreimageFut, TradePreimageResult, TradePreimageValue, Transaction, | ||
TransactionDetails, TransactionEnum, TransactionErr, TransactionFut, TxMarshalingErr, | ||
UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, | ||
ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationError, | ||
VerificationResult, WatcherOps, WatcherValidatePaymentInput, WithdrawError, WithdrawFee, WithdrawFut, | ||
WithdrawRequest, WithdrawResult}; | ||
IguanaPrivKey, MarketCoinOps, MmCoin, MyAddressError, NegotiateSwapContractAddrErr, NumConversError, | ||
NumConversResult, PaymentInstructions, PaymentInstructionsErr, PrivKeyBuildPolicy, | ||
PrivKeyPolicyNotAllowed, RawTransactionError, RawTransactionFut, RawTransactionRequest, RawTransactionRes, | ||
RawTransactionResult, RpcClientType, RpcTransportEventHandler, RpcTransportEventHandlerShared, | ||
SearchForSwapTxSpendInput, SendMakerPaymentArgs, SendMakerRefundsPaymentArgs, | ||
SendMakerSpendsTakerPaymentArgs, SendTakerPaymentArgs, SendTakerRefundsPaymentArgs, | ||
SendTakerSpendsMakerPaymentArgs, SignatureError, SignatureResult, SwapOps, TradeFee, TradePreimageError, | ||
TradePreimageFut, TradePreimageResult, TradePreimageValue, Transaction, TransactionDetails, | ||
TransactionEnum, TransactionErr, TransactionFut, TxMarshalingErr, UnexpectedDerivationMethod, | ||
ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, | ||
ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, VerificationError, VerificationResult, | ||
WatcherOps, WatcherValidatePaymentInput, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, | ||
WithdrawResult}; | ||
pub use rlp; | ||
|
||
#[cfg(test)] mod eth_tests; | ||
#[cfg(target_arch = "wasm32")] mod eth_wasm_tests; | ||
mod web3_transport; | ||
|
||
#[path = "eth/v2_activation.rs"] pub mod v2_activation; | ||
use v2_activation::key_pair_from_priv_key_policy; | ||
use v2_activation::build_address_and_priv_key_policy; | ||
|
||
/// https://github.com/artemii235/etomic-swap/blob/master/contracts/EtomicSwap.sol | ||
/// Dev chain (195.201.0.6:8565) contract address: 0xa09ad3cd7e96586ebd05a2607ee56b56fb2db8fd | ||
|
@@ -293,11 +299,94 @@ pub enum EthCoinType { | |
Erc20 { platform: String, token_addr: Address }, | ||
} | ||
|
||
/// An alternative to `crate::PrivKeyBuildPolicy`, typical only for ETH coin. | ||
pub enum EthPrivKeyBuildPolicy { | ||
IguanaPrivKey(IguanaPrivKey), | ||
GlobalHDAccount(GlobalHDAccountArc), | ||
#[cfg(target_arch = "wasm32")] | ||
Metamask(MetamaskArc), | ||
} | ||
|
||
impl EthPrivKeyBuildPolicy { | ||
/// Detects the `EthPrivKeyBuildPolicy` with which the given `MmArc` is initialized. | ||
pub fn detect_priv_key_policy(ctx: &MmArc) -> MmResult<EthPrivKeyBuildPolicy, CryptoCtxError> { | ||
let crypto_ctx = CryptoCtx::from_ctx(ctx)?; | ||
|
||
match crypto_ctx.key_pair_policy() { | ||
KeyPairPolicy::Iguana => { | ||
// Use an internal private key as the coin secret. | ||
let priv_key = crypto_ctx.mm2_internal_privkey_secret(); | ||
Ok(EthPrivKeyBuildPolicy::IguanaPrivKey(priv_key)) | ||
}, | ||
KeyPairPolicy::GlobalHDAccount(global_hd) => Ok(EthPrivKeyBuildPolicy::GlobalHDAccount(global_hd.clone())), | ||
} | ||
} | ||
|
||
pub fn is_metamask(&self) -> bool { | ||
#[cfg(not(target_arch = "wasm32"))] | ||
return false; | ||
|
||
#[cfg(target_arch = "wasm32")] | ||
matches!(self, EthPrivKeyBuildPolicy::Metamask(_)) | ||
} | ||
} | ||
|
||
impl TryFrom<PrivKeyBuildPolicy> for EthPrivKeyBuildPolicy { | ||
type Error = PrivKeyPolicyNotAllowed; | ||
|
||
/// Converts `PrivKeyBuildPolicy` to `EthPrivKeyBuildPolicy` | ||
/// taking into account that ETH doesn't support `Trezor` yet. | ||
fn try_from(policy: PrivKeyBuildPolicy) -> Result<Self, Self::Error> { | ||
match policy { | ||
PrivKeyBuildPolicy::IguanaPrivKey(iguana) => Ok(EthPrivKeyBuildPolicy::IguanaPrivKey(iguana)), | ||
PrivKeyBuildPolicy::GlobalHDAccount(global_hd) => Ok(EthPrivKeyBuildPolicy::GlobalHDAccount(global_hd)), | ||
PrivKeyBuildPolicy::Trezor => Err(PrivKeyPolicyNotAllowed::HardwareWalletNotSupported), | ||
} | ||
} | ||
} | ||
|
||
/// An alternative to `crate::PrivKeyPolicy`, typical only for ETH coin. | ||
#[derive(Clone)] | ||
pub enum EthPrivKeyPolicy { | ||
KeyPair(KeyPair), | ||
#[cfg(target_arch = "wasm32")] | ||
Metamask(MetamaskWeak), | ||
} | ||
|
||
impl fmt::Debug for EthPrivKeyPolicy { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
match self { | ||
EthPrivKeyPolicy::KeyPair(_) => write!(f, "KeyPair"), | ||
#[cfg(target_arch = "wasm32")] | ||
EthPrivKeyPolicy::Metamask(_) => write!(f, "Metamask"), | ||
} | ||
} | ||
} | ||
|
||
impl From<KeyPair> for EthPrivKeyPolicy { | ||
fn from(key_pair: KeyPair) -> Self { EthPrivKeyPolicy::KeyPair(key_pair) } | ||
} | ||
|
||
impl EthPrivKeyPolicy { | ||
pub fn key_pair(&self) -> Option<&KeyPair> { | ||
match self { | ||
EthPrivKeyPolicy::KeyPair(key_pair) => Some(key_pair), | ||
#[cfg(target_arch = "wasm32")] | ||
EthPrivKeyPolicy::Metamask(_) => None, | ||
} | ||
} | ||
|
||
pub fn key_pair_or_err(&self) -> MmResult<&KeyPair, PrivKeyPolicyNotAllowed> { | ||
self.key_pair() | ||
.or_mm_err(|| PrivKeyPolicyNotAllowed::HardwareWalletNotSupported) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I repeated the same methods from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think it's also fine. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
} | ||
|
||
/// pImpl idiom. | ||
pub struct EthCoinImpl { | ||
ticker: String, | ||
coin_type: EthCoinType, | ||
key_pair: KeyPair, | ||
priv_key_policy: EthPrivKeyPolicy, | ||
my_address: Address, | ||
sign_message_prefix: Option<String>, | ||
swap_contract_address: Address, | ||
|
@@ -680,8 +769,10 @@ async fn withdraw_impl(coin: EthCoin, req: WithdrawRequest) -> WithdrawResult { | |
gas_price, | ||
}; | ||
|
||
let signed = tx.sign(coin.key_pair.secret(), coin.chain_id); | ||
let secret = coin.priv_key_policy.key_pair_or_err()?.secret(); | ||
let signed = tx.sign(secret, coin.chain_id); | ||
let bytes = rlp::encode(&signed); | ||
|
||
let amount_decimal = u256_to_big_decimal(wei_amount, coin.decimals)?; | ||
let mut spent_by_me = amount_decimal.clone(); | ||
let received_by_me = if to_addr == coin.my_address { | ||
|
@@ -1100,7 +1191,13 @@ impl SwapOps for EthCoin { | |
|
||
#[inline] | ||
fn derive_htlc_key_pair(&self, _swap_unique_data: &[u8]) -> keys::KeyPair { | ||
key_pair_from_secret(self.key_pair.secret()).expect("valid key") | ||
#[allow(clippy::infallible_destructuring_match)] | ||
let key_pair = match self.priv_key_policy { | ||
EthPrivKeyPolicy::KeyPair(ref key_pair) => key_pair, | ||
#[cfg(target_arch = "wasm32")] | ||
EthPrivKeyPolicy::Metamask(_) => todo!(), | ||
}; | ||
key_pair_from_secret(key_pair.secret()).expect("valid key") | ||
} | ||
|
||
fn validate_other_pubkey(&self, raw_pubkey: &[u8]) -> MmResult<(), ValidateOtherPubKeyErr> { | ||
|
@@ -1181,7 +1278,15 @@ impl MarketCoinOps for EthCoin { | |
} | ||
|
||
fn get_public_key(&self) -> Result<String, MmError<UnexpectedDerivationMethod>> { | ||
let uncompressed_without_prefix = hex::encode(self.key_pair.public()); | ||
#[allow(clippy::infallible_destructuring_match)] | ||
let key_pair = match self.priv_key_policy { | ||
EthPrivKeyPolicy::KeyPair(ref key_pair) => key_pair, | ||
// Return a default pubkey for a while. | ||
// TODO return a pubkey extracted from `Login to AtomicDEX` signature. | ||
#[cfg(target_arch = "wasm32")] | ||
EthPrivKeyPolicy::Metamask(_) => return Ok("NOT SUPPORTED YET".to_string()), | ||
}; | ||
let uncompressed_without_prefix = hex::encode(key_pair.public()); | ||
Ok(format!("04{}", uncompressed_without_prefix)) | ||
} | ||
|
||
|
@@ -1201,7 +1306,7 @@ impl MarketCoinOps for EthCoin { | |
|
||
fn sign_message(&self, message: &str) -> SignatureResult<String> { | ||
let message_hash = self.sign_message_hash(message).ok_or(SignatureError::PrefixNotFound)?; | ||
let privkey = &self.key_pair.secret(); | ||
let privkey = &self.priv_key_policy.key_pair_or_err()?.secret(); | ||
let signature = sign(privkey, &H256::from(message_hash))?; | ||
Ok(format!("0x{}", signature)) | ||
} | ||
|
@@ -1459,7 +1564,13 @@ impl MarketCoinOps for EthCoin { | |
) | ||
} | ||
|
||
fn display_priv_key(&self) -> Result<String, String> { Ok(format!("{:#02x}", self.key_pair.secret())) } | ||
fn display_priv_key(&self) -> Result<String, String> { | ||
match self.priv_key_policy { | ||
EthPrivKeyPolicy::KeyPair(ref key_pair) => Ok(format!("{:#02x}", key_pair.secret())), | ||
#[cfg(target_arch = "wasm32")] | ||
EthPrivKeyPolicy::Metamask(_) => ERR!("'display_priv_key' doesn't support MetaMask"), | ||
} | ||
onur-ozkan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
fn min_tx_amount(&self) -> BigDecimal { BigDecimal::from(0) } | ||
|
||
|
@@ -1514,7 +1625,8 @@ async fn sign_and_send_transaction_impl( | |
value, | ||
data, | ||
}; | ||
let signed = tx.sign(coin.key_pair.secret(), coin.chain_id); | ||
let key_pair = try_tx_s!(coin.priv_key_policy.key_pair_or_err()); | ||
let signed = tx.sign(key_pair.secret(), coin.chain_id); | ||
let bytes = web3::types::Bytes(rlp::encode(&signed).to_vec()); | ||
status.status(tags!(), "send_raw_transaction…"); | ||
|
||
|
@@ -3583,6 +3695,9 @@ pub async fn eth_coin_from_conf_and_request( | |
protocol: CoinProtocol, | ||
priv_key_policy: PrivKeyBuildPolicy, | ||
) -> Result<EthCoin, String> { | ||
// Convert `PrivKeyBuildPolicy` to `EthPrivKeyBuildPolicy` if it's possible. | ||
let priv_key_policy = try_s!(EthPrivKeyBuildPolicy::try_from(priv_key_policy)); | ||
|
||
let mut urls: Vec<String> = try_s!(json::from_value(req["urls"].clone())); | ||
if urls.is_empty() { | ||
return ERR!("Enable request for ETH coin must have at least 1 node URL"); | ||
|
@@ -3592,7 +3707,7 @@ pub async fn eth_coin_from_conf_and_request( | |
|
||
let mut nodes = vec![]; | ||
for url in urls.iter() { | ||
nodes.push(Web3TransportNode { | ||
nodes.push(HttpTransportNode { | ||
uri: try_s!(url.parse()), | ||
gui_auth: false, | ||
}); | ||
|
@@ -3611,13 +3726,12 @@ pub async fn eth_coin_from_conf_and_request( | |
} | ||
} | ||
|
||
let key_pair = try_s!(key_pair_from_priv_key_policy(conf, priv_key_policy)); | ||
let my_address = key_pair.address(); | ||
let (my_address, key_pair) = try_s!(build_address_and_priv_key_policy(conf, priv_key_policy)); | ||
|
||
let mut web3_instances = vec![]; | ||
let event_handlers = rpc_event_handlers_for_eth_transport(ctx, ticker.to_string()); | ||
for node in nodes.iter() { | ||
let transport = Web3Transport::with_event_handlers(vec![node.clone()], event_handlers.clone()); | ||
let transport = Web3Transport::new_http(vec![node.clone()], event_handlers.clone()); | ||
let web3 = Web3::new(transport); | ||
let version = match web3.web3().client_version().compat().await { | ||
Ok(v) => v, | ||
|
@@ -3636,7 +3750,7 @@ pub async fn eth_coin_from_conf_and_request( | |
return ERR!("Failed to get client version for all urls"); | ||
} | ||
|
||
let transport = Web3Transport::with_event_handlers(nodes, event_handlers); | ||
let transport = Web3Transport::new_http(nodes, event_handlers); | ||
let web3 = Web3::new(transport); | ||
|
||
let (coin_type, decimals) = match protocol { | ||
|
@@ -3695,7 +3809,7 @@ pub async fn eth_coin_from_conf_and_request( | |
let abortable_system = try_s!(ctx.abortable_system.create_subsystem()); | ||
|
||
let coin = EthCoinImpl { | ||
key_pair, | ||
priv_key_policy: key_pair, | ||
my_address, | ||
coin_type, | ||
sign_message_prefix, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't used. Should we keep it implemented?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, looks like it used to be used before. Will remove it, thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done