-
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] 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
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about providing proper result type instead of plain text? Or maybe even bool
type can be nice.
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/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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here. (About plain text result types)
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/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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here. (About plain text result types)
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/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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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())),
};
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
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.
Huge progress!
First review iteration
@@ -919,6 +957,52 @@ pub async fn open_channel(ctx: MmArc, req: OpenChannelRequest) -> OpenChannelRes | |||
}) | |||
} | |||
|
|||
#[derive(Deserialize)] | |||
pub struct UpdateChannelReq { |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added this comment to this checklist to not forget
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.
Next review iteration :)
.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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can apply this update here and other cases in this module. I see this block used in many functions.
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/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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't need to be vector. Can simply be slice like &[req.channel_id.0]
.
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/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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, thanks for the info :)
I think Not Found
is a good fit for this then
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/lightning/ln_errors.rs
Outdated
fn status_code(&self) -> StatusCode { | ||
match self { | ||
TrustedNodeError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST, | ||
TrustedNodeError::NoSuchCoin(_) => StatusCode::PRECONDITION_REQUIRED, |
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 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.
Done
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just File::open(persister.manager_path())?
:)
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 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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(())
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
@sergeyboyko0791 @ozkanonur this is ready for another review iteration. |
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.
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
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.
Just minor notes/suggestions 🙂
@@ -288,4 +291,8 @@ impl<'a> UtxoConfBuilder<'a> { | |||
} | |||
|
|||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here (about unwrap_or_default())
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.
@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. |
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!
One suggestion for the future not to forget.
UtxoStandardCoin::from, | ||
) | ||
.build() | ||
.await | ||
.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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. Added this comment to the checklist #1045 (comment) to not forget.
#1045
version 4
,KAWPOW_VERSION
.