-
Notifications
You must be signed in to change notification settings - Fork 105
[r2r] UTXO RPC batch requests #1255
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
Conversation
* Refactor `CryptoCtx` to be a structure that contains `IguanaArc` always and a constructible `HwArc` * Add `init_trezor` RPC call that initializes `HwArc` * Rename `KeyPairCtx` to `IguanaCtx` * Comment out `mm_init_task`, `rpc_command` modules, `mm_init_status`, `mm_init_user_action` RPC calls
* Process batch response in the same order in which the requests were sent
* Add `utxo_common_tests.rs`
* Refactor functions within `utxo::tx_cache` to be able to work concurrently * Refactor `UtxoCommonOps::get_verbose_transactions_from_cache_or_rpc` to be able to work concurrently * Remove `UtxoCommonOps::list_all_unspent_ordered`, `UtxoCommonOps::list_mature_unspent_ordered` * Add the `ListUtxoOps` trait and its methods `get_all_unspent_ordered_map`, `get_mature_unspent_ordered_map`, `get_unspent_ordered_map` * Move and rename `UtxoCommonOps::list_unspent_ordered` to `ListUtxoOps::get_unspent_ordered_list`
# Conflicts: # mm2src/coins/hd_pubkey.rs # mm2src/coins/hd_wallet.rs # mm2src/coins/utxo.rs # mm2src/coins/utxo/utxo_builder/utxo_coin_builder.rs # mm2src/coins/utxo/utxo_common.rs # mm2src/coins/utxo/utxo_withdraw.rs # mm2src/crypto/src/crypto_ctx.rs # mm2src/lp_init/init_context.rs # mm2src/lp_init/init_hw.rs # mm2src/lp_native_dex.rs # mm2src/rpc/dispatcher/dispatcher.rs
* Move `account_balance`, `init_withdraw`, `init_create_account` RPCs from `coins` to `coins::rpc_command` * Use `RpcTask` for `init_scan_for_new_addresses`
@@ -212,6 +212,8 @@ mod native_impl { | |||
my_taker_order_file_path, my_taker_orders_dir}; | |||
use common::fs::{read_dir_json, read_json, remove_file_async, write_json, FsJsonError}; | |||
|
|||
const USE_TMP_FILE: bool = false; |
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.
Do we need to use tmp files when save orders?
@@ -176,6 +176,8 @@ mod native_impl { | |||
use crate::mm2::lp_swap::{my_swap_file_path, my_swaps_dir}; | |||
use common::fs::{read_dir_json, read_json, write_json, FsJsonError}; | |||
|
|||
const USE_TMP_FILE: bool = false; |
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.
Same question
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.
Great Work! Just a few minor comments
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.
First review iteration.
mm2src/coins/z_coin.rs
Outdated
@@ -1321,6 +1322,38 @@ impl UtxoTxBroadcastOps for ZCoin { | |||
} | |||
} | |||
|
|||
#[async_trait] | |||
#[cfg_attr(test, mockable)] | |||
impl ListUtxoOps for ZCoin { |
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.
ZCoin is not assumed to work with transparent UTXOs. This trait should not be implemented for it.
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.
Unfortunately, ZCoin::preimage_trade_fee_required_to_send_outputs
is based on utxo_common::preimage_trade_fee_required_to_send_outputs
, that requires ListUtxoOps
to generate a preimage transaction.
https://github.com/KomodoPlatform/atomicDEX-API/blob/778461afccf42c17da0e02394ef6890a092a7a99/mm2src/coins/z_coin.rs#L1425-L1433
So I think we need to implement this method requesting z_unspents instead and then we can get rid of the ListUtxoOps
trait implementation for ZCoin
.
Do you think I need to do it within this PR?
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.
I can refactor it during next iteration of Zcoin implementation. Not required for now.
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.
But I already have a different idea: if only one method from a trait is required to split these implementations, then we can maybe add a trait like TradePreimageUtxoOps
and implement only it for Zcoin
? I didn't dig in the code regarding this, so not sure how feasible it will be. @sergeyboyko0791
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.
Could you please explain more how TradePreimageUtxoOps
can allow us to avoid implementing ListUtxoOps
for ZCoin
? 😅
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.
I need to check this in more details myself. It will require some time to perform this refactoring, so we can leave this as is for now.
* Do not inherit `UtxoCommonOps` from `ListUtxoOps` (due to `ZCoin` support) * Refactor some of `UtxoRpcClientOps`, `utxo_common` methods by getting rid of nesting * Add the `common::custom_iter::CollectInto` trait * Add `ListUtxoOps::get_mature_unspent_ordered_list`
* Reorder RPCs alphabetically
* Fix `from_id` paging option for `account_balance` RPC * Fix WASM build
* Divide `ListUtxoOps` into `GetUtxoListOps` and `GetUtxoMapOps` * Implement `GetUtxoMapOps` for `UtxoStandardCoin` and `QtumCoin` only
* Rename `UtxoVerboseCache` to `FsVerboseCache` * Add `WasmVerboseCache` that is an alias to `DummyVerboseCache`
@artemii235 @shamardy @ozkanonur @caglaryucekaya PR is ready for the review |
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.
Few changes requests and questions.
mm2src/coins/utxo/bch.rs
Outdated
let mut result = BchUnspents::default(); | ||
for unspent in utxos { | ||
if unspent.outpoint.index == 0 { | ||
// zero output is reserved for OP_RETURN of specific protocols | ||
// so if we get it we can safely consider this as standard BCH UTXO | ||
// Zero output is reserved for OP_RETURN of specific protocols |
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.
To avoid duplicated if unspent.outpoint.index == 0
check, we can use something like
let mut result = BchUnspents::default();
let mut temporary_undetermined = Vec::new();
let to_verbose: HashSet<H256Json> = utxos
.into_iter()
.filter_map(|unspent| {
if unspent.outpoint.index == 0 {
// Zero output is reserved for OP_RETURN of specific protocols
// so if we get it we can safely consider this as standard BCH UTXO.
// There is no need to request verbose transaction for such UTXO.
result.add_standard(unspent);
None
} else {
let hash = unspent.outpoint.hash.reversed().into();
temporary_undetermined.push(unspent);
Some(hash)
}
})
.collect();
let verbose_txs = self
.get_verbose_transactions_from_cache_or_rpc(to_verbose)
.compat()
.await?;
for unspent in temporary_undetermined {
...
}
What do you think?
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.
I tried to do something like this, but couldn't do it nicely. Will change 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
mm2src/common/jsonrpc_client.rs
Outdated
T: DeserializeOwned + Send + 'static; | ||
} | ||
|
||
impl<I> BatchRequest for I |
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.
It looks like the iterator is sending actual requests while it's responsibility of rpc_client
. It can also be confused with combinators like map/filter/etc.
. So the interface should be as follows:
fn batch_rpc<Rpc, T>(&self (Rpc), requests: I) -> RpcRes<Vec<T>>
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.
Yes, this makes sense! I wanted to use the trait this way:
tx_ids
.iter()
.map(|txid| rpc_req!(self, "getrawtransaction", txid, verbose))
.batch_rpc(self)
But I agree that it might be confusing.
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
@@ -124,6 +130,8 @@ const DEFAULT_GAP_LIMIT: u32 = 20; | |||
|
|||
pub type GenerateTxResult = Result<(TransactionInputSigner, AdditionalTxData), MmError<GenerateTxError>>; | |||
pub type HistoryUtxoTxMap = HashMap<H256Json, HistoryUtxoTx>; | |||
pub type MatureUnspentMap = HashMap<Address, MatureUnspentList>; | |||
pub type RecentlySpentOutPointsGuard<'a> = AsyncMutexGuard<'a, RecentlySpentOutPoints>; |
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.
RecentlySpentOutPoints
support only single for_script_pubkey
now. It has to be refactored for HD wallets support. Do you plan to do it on next iterations?
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.
Yes, there is a note here. I'm still thinking about the refactoring. Can I refactor it at the next iteration with tx history?
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.
Yes 🙂
mm2src/coins/utxo/rpc_clients.rs
Outdated
@@ -668,6 +746,34 @@ impl UtxoRpcClientOps for NativeClient { | |||
) | |||
} | |||
|
|||
fn display_balances(&self, addresses: Vec<Address>, decimals: u8) -> UtxoRpcFut<Vec<(Address, BigDecimal)>> { | |||
fn address_balance_from_unspent_map(address: &Address, unspent_map: &UnspentMap, decimals: u8) -> BigDecimal { |
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.
If this can be reused somewhere else, it's preferred to move it outside another fn.
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.
I think address_balance_from_unspent_map
is pinned to the NativeClient::display_balances
, but I think this can be reused later somewhere:
unspents.iter().fold(BigDecimal::from(0), |sum, unspent| {
sum + big_decimal_from_sat_unsigned(unspent.value, decimals)
})
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.
Moved address_balance_from_unspent_map
outside the NativeClient::display_balances
method
* Refactor `BchRequest` trait into `JsonRpcBatchClient` * Move `send_batch_request` from `JsonRpcClient` to `JsonRpcBatchClient` * Refactor `BchCoin::utxos_into_bch_unspents`
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.
First review iteration.
First of all great work with documenting the codebase! :) I tried to not spam the same topic in the PR comments. So please consider them as general optimizations like inlining public functions, removing the unneeded variables, etc.
About removing the variable, I think we should avoid creating variables unless the code will be very hard to read and maintain. Compilers these days are trying to solve this in order to avoid memory preasure, but it's not guaranteed so we can't rely on that specially in projects like this.
mm2src/coins/qrc20.rs
Outdated
/// Please note the method is overridden for Native mode only. | ||
#[cfg(not(target_arch = "wasm32"))] | ||
fn tx_cache(&self) -> UtxoVerboseCacheShared { | ||
let tx_cache_path = self.ctx().dbdir().join("TX_CACHE"); |
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.
How about creating a constant for "TX_CACHE", I see it's used multiple times. Would be better if we have constant for this value so we don't need to make so many diffs in order to update this value.
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.
Will it be ok for you if I do the following (but without the constant)?
#[cfg(not(target_arch = "wasm32"))]
fn tx_cache(&self) -> UtxoVerboseCacheShared {
crate::utxo::tx_cache::fs_tx_cache::FsVerboseCache::new(self.ticker().to_owned(), self.tx_cache_path())
.into_shared()
}
fn tx_cache_path(&self) -> PathBuf { self.ctx().dbdir().join("TX_CACHE") }
@@ -55,7 +56,7 @@ fn check_tx_fee(coin: &Qrc20Coin, expected_tx_fee: ActualTxFee) { | |||
|
|||
#[test] | |||
fn test_withdraw_impl_fee_details() { | |||
Qrc20Coin::list_mature_unspent_ordered.mock_safe(|coin, _| { | |||
Qrc20Coin::get_unspent_ordered_list.mock_safe(|coin, _| { |
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.
question
I see the functionality has been changed a little bit in this test function. Is that how it's supposed to be?
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.
Sorry, I didn't understand your question. If I'm not mistaken, neither test_withdraw_impl_fee_details
nor Qrc20Coin::get_unspent_ordered_list
haven't been changed.
What do you mean? 😅
the functionality has been changed a little bit in this test function
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.
What I mean is list_mature_unspent_ordered
renamed into get_mature_unspent_ordered_list
, but you used get_unspent_ordered_list
. I wanted to know if you did it on purpose, I thought you might be confused about the function name. I guess you did it on purpose so no problem 👍
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.
I renamed it because there is similar method that returns UnspentMap
of address -> UnspentList
. So map_mature_unspent_ordered
doesn't fit its purpose, so I had to rename all of those methods to get_[all/mature]_unspent_ordered_[list/map]
😁
) -> MmResult<HDAccountBalanceResponse, HDAccountBalanceRpcError>; | ||
} | ||
|
||
pub async fn account_balance( |
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 can be optimized in a following way:
#[inline]
pub async fn account_balance(
ctx: MmArc,
req: HDAccountBalanceRequest,
) -> MmResult<HDAccountBalanceResponse, HDAccountBalanceRpcError> {
match lp_coinfind_or_err(&ctx, &req.coin).await? {
MmCoinEnum::UtxoCoin(utxo) => utxo.account_balance_rpc(req.params).await,
MmCoinEnum::QtumCoin(qtum) => qtum.account_balance_rpc(req.params).await,
_ => MmError::err(HDAccountBalanceRpcError::CoinIsActivatedNotWithHDWallet),
}
}
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
Coin: HDWalletBalanceOps + CoinWithDerivationMethod<HDWallet = <Coin as HDWalletCoinOps>::HDWallet> + Sync, | ||
<Coin as HDWalletCoinOps>::Address: fmt::Display + Clone, | ||
{ | ||
let hd_wallet = coin.derivation_method().hd_wallet_or_err()?; |
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.
I would prefer the shorter way as in the following diff:
@@ -73,14 +73,15 @@ pub mod common_impl {
Coin: HDWalletBalanceOps + CoinWithDerivationMethod<HDWallet = <Coin as HDWalletCoinOps>::HDWallet> + Sync,
<Coin as HDWalletCoinOps>::Address: fmt::Display + Clone,
{
- let hd_wallet = coin.derivation_method().hd_wallet_or_err()?;
-
- let account_id = params.account_index;
- let chain = params.chain;
- let hd_account = hd_wallet
- .get_account(account_id)
+ let hd_account = coin
+ .derivation_method()
+ .hd_wallet_or_err()?
+ .get_account(params.account_index)
.await
- .or_mm_err(|| HDAccountBalanceRpcError::UnknownAccount { account_id })?;
+ .or_mm_err(|| HDAccountBalanceRpcError::UnknownAccount {
+ account_id: params.account_index,
+ })?;
+
let total_addresses_number = hd_account.known_addresses_number(params.chain)?;
let from_address_id = match params.paging_options {
@@ -90,13 +91,13 @@ pub mod common_impl {
let to_address_id = std::cmp::min(from_address_id + params.limit as u32, total_addresses_number);
let addresses = coin
- .known_addresses_balances_with_ids(&hd_account, chain, from_address_id..to_address_id)
+ .known_addresses_balances_with_ids(&hd_account, params.chain, from_address_id..to_address_id)
.await?;
let page_balance = addresses.iter().fold(CoinBalance::default(), |total, addr_balance| {
total + addr_balance.balance.clone()
});
- let result = HDAccountBalanceResponse {
+ Ok(HDAccountBalanceResponse {
account_index: params.account_index,
derivation_path: RpcDerivationPath(hd_account.account_derivation_path()),
addresses,
@@ -106,8 +107,6 @@ pub mod common_impl {
total: total_addresses_number,
total_pages: calc_total_pages(total_addresses_number as usize, params.limit),
paging_options: params.paging_options,
- };
-
- Ok(result)
+ })
}
}
to avoid from memory pressure and doing extra process executions. Do you think that this is way more complicated in order to read and maintain code?
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.
No, I think it doesn't make the code more complicated. Will do 🙂
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.
Refactored, but I left
let account_id = params.account_index;
variable, because it's used here
- .or_mm_err(|| HDAccountBalanceRpcError::UnknownAccount { account_id })?;
+ .or_mm_err(|| HDAccountBalanceRpcError::UnknownAccount {
+ account_id: params.account_index,
+ })?;
RpcTaskError::Canceled => HDAccountBalanceRpcError::Internal("Canceled".to_owned()), | ||
RpcTaskError::Timeout(timeout) => HDAccountBalanceRpcError::Timeout(timeout), | ||
RpcTaskError::NoSuchTask(_) | RpcTaskError::UnexpectedTaskStatus { .. } => { | ||
HDAccountBalanceRpcError::Internal(error) |
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.
Since the error
is only used once, maybe it's better to not create variable and directly do e.to_string()
here.
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.
e
is moved at this moment. There are other ways to convert the error to string on NoSuchTask
and UnexpectedTaskStatus
only:
match e {
RpcTaskError::Canceled => HDAccountBalanceRpcError::Internal("Canceled".to_owned()),
RpcTaskError::Timeout(timeout) => HDAccountBalanceRpcError::Timeout(timeout),
error @ RpcTaskError::NoSuchTask(_) | error @ RpcTaskError::UnexpectedTaskStatus { .. } => {
HDAccountBalanceRpcError::Internal(error.to_string())
},
RpcTaskError::Internal(internal) => HDAccountBalanceRpcError::Internal(internal),
}
or
match e {
RpcTaskError::Canceled => HDAccountBalanceRpcError::Internal("Canceled".to_owned()),
RpcTaskError::Timeout(timeout) => HDAccountBalanceRpcError::Timeout(timeout),
RpcTaskError::Internal(internal) => HDAccountBalanceRpcError::Internal(internal),
error => HDAccountBalanceRpcError::Internal(error.to_string()),
}
I'd prefer the first solution but it looks scary a bit 😅
What do you think?
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.
e
is moved at this moment. There are other ways to convert the error to string onNoSuchTask
andUnexpectedTaskStatus
only:match e { RpcTaskError::Canceled => HDAccountBalanceRpcError::Internal("Canceled".to_owned()), RpcTaskError::Timeout(timeout) => HDAccountBalanceRpcError::Timeout(timeout), error @ RpcTaskError::NoSuchTask(_) | error @ RpcTaskError::UnexpectedTaskStatus { .. } => { HDAccountBalanceRpcError::Internal(error.to_string()) }, RpcTaskError::Internal(internal) => HDAccountBalanceRpcError::Internal(internal), }or
match e { RpcTaskError::Canceled => HDAccountBalanceRpcError::Internal("Canceled".to_owned()), RpcTaskError::Timeout(timeout) => HDAccountBalanceRpcError::Timeout(timeout), RpcTaskError::Internal(internal) => HDAccountBalanceRpcError::Internal(internal), error => HDAccountBalanceRpcError::Internal(error.to_string()), }I'd prefer the first solution but it looks scary a bit sweat_smile What do you think?
How about the following example:
@@ -94,14 +94,13 @@ impl From<AddressDerivingError> for HDAccountBalanceRpcError {
impl From<RpcTaskError> for HDAccountBalanceRpcError {
fn from(e: RpcTaskError) -> Self {
- let error = e.to_string();
- match e {
+ match &e {
RpcTaskError::Canceled => HDAccountBalanceRpcError::Internal("Canceled".to_owned()),
- RpcTaskError::Timeout(timeout) => HDAccountBalanceRpcError::Timeout(timeout),
+ RpcTaskError::Timeout(timeout) => HDAccountBalanceRpcError::Timeout(*timeout),
RpcTaskError::NoSuchTask(_) | RpcTaskError::UnexpectedTaskStatus { .. } => {
- HDAccountBalanceRpcError::Internal(error)
+ HDAccountBalanceRpcError::Internal(e.to_string())
},
- RpcTaskError::Internal(internal) => HDAccountBalanceRpcError::Internal(internal),
+ RpcTaskError::Internal(internal) => HDAccountBalanceRpcError::Internal(internal.to_string()),
}
}
}
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.
Also, currently RpcTaskError::Internal(internal) => ...
already does the partially move.
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
I figured out the following:
let res: Result<String, String> = Err("foo".to_owned());
let own_value: String = match res {
Ok(own_ok) => own_ok,
// `_` is syntactic sugar that doesn't take ownership of `res`,
// so `res` can be used in this branch as it's not even borrowed.
Err(_) => res.expect_err(""),
};
assert!(unsafe { LIST_MATURE_UNSPENT_ORDERED_CALLED }); | ||
} | ||
|
||
/// `UtxoStandardCoin` hasn't to check UTXO maturity if `check_utxo_maturity` is not set. | ||
/// https://github.com/KomodoPlatform/atomicDEX-API/issues/1181 | ||
#[test] | ||
fn test_utxo_standard_without_check_utxo_maturity() { | ||
static mut LIST_ALL_UNSPENT_ORDERED_CALLED: bool = false; | ||
static mut GET_ALL_UNSPENT_ORDERED_LIST_CALLED: bool = false; |
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.
Can you please specify a short doc something like:
/// Checks if `fn get_all_unspent_ordered_list` is called or not.
It could be a little bit hard to understand at the first try for newbies(like me).
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.
Ok, sure :)
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
|
||
let rpc_client = native_client_for_test(); | ||
|
||
let expected: Vec<(Address, BigDecimal)> = vec![ |
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.
I would put expected value right before the assertion rather than in the middle of the test flow. Just my opinion, not necessary change. :)
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.
The expected
value is used in the line below
https://github.com/KomodoPlatform/atomicDEX-API/blob/7224b85e39a2406b5b9e8cb9a8e69cd59fda514f/mm2src/coins/utxo/utxo_tests.rs#L3793-L3800
This could be done in more lines:
let addresses = vec![
"RG278CfeNPFtNztFZQir8cgdWexVhViYVy".into(),
"RYPz6Lr4muj4gcFzpMdv3ks1NCGn3mkDPN".into(),
"RJeDDtDRtKUoL8BCKdH7TNCHqUKr7kQRsi".into(),
"RQHn9VPHBqNjYwyKfJbZCiaxVrWPKGQjeF".into(),
];
let addresses = expected.iter().map(|(address, _)| address.clone()).collect();
let actual = rpc_client
.display_balances(addresses, TEST_COIN_DECIMALS)
.wait()
.unwrap();
let expected: Vec<(Address, BigDecimal)> = vec![
("RG278CfeNPFtNztFZQir8cgdWexVhViYVy".into(), BigDecimal::from(5.77699)),
("RYPz6Lr4muj4gcFzpMdv3ks1NCGn3mkDPN".into(), BigDecimal::from(0)),
("RJeDDtDRtKUoL8BCKdH7TNCHqUKr7kQRsi".into(), BigDecimal::from(0.77699)),
("RQHn9VPHBqNjYwyKfJbZCiaxVrWPKGQjeF".into(), BigDecimal::from(0.99998)),
];
assert_eq!(actual, expected);
What do you think?
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.
Looks okay. You can consider also the following:
fn test_native_display_balances() {
let rpc_client = native_client_for_test();
- let expected: Vec<(Address, BigDecimal)> = vec![
- ("RG278CfeNPFtNztFZQir8cgdWexVhViYVy".into(), BigDecimal::from(5.77699)),
- ("RYPz6Lr4muj4gcFzpMdv3ks1NCGn3mkDPN".into(), BigDecimal::from(0)),
- ("RJeDDtDRtKUoL8BCKdH7TNCHqUKr7kQRsi".into(), BigDecimal::from(0.77699)),
- ("RQHn9VPHBqNjYwyKfJbZCiaxVrWPKGQjeF".into(), BigDecimal::from(0.99998)),
+ let addresses: Vec<Address> = vec![
+ "RG278CfeNPFtNztFZQir8cgdWexVhViYVy".into(),
+ "RYPz6Lr4muj4gcFzpMdv3ks1NCGn3mkDPN".into(),
+ "RJeDDtDRtKUoL8BCKdH7TNCHqUKr7kQRsi".into(),
+ "RQHn9VPHBqNjYwyKfJbZCiaxVrWPKGQjeF".into(),
];
- let addresses = expected.iter().map(|(address, _)| address.clone()).collect();
let actual = rpc_client
- .display_balances(addresses, TEST_COIN_DECIMALS)
+ .display_balances(addresses.clone(), TEST_COIN_DECIMALS)
.wait()
.unwrap();
+ let expected_balances: Vec<BigDecimal> = vec![
+ BigDecimal::from(5.77699 as f64),
+ BigDecimal::from(0 as i32),
+ BigDecimal::from(0.77699 as f64),
+ BigDecimal::from(0.99998 as f64),
+ ];
+
+ assert_eq!(addresses.len(), expected_balances.len());
+
+ let mut expected: Vec<(Address, BigDecimal)> = Vec::new();
+ addresses.iter().enumerate().for_each(|(index, address)| {
+ expected.push((*address, expected_balances[index]));
+ });
+
assert_eq!(actual, expected);
}
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.
Isn't my solution more compact and easy to read? 😅
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.
I just wanted to share a code flow from another aspect, all looks confirmable. The decision is up to you, I am fine with any of them :)
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
mm2src/coins/utxo/rpc_clients.rs
Outdated
unspents | ||
.into_iter() | ||
.map(|hash_unspents| { | ||
let hash_unspents: Vec<_> = hash_unspents |
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.
Can be simplified as:
hash_unspents
.into_iter()
.unique_by(|unspent| (unspent.tx_hash, unspent.tx_pos))
.collect::<Vec<_>>()
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.
Oooh right... 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
* Move `address_balance_from_unspent_map` outside the `NativeClient::display_balance` function
* Avoid adding unnecessary variables * Add `inline` attribute where it's needed
@artemii235 @ozkanonur @shamardy the PR is ready for re-review. Please check if I resolved all issues |
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.
LGTM! Only one non-blocker comment about inline(always)
#1255 (comment)
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.
Looks good to me. Great work!
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.
One question and two changes.
* Refactor `utxo_common::addresses_balances`
* Make `OutPoint` copiable
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.
👍
Optimize requesting balances of HD addresses by using RPC batch request.
init_utxo
,init_qtum
RPCs by using batch requestsaddresses_balances
RPCscan_for_new_addresses
RPC by spawning it as an RPC task, rename the method toinit_scan_for_new_addresses
list_mature_unspent_ordered
,list_all_unspent_ordered
,list_unspent_ordered
fromUtxoCommonOps
into the following:ListUtxoOps::get_mature_unspent_ordered_map
- returns available mature and immature unspents in ascending order for every givenaddresses: Vec<Address>
ListUtxoOps::get_all_unspent_ordered_map
- returns available unspents in ascending order for every givenVec<Address>
ListUtxoOps::get_unspent_ordered_list
- returns available unspents in ascending orderListUtxoOps::get_unspent_ordered_map
- returns available unspents in ascending order for every givenVec<Address>
UtxoCommonOps::get_verbose_transaction_from_cache_or_rpc
toUtxoCommonOps::get_verbose_transactions_from_cache_or_rpc
. The method requests multiple transactions concurrentlyUtxoRpcClientOps::list_unspent_group
,UtxoRpcClientOps::get_verbose_transactions
,UtxoRpcClientOps::display_balances