forked from jl777/SuperNET
-
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
Partial lightning network node implementation #1103
Merged
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
6991e16
impl FeeEstimator, Logger, BroadcasterInterface
shamardy 3f72b8a
impl Filter for ElectrumClient
shamardy 35473c8
lightning conf + ChainMonitor for electrum
shamardy bbd4bdd
fix wasm build
shamardy 0db0f29
impl keys_manager and channel_manager
shamardy 7deb32e
NetGraphMsgHandler, PeerManager, spawn network
shamardy 36ff2ac
update best block for chainmon and channel manager
shamardy 9143078
Mock LN events, Persist CM, Background Processing
shamardy c1482ef
1st version of enable_lightning rpc for test
shamardy 94e7d74
refactoring + Error handling
shamardy c64bd37
remove hardcoded lightning conf paramaters
shamardy d2c2595
move LN trait impls for Electrum to seperate file
shamardy e61056a
enable_lightning test
shamardy ad87f48
Merge remote-tracking branch 'origin/dev' into ln-htlc-poc
shamardy 5ab74b0
move enable_lightning to dispacher_v2
shamardy d7b7c04
announce external ip address to LN
shamardy 7c74dac
Add node color to configs and request
shamardy File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
#[cfg(not(target_arch = "wasm32"))] | ||
use crate::utxo::rpc_clients::UtxoRpcClientEnum; | ||
#[cfg(not(target_arch = "wasm32"))] | ||
use common::ip_addr::myipaddr; | ||
use common::mm_ctx::MmArc; | ||
use common::mm_error::prelude::*; | ||
use ln_errors::{EnableLightningError, EnableLightningResult}; | ||
#[cfg(not(target_arch = "wasm32"))] | ||
use ln_utils::{network_from_string, start_lightning, LightningConf}; | ||
|
||
#[cfg(not(target_arch = "wasm32"))] | ||
use super::{lp_coinfind_or_err, MmCoinEnum}; | ||
|
||
mod ln_errors; | ||
mod ln_rpc; | ||
#[cfg(not(target_arch = "wasm32"))] mod ln_utils; | ||
|
||
#[derive(Deserialize)] | ||
pub struct EnableLightningRequest { | ||
pub coin: String, | ||
pub port: Option<u16>, | ||
pub name: String, | ||
pub color: Option<String>, | ||
} | ||
|
||
#[cfg(target_arch = "wasm32")] | ||
pub async fn enable_lightning(_ctx: MmArc, _req: EnableLightningRequest) -> EnableLightningResult<String> { | ||
MmError::err(EnableLightningError::UnsupportedMode( | ||
"'enable_lightning'".into(), | ||
"native".into(), | ||
)) | ||
} | ||
|
||
/// Start a BTC lightning node (LTC should be added later). | ||
#[cfg(not(target_arch = "wasm32"))] | ||
pub async fn enable_lightning(ctx: MmArc, req: EnableLightningRequest) -> EnableLightningResult<String> { | ||
// coin has to be enabled in electrum to start a lightning node | ||
let coin = lp_coinfind_or_err(&ctx, &req.coin).await?; | ||
|
||
let utxo_coin = match coin { | ||
MmCoinEnum::UtxoCoin(utxo) => utxo, | ||
_ => { | ||
return MmError::err(EnableLightningError::UnsupportedCoin( | ||
req.coin, | ||
"Only utxo coins are supported in lightning".into(), | ||
)) | ||
}, | ||
}; | ||
|
||
if !utxo_coin.as_ref().conf.lightning { | ||
return MmError::err(EnableLightningError::UnsupportedCoin( | ||
req.coin, | ||
"'lightning' field not found in coin config".into(), | ||
)); | ||
} | ||
|
||
let client = match &utxo_coin.as_ref().rpc_client { | ||
UtxoRpcClientEnum::Electrum(c) => c, | ||
UtxoRpcClientEnum::Native(_) => { | ||
return MmError::err(EnableLightningError::UnsupportedMode( | ||
"Lightning network".into(), | ||
"electrum".into(), | ||
)) | ||
}, | ||
}; | ||
|
||
let network = match &utxo_coin.as_ref().conf.network { | ||
Some(n) => network_from_string(n.clone())?, | ||
None => { | ||
return MmError::err(EnableLightningError::UnsupportedCoin( | ||
req.coin, | ||
"'network' field not found in coin config".into(), | ||
)) | ||
}, | ||
}; | ||
|
||
if req.name.len() > 32 { | ||
return MmError::err(EnableLightningError::InvalidRequest( | ||
"Node name length can't be more than 32 characters".into(), | ||
)); | ||
} | ||
let node_name = format!("{}{:width$}", req.name, " ", width = 32 - req.name.len()); | ||
|
||
let mut node_color = [0u8; 3]; | ||
hex::decode_to_slice( | ||
req.color.unwrap_or_else(|| "000000".into()), | ||
&mut node_color as &mut [u8], | ||
) | ||
.map_to_mm(|_| EnableLightningError::InvalidRequest("Invalid Hex Color".into()))?; | ||
|
||
let listen_addr = myipaddr(ctx.clone()) | ||
.await | ||
.map_to_mm(EnableLightningError::InvalidAddress)?; | ||
let port = req.port.unwrap_or(9735); | ||
|
||
let conf = LightningConf::new(client.clone(), network, listen_addr, port, node_name, node_color); | ||
start_lightning(&ctx, conf).await?; | ||
|
||
Ok("success".into()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
use crate::CoinFindError; | ||
use common::mm_error::prelude::*; | ||
use common::HttpStatusCode; | ||
use derive_more::Display; | ||
use http::StatusCode; | ||
|
||
pub type EnableLightningResult<T> = Result<T, MmError<EnableLightningError>>; | ||
|
||
#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)] | ||
#[serde(tag = "error_type", content = "error_data")] | ||
pub enum EnableLightningError { | ||
#[display(fmt = "Invalid request: {}", _0)] | ||
InvalidRequest(String), | ||
#[display(fmt = "Invalid address: {}", _0)] | ||
InvalidAddress(String), | ||
#[display(fmt = "Invalid path: {}", _0)] | ||
InvalidPath(String), | ||
#[display(fmt = "Lightning node already running")] | ||
AlreadyRunning, | ||
#[display(fmt = "{} is only supported in {} mode", _0, _1)] | ||
UnsupportedMode(String, String), | ||
#[display(fmt = "Lightning network is not supported for {}: {}", _0, _1)] | ||
UnsupportedCoin(String, String), | ||
#[display(fmt = "No such coin {}", _0)] | ||
NoSuchCoin(String), | ||
#[display(fmt = "System time error {}", _0)] | ||
SystemTimeError(String), | ||
#[display(fmt = "I/O error {}", _0)] | ||
IOError(String), | ||
#[display(fmt = "Hash error {}", _0)] | ||
HashError(String), | ||
#[display(fmt = "RPC error {}", _0)] | ||
RpcError(String), | ||
} | ||
|
||
impl HttpStatusCode for EnableLightningError { | ||
fn status_code(&self) -> StatusCode { | ||
match self { | ||
EnableLightningError::InvalidRequest(_) | ||
| EnableLightningError::RpcError(_) | ||
| EnableLightningError::UnsupportedCoin(_, _) => StatusCode::BAD_REQUEST, | ||
EnableLightningError::AlreadyRunning | EnableLightningError::UnsupportedMode(_, _) => { | ||
StatusCode::METHOD_NOT_ALLOWED | ||
}, | ||
EnableLightningError::InvalidAddress(_) | ||
| EnableLightningError::InvalidPath(_) | ||
| EnableLightningError::SystemTimeError(_) | ||
| EnableLightningError::IOError(_) | ||
| EnableLightningError::HashError(_) => StatusCode::INTERNAL_SERVER_ERROR, | ||
EnableLightningError::NoSuchCoin(_) => StatusCode::PRECONDITION_REQUIRED, | ||
} | ||
} | ||
} | ||
|
||
impl From<CoinFindError> for EnableLightningError { | ||
fn from(e: CoinFindError) -> Self { | ||
match e { | ||
CoinFindError::NoSuchCoin { coin } => EnableLightningError::NoSuchCoin(coin), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
use crate::utxo::rpc_clients::{electrum_script_hash, ElectrumClient, UtxoRpcClientOps, UtxoRpcError}; | ||
use bitcoin::blockdata::script::Script; | ||
use bitcoin::blockdata::transaction::Transaction; | ||
use bitcoin::consensus::encode; | ||
use bitcoin::hash_types::Txid; | ||
use common::block_on; | ||
use common::mm_error::prelude::MapToMmFutureExt; | ||
use futures::compat::Future01CompatExt; | ||
use lightning::chain::{chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator}, | ||
Filter, WatchedOutput}; | ||
use rpc::v1::types::Bytes as BytesJson; | ||
|
||
impl FeeEstimator for ElectrumClient { | ||
// Gets estimated satoshis of fee required per 1000 Weight-Units. | ||
// TODO: use fn estimate_fee instead of fixed number when starting work on opening channels | ||
fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u32 { | ||
match confirmation_target { | ||
// fetch background feerate | ||
ConfirmationTarget::Background => 253, | ||
// fetch normal feerate (~6 blocks) | ||
ConfirmationTarget::Normal => 2000, | ||
// fetch high priority feerate | ||
ConfirmationTarget::HighPriority => 5000, | ||
} | ||
} | ||
} | ||
|
||
impl BroadcasterInterface for ElectrumClient { | ||
fn broadcast_transaction(&self, tx: &Transaction) { | ||
let tx_bytes = BytesJson::from(encode::serialize_hex(tx).as_bytes()); | ||
let _ = Box::new( | ||
self.blockchain_transaction_broadcast(tx_bytes) | ||
.map_to_mm_fut(UtxoRpcError::from), | ||
); | ||
} | ||
} | ||
|
||
impl Filter for ElectrumClient { | ||
// Watches for this transaction on-chain | ||
fn register_tx(&self, _txid: &Txid, _script_pubkey: &Script) { unimplemented!() } | ||
|
||
// Watches for any transactions that spend this output on-chain | ||
fn register_output(&self, output: WatchedOutput) -> Option<(usize, Transaction)> { | ||
let selfi = self.clone(); | ||
let script_hash = hex::encode(electrum_script_hash(output.script_pubkey.as_ref())); | ||
let history = block_on(selfi.scripthash_get_history(&script_hash).compat()).unwrap_or_default(); | ||
|
||
if history.len() < 2 { | ||
return None; | ||
} | ||
|
||
for item in history.iter() { | ||
let transaction = match block_on(selfi.get_transaction_bytes(item.tx_hash.clone()).compat()) { | ||
Ok(tx) => tx, | ||
Err(_) => continue, | ||
}; | ||
|
||
let maybe_spend_tx: Transaction = match encode::deserialize(transaction.as_slice()) { | ||
Ok(tx) => tx, | ||
Err(_) => continue, | ||
}; | ||
|
||
for (index, input) in maybe_spend_tx.input.iter().enumerate() { | ||
if input.previous_output.txid == output.outpoint.txid | ||
&& input.previous_output.vout == output.outpoint.index as u32 | ||
{ | ||
return Some((index, maybe_spend_tx)); | ||
} | ||
} | ||
} | ||
None | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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's a bit sad that they assume a synchronous interface for this 🙁
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 this is intended as they leave you the choice to run these functions in asynchronous code through other interfaces. For the
broadcast_transaction
function for instance, when a new best block is updated for the channel manager https://github.com/KomodoPlatform/atomicDEX-API/blob/ad87f483cc5199851a781c5b99a1bda341f75ecb/mm2src/coins/lightning.rs#L384 if it's required to claim on-chain channel funds for some reason as this case https://github.com/rust-bitcoin/rust-lightning/blob/fe8c10db95124e3238b7469bdabb00afc7c5bdd6/lightning/src/chain/onchaintx.rs#L486,broadcast_transaction
is run in the best_block_update thread away from the p2p networking and node announcement. The only problem I see is if for the same new best block more than one on-chain transaction needs to be broadcasted then they will be broadcasted synchronously inside the best_block_update thread but this is very rare but would have been solved ifbroadcast_transaction
was async.