[r2r] Update rust-lightning to latest version, 0_confs channels, complete btc spv, multiple fixes#1452
Conversation
…mediately for invoice payments that we don't have a preimage for
…PC, finally can send payments in unit tests (due to 0 confs channels
…Invoice serialization/deserialization is supported
…en calling stop RPC, to persist the latest states on exit
…hat block headers validation work for the complete BTC chain
borngraced
left a comment
There was a problem hiding this comment.
Really great work! 🔥
I have only 2 non blockers
| Ok(BlockHeaderStorage { | ||
| inner: Box::new(SqliteBlockHeadersStorage { | ||
| ticker, | ||
| conn: sqlite_connection, |
There was a problem hiding this comment.
I guess this can also be rewritten as
let conn = Arc::new(Mutex::new(Connection::open_in_memory().unwrap()));
let conn = ctx.sqlite_connection.clone_or(conn);
Ok(BlockHeaderStorage {
inner: Box::new(SqliteBlockHeadersStorage {
ticker,
conn,
}),| // "LBC", "LBC-segwit", etc.. | ||
| CoinVariant::LBC | ||
| } else { | ||
| CoinVariant::Standard |
There was a problem hiding this comment.
what about replacing coin_variant_by_ticker with this? to avoid having boring if/else statements
fn coin_variant_by_ticker(ticker: &str) -> CoinVariant {
// "BTC", "BTC-segwit", "tBTC", "tBTC-segwit", etc..
let btc_ticker = ticker == "BTC" || ticker.contains("BTC-") || ticker.contains("BTC_");
// "LBC", "LBC-segwit", etc..
let lbc_ticker = ticker == "LBC" || ticker.contains("LBC-") || ticker.contains("LBC_");
match (btc_ticker, lbc_ticker) {
(btc @ true, _) => CoinVariant::BTC,
(_, lbc @ true) => CoinVariant::LBC,
_ => CoinVariant::Standard,
}
}until at least when this issue is resolved #1345
There was a problem hiding this comment.
I think the below is even a better way of doing this :)
pub fn coin_variant_by_ticker(ticker: &str) -> CoinVariant {
match ticker {
// "BTC", "BTC-segwit", "tBTC", "tBTC-segwit", etc..
t if t == "BTC" || t.contains("BTC-") || t.contains("BTC_") => CoinVariant::BTC,
// "LBC", "LBC-segwit", etc..
t if t == "LBC" || t.contains("LBC-") || t.contains("LBC_") => CoinVariant::LBC,
_ => CoinVariant::Standard,
}
}I will also remove coin_variant_by_ticker in favor of impl From<&str> for CoinVariant
onur-ozkan
left a comment
There was a problem hiding this comment.
Appreciated the work! Here is my first review notes :)
mm2src/coins/lightning.rs
Outdated
| } | ||
|
|
||
| /// Updates configuration for an open channel. | ||
| pub async fn update_channel(ctx: MmArc, req: UpdateChannelReq) -> UpdateChannelResult<String> { |
There was a problem hiding this comment.
What about providing proper result type instead of plain text? Or maybe even bool type can be nice.
mm2src/coins/lightning.rs
Outdated
| pub node_id: PublicKeyForRPC, | ||
| } | ||
|
|
||
| pub async fn add_trusted_node(ctx: MmArc, req: AddTrustedNodeReq) -> TrustedNodeResult<String> { |
There was a problem hiding this comment.
Same here. (About plain text result types)
mm2src/coins/lightning.rs
Outdated
| pub node_id: PublicKeyForRPC, | ||
| } | ||
|
|
||
| pub async fn remove_trusted_node(ctx: MmArc, req: RemoveTrustedNodeReq) -> TrustedNodeResult<String> { |
There was a problem hiding this comment.
Same here. (About plain text result types)
mm2src/coins/lightning.rs
Outdated
| let coin = lp_coinfind_or_err(&ctx, &req.coin).await?; | ||
| let ln_coin = match coin { | ||
| MmCoinEnum::LightningCoin(c) => c, | ||
| _ => return MmError::err(TrustedNodeError::UnsupportedCoin(coin.ticker().to_string())), | ||
| }; |
There was a problem hiding this comment.
Since lp_coinfind_or_err always returns MmCoinEnum instance, we don't need to keep coin variable.
So we can shorten this like:
let ln_coin = match lp_coinfind_or_err(&ctx, &req.coin).await? {
MmCoinEnum::LightningCoin(c) => c,
e => return MmError::err(TrustedNodeError::UnsupportedCoin(e.ticker().to_string())),
};
sergeyboyko0791
left a comment
There was a problem hiding this comment.
Huge progress!
First review iteration
| } | ||
|
|
||
| #[derive(Deserialize)] | ||
| pub struct UpdateChannelReq { |
There was a problem hiding this comment.
Not critical.
I think all RPCs could be moved to coins/rpc_command/ directory or a new module like coins/rpc_command/lightning.
There was a problem hiding this comment.
This can be done in a separate refactor PR maybe. Moving all the lightning RPCs now will add lots of changes to this PR that are not really changes but might confuse the reviewers a bit.
There was a problem hiding this comment.
Added this comment to this checklist to not forget
| .list_channels() | ||
| async fn list_channels(&self) -> Vec<ChannelDetails> { | ||
| let channel_manager = self.channel_manager.clone(); | ||
| async_blocking(move || channel_manager.list_channels()).await |
There was a problem hiding this comment.
Just a question, does list_channels really require thread spawning? I am not sure how long it takes to finish. If the operaton is not a long-running task, we should avoid context switching and spawning threads which impacts the performance badly.
There was a problem hiding this comment.
This is all due to the channel_state Mutex
https://github.com/lightningdevkit/rust-lightning/blob/00f08c910ff87fecc580750df33d3ebf5ac1fceb/lightning/src/ln/channelmanager.rs#L702
Since list_channels uses list_channels_with_filter which waits to acquire the lock here https://github.com/lightningdevkit/rust-lightning/blob/00f08c910ff87fecc580750df33d3ebf5ac1fceb/lightning/src/ln/channelmanager.rs#L1743
And I believe, this can take some time, as this lock is locked everywhere when dealing with channels in rust-lightning, so it should require using async_blocking here. An example for a function that acquires the lock for sometime is internal_funding_signed
https://github.com/lightningdevkit/rust-lightning/blob/00f08c910ff87fecc580750df33d3ebf5ac1fceb/lightning/src/ln/channelmanager.rs#L4668 https://github.com/lightningdevkit/rust-lightning/blob/00f08c910ff87fecc580750df33d3ebf5ac1fceb/lightning/src/ln/channelmanager.rs#L4679 https://github.com/lightningdevkit/rust-lightning/blob/00f08c910ff87fecc580750df33d3ebf5ac1fceb/lightning/src/chain/chainmonitor.rs#L584
Where persist_new_channel saves the new monitor to the filesystem.
It's not always the case that list_channels will wait for an I/O process to complete to acquire the lock, I even think that this happens rarely, but the frequency with which this happens should increase with the number of channels open.
mm2src/coins/lightning.rs
Outdated
| let coin = lp_coinfind_or_err(&ctx, &req.coin).await?; | ||
| let ln_coin = match coin { | ||
| MmCoinEnum::LightningCoin(c) => c, | ||
| _ => return MmError::err(UpdateChannelError::UnsupportedCoin(coin.ticker().to_string())), | ||
| }; |
There was a problem hiding this comment.
You can apply this update here and other cases in this module. I see this block used in many functions.
mm2src/coins/lightning.rs
Outdated
| if channel_options != req.channel_options { | ||
| channel_options.update(req.channel_options.clone()); | ||
| } | ||
| let channel_ids = vec![req.channel_id.0]; |
There was a problem hiding this comment.
This doesn't need to be vector. Can simply be slice like &[req.channel_id.0].
mm2src/coins/lightning/ln_errors.rs
Outdated
| fn status_code(&self) -> StatusCode { | ||
| match self { | ||
| UpdateChannelError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, | ||
| UpdateChannelError::NoSuchCoin(_) => StatusCode::PRECONDITION_REQUIRED, |
There was a problem hiding this comment.
We can either return Bad Request or Not Found for UpdateChannelError::NoSuchCoin. 428 Status Code(Precondition Required) is related with conditional requests. See the details.
There was a problem hiding this comment.
I see, thanks for the info :)
I think Not Found is a good fit for this then
mm2src/coins/lightning/ln_errors.rs
Outdated
| fn status_code(&self) -> StatusCode { | ||
| match self { | ||
| TrustedNodeError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, | ||
| TrustedNodeError::NoSuchCoin(_) => StatusCode::PRECONDITION_REQUIRED, |
mm2src/coins/lightning/ln_utils.rs
Outdated
| let chain_monitor_for_args = chain_monitor.clone(); | ||
|
|
||
| let (channel_manager_blockhash, channel_manager, channelmonitors) = async_blocking(move || { | ||
| let mut manager_file = match File::open(persister.manager_path()) { |
There was a problem hiding this comment.
Why not just File::open(persister.manager_path())? :)
| let mut reader = | ||
| Reader::new_with_coin_variant(serialized.as_slice(), coin_name.as_str().into()); | ||
| let maybe_block_headers = reader.read_list::<BlockHeader>(); |
There was a problem hiding this comment.
A note, there is a macro called drop_mutability in common module. You can use it on such cases like this to prevent invalid data assignment to the mutable addresses.
For example:
let mut x = 10;
.
.
x = 1;
drop_mutability(x); // Since the variable no longer needs mutability
.
.
.
.
.
Ok(())|
@sergeyboyko0791 @ozkanonur this is ready for another review iteration. |
sergeyboyko0791
left a comment
There was a problem hiding this comment.
Thank you for the fixes!
Next review iteration mainly consists of suggestions and questions.
mm2src/coins_activation/src/utxo_activation/init_utxo_standard_activation.rs
Outdated
Show resolved
Hide resolved
| fn enable_spv_proof(&self) -> bool { self.conf["enable_spv_proof"].as_bool().unwrap_or(false) } | ||
|
|
||
| fn block_headers_verification_params(&self) -> Option<BlockHeaderVerificationParams> { | ||
| json::from_value(self.conf["block_headers_verification_params"].clone()).unwrap_or(None) |
There was a problem hiding this comment.
How about using unwrap_or_default() instead of unwrap_or. I think unwrap_or more like if you wan't to apply non-default values. If you agree, could you please replace all of them in this module?
There was a problem hiding this comment.
I like to use unwrap_or even for default values for readability purposes, it's also how it's implemented for all the other confs.
| Option<UtxoSyncStatusLoopHandle>, | ||
| Option<AsyncMutex<AsyncReceiver<UtxoSyncStatus>>>, | ||
| ) { | ||
| if self.conf()["enable_spv_proof"].as_bool().unwrap_or(false) && !self.activation_params().mode.is_native() { |
There was a problem hiding this comment.
Same here (about unwrap_or_default())
|
@sergeyboyko0791 It's [r2r] for 6 days, please complete the review. Ideally, we should merge everything today. |
|
@shamardy There are also git conflicts that appeared after merging other PRs. |
sergeyboyko0791
left a comment
There was a problem hiding this comment.
LGTM!
One suggestion for the future not to forget.
| .mm_err(|e| InitUtxoStandardError::from_build_err(e, ticker.clone()))?; | ||
|
|
||
| if let Some(mut sync_watcher) = maybe_sync_watcher { | ||
| if let Some(sync_watcher_mutex) = &coin.as_ref().block_headers_status_watcher { |
There was a problem hiding this comment.
At one of the upcoming PRs we should move this into a common function that will be used for other coins. For example, we might be interesting to use block-headers storage for QTUM coin and even probably for BCH
There was a problem hiding this comment.
I agree. Added this comment to the checklist #1045 (comment) to not forget.
#1045
version 4,KAWPOW_VERSION.