Skip to content
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

Merged
merged 38 commits into from
Sep 12, 2022

Conversation

shamardy
Copy link
Collaborator

@shamardy shamardy commented Aug 26, 2022

#1045

  • Use async_blocking for sync rust-lightning functions that calls other I/O functions or that has mutexes that can be held for some time. [r2r] Lightning batch transactions confirmations, spv (wip), refactors #1339 (comment)
  • Get default fees from rpc when starting lightning instead of config. [r2r] Lightning batch transactions confirmations, spv (wip), refactors #1339 (comment)
  • Update rust-lightning from v0.0.106 to v0.0.110.
  • After updating to v0.0.108, the option to enable 0 confirmations channels was added. 3 new RPCs were added to enable this feature (add_trusted_node, remove_trusted_node , list_trusted_nodes). This feature allows immediate forwarding of payments after channel opening which in turn allows writing unit tests for lightning payments. This should help unit testing swaps functions in the future.
  • Added update_channel RPC that updates a channel that is open without closing it. The P2P messages required for this were added to rust-lightning v0.0.110.
  • Moved the lightning Background processor to mm2 context so that the latest state can be persisted to the filesystem on exiting mm2, since the background can be dropped on exiting now (when calling the “stop” RPC). This problem was detected due to updating to rust-lightning v0.0.110 since now the network graph only persists on exit.
  • BTC headers can now be validated using the difficulty calculations.
  • Made block headers storage non-optional. [r2r] Lightning batch transactions confirmations, spv (wip), refactors #1339 (comment)
  • Fixed problems related to BTC block headers deserialization since there are some headers that use version 4, KAWPOW_VERSION.
  • Added spv and block headers synchronization to enable utxo v2. They are not supported in v1 since block headers should be added to the database on enabling the coin which takes too much time for enable v1.
  • Simple payment verification for transactions now use a validated block header from storage.
  • There are multiple todo comments left that should be done in the next PR.

…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
borngraced previously approved these changes Aug 27, 2022
Copy link
Member

@borngraced borngraced left a 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,
Copy link
Member

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
Copy link
Member

@borngraced borngraced Aug 27, 2022

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

Copy link
Collaborator Author

@shamardy shamardy Aug 30, 2022

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

Copy link
Member

@onur-ozkan onur-ozkan left a 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/ln_conf.rs Show resolved Hide resolved
mm2src/coins/lightning/ln_conf.rs Show resolved Hide resolved
mm2src/coins/lightning/ln_conf.rs Show resolved Hide resolved
}

/// Updates configuration for an open channel.
pub async fn update_channel(ctx: MmArc, req: UpdateChannelReq) -> UpdateChannelResult<String> {
Copy link
Member

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

pub node_id: PublicKeyForRPC,
}

pub async fn add_trusted_node(ctx: MmArc, req: AddTrustedNodeReq) -> TrustedNodeResult<String> {
Copy link
Member

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)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

pub node_id: PublicKeyForRPC,
}

pub async fn remove_trusted_node(ctx: MmArc, req: RemoveTrustedNodeReq) -> TrustedNodeResult<String> {
Copy link
Member

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)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 1755 to 1759
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())),
};
Copy link
Member

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())),
};

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link

@sergeyboyko0791 sergeyboyko0791 left a 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

Cargo.lock Show resolved Hide resolved
mm2src/coins/Cargo.toml Outdated Show resolved Hide resolved
@@ -919,6 +957,52 @@ pub async fn open_channel(ctx: MmArc, req: OpenChannelRequest) -> OpenChannelRes
})
}

#[derive(Deserialize)]
pub struct UpdateChannelReq {

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.

Copy link
Collaborator Author

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.

Copy link
Collaborator Author

@shamardy shamardy Sep 5, 2022

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

mm2src/coins/lightning.rs Outdated Show resolved Hide resolved
mm2src/coins/lightning.rs Outdated Show resolved Hide resolved
mm2src/coins/lightning/ln_filesystem_persister.rs Outdated Show resolved Hide resolved
mm2src/coins/lightning/ln_filesystem_persister.rs Outdated Show resolved Hide resolved
mm2src/coins/lightning/ln_filesystem_persister.rs Outdated Show resolved Hide resolved
mm2src/coins/lightning/ln_filesystem_persister.rs Outdated Show resolved Hide resolved
mm2src/coins/lightning/ln_platform.rs Outdated Show resolved Hide resolved
Copy link
Member

@onur-ozkan onur-ozkan left a 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
Copy link
Member

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.

Copy link
Collaborator Author

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.

Comment on lines 975 to 979
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())),
};
Copy link
Member

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

if channel_options != req.channel_options {
channel_options.update(req.channel_options.clone());
}
let channel_ids = vec![req.channel_id.0];
Copy link
Member

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].

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

fn status_code(&self) -> StatusCode {
match self {
UpdateChannelError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST,
UpdateChannelError::NoSuchCoin(_) => StatusCode::PRECONDITION_REQUIRED,
Copy link
Member

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.

Copy link
Collaborator Author

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

fn status_code(&self) -> StatusCode {
match self {
TrustedNodeError::UnsupportedCoin(_) => StatusCode::BAD_REQUEST,
TrustedNodeError::NoSuchCoin(_) => StatusCode::PRECONDITION_REQUIRED,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

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()) {
Copy link
Member

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())? :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +1907 to 1909
let mut reader =
Reader::new_with_coin_variant(serialized.as_slice(), coin_name.as_str().into());
let maybe_block_headers = reader.read_list::<BlockHeader>();
Copy link
Member

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(())

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@shamardy shamardy changed the title [r2r] Update rust-lightning to latest version, 0_confs channels, complete btc spv, multiple fixes [wip] Update rust-lightning to latest version, 0_confs channels, complete btc spv, multiple fixes Sep 2, 2022
@shamardy shamardy changed the title [wip] Update rust-lightning to latest version, 0_confs channels, complete btc spv, multiple fixes [r2r] Update rust-lightning to latest version, 0_confs channels, complete btc spv, multiple fixes Sep 2, 2022
@shamardy
Copy link
Collaborator Author

shamardy commented Sep 2, 2022

@sergeyboyko0791 @ozkanonur this is ready for another review iteration.

Copy link

@sergeyboyko0791 sergeyboyko0791 left a 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/lightning/ln_filesystem_persister.rs Outdated Show resolved Hide resolved
mm2src/coins/lightning/ln_platform.rs Outdated Show resolved Hide resolved
mm2src/coins/lightning/ln_platform.rs Outdated Show resolved Hide resolved
mm2src/coins/utxo/utxo_builder/utxo_arc_builder.rs Outdated Show resolved Hide resolved
@shamardy shamardy mentioned this pull request Sep 5, 2022
24 tasks
@shamardy shamardy changed the title [r2r] Update rust-lightning to latest version, 0_confs channels, complete btc spv, multiple fixes [wip] Update rust-lightning to latest version, 0_confs channels, complete btc spv, multiple fixes Sep 5, 2022
@shamardy shamardy changed the title [wip] Update rust-lightning to latest version, 0_confs channels, complete btc spv, multiple fixes [r2r] Update rust-lightning to latest version, 0_confs channels, complete btc spv, multiple fixes Sep 6, 2022
Copy link
Member

@onur-ozkan onur-ozkan left a 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)
Copy link
Member

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?

Copy link
Collaborator Author

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() {
Copy link
Member

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())

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mm2src/mm2_main/src/mm2_tests.rs Show resolved Hide resolved
mm2src/mm2_main/src/mm2_tests.rs Show resolved Hide resolved
@artemii235
Copy link
Member

@sergeyboyko0791 It's [r2r] for 6 days, please complete the review. Ideally, we should merge everything today.

@artemii235
Copy link
Member

@shamardy There are also git conflicts that appeared after merging other PRs.

Copy link

@sergeyboyko0791 sergeyboyko0791 left a 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 {

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

Copy link
Collaborator Author

@shamardy shamardy Sep 12, 2022

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.

@artemii235 artemii235 merged commit 1519658 into dev Sep 12, 2022
@artemii235 artemii235 deleted the lightning-wallet-final-fixes branch September 12, 2022 12:27
@shamardy shamardy mentioned this pull request Feb 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants