From 1b034d63a4cb4e0b249ae6240166513b6be76737 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Tue, 2 Mar 2021 15:54:17 -0500 Subject: [PATCH] Initial node --- Cargo.lock | 275 ++++--------------------- Cargo.toml | 23 ++- src/bitcoind_client.rs | 76 +++++++ src/cli.rs | 100 +++++++++ src/main.rs | 448 ++++++++++++++++++++++++++++++++++++----- src/utils.rs | 42 ++++ 6 files changed, 679 insertions(+), 285 deletions(-) create mode 100644 src/bitcoind_client.rs create mode 100644 src/cli.rs create mode 100644 src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index f167acc..cdd9335 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,11 +9,10 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "background-processor" version = "0.1.0" -source = "git+https://github.com/rust-bitcoin/rust-lightning?rev=d6f41d3c0b38b9ec9e06a3acfdd9f4b1d007a27d#d6f41d3c0b38b9ec9e06a3acfdd9f4b1d007a27d" dependencies = [ - "bitcoin 0.24.0", - "lightning 0.0.12 (git+https://github.com/rust-bitcoin/rust-lightning?rev=d6f41d3c0b38b9ec9e06a3acfdd9f4b1d007a27d)", - "lightning-persister 0.0.1 (git+https://github.com/rust-bitcoin/rust-lightning?rev=d6f41d3c0b38b9ec9e06a3acfdd9f4b1d007a27d)", + "bitcoin", + "lightning", + "lightning-persister", ] [[package]] @@ -28,6 +27,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "bech32" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4408a9bf5c378a42ca9039e4ef3e3d8df3443f76d2ebe249fd720a2c5e17d2da" + [[package]] name = "bech32" version = "0.7.2" @@ -40,20 +45,18 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6558aeb12c290cce541a222cba280387161f2bd180a7feb85f8f172cd8ba80e8" dependencies = [ - "bech32", + "bech32 0.7.2", "bitcoin_hashes 0.8.0", "secp256k1 0.18.0", ] [[package]] -name = "bitcoin" -version = "0.26.0" +name = "bitcoin-bech32" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec5f88a446d66e7474a3b8fa2e348320b574463fb78d799d90ba68f79f48e0e" +checksum = "b5791779c83cf6bde070a92c22505f2faa06487fc8f372e4d485c41a71351105" dependencies = [ - "bech32", - "bitcoin_hashes 0.9.4", - "secp256k1 0.20.1", + "bech32 0.4.1", ] [[package]] @@ -68,24 +71,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0aaf87b776808e26ae93289bc7d025092b6d909c193f0cdee0b3a86e7bd3c776" -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" - [[package]] name = "bumpalo" version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9" -[[package]] -name = "bytes" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" - [[package]] name = "bytes" version = "1.0.1" @@ -98,12 +89,6 @@ version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -128,34 +113,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -dependencies = [ - "bitflags", - "fuchsia-zircon-sys", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" - [[package]] name = "futures" version = "0.3.12" @@ -244,7 +207,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.4", + "pin-project-lite", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -266,31 +229,12 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" -[[package]] -name = "iovec" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" -dependencies = [ - "libc", -] - [[package]] name = "itoa" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -303,17 +247,18 @@ version = "0.1.0" dependencies = [ "background-processor", "base64", - "bitcoin 0.26.0", + "bitcoin", + "bitcoin-bech32", "hex", - "lightning 0.0.12 (git+https://github.com/rust-bitcoin/rust-lightning?rev=c35002fa9c16042badfa5e7bf819df5f1d2ae60a)", + "lightning", "lightning-block-sync", "lightning-invoice", "lightning-net-tokio", - "lightning-persister 0.0.1 (git+https://github.com/rust-bitcoin/rust-lightning?rev=aa127f55edc4439b03426644d178e402397329e8)", + "lightning-persister", "rand", "serde_json", "time", - "tokio 0.2.25", + "tokio", ] [[package]] @@ -326,52 +271,20 @@ checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" name = "lightning" version = "0.0.12" dependencies = [ - "bitcoin 0.24.0", -] - -[[package]] -name = "lightning" -version = "0.0.12" -source = "git+https://github.com/rust-bitcoin/rust-lightning?rev=aa127f55edc4439b03426644d178e402397329e8#aa127f55edc4439b03426644d178e402397329e8" -dependencies = [ - "bitcoin 0.24.0", -] - -[[package]] -name = "lightning" -version = "0.0.12" -source = "git+https://github.com/rust-bitcoin/rust-lightning?rev=c35002fa9c16042badfa5e7bf819df5f1d2ae60a#c35002fa9c16042badfa5e7bf819df5f1d2ae60a" -dependencies = [ - "bitcoin 0.24.0", -] - -[[package]] -name = "lightning" -version = "0.0.12" -source = "git+https://github.com/rust-bitcoin/rust-lightning?rev=d6f41d3c0b38b9ec9e06a3acfdd9f4b1d007a27d#d6f41d3c0b38b9ec9e06a3acfdd9f4b1d007a27d" -dependencies = [ - "bitcoin 0.24.0", -] - -[[package]] -name = "lightning" -version = "0.0.12" -source = "git+https://github.com/rust-bitcoin/rust-lightning?rev=ff00f6f8861419b73269e6c51d75ac9de75f1d1f#ff00f6f8861419b73269e6c51d75ac9de75f1d1f" -dependencies = [ - "bitcoin 0.24.0", + "bitcoin", ] [[package]] name = "lightning-block-sync" version = "0.0.1" dependencies = [ - "bitcoin 0.24.0", + "bitcoin", "chunked_transfer", "futures", - "lightning 0.0.12", + "lightning", "serde", "serde_json", - "tokio 1.2.0", + "tokio", ] [[package]] @@ -379,7 +292,7 @@ name = "lightning-invoice" version = "0.4.0" source = "git+https://github.com/rust-bitcoin/rust-lightning-invoice?rev=ea25dc7e46a6339493032c500db4fe3a8fdb1acd#ea25dc7e46a6339493032c500db4fe3a8fdb1acd" dependencies = [ - "bech32", + "bech32 0.7.2", "bitcoin_hashes 0.9.4", "num-traits", "secp256k1 0.20.1", @@ -388,33 +301,20 @@ dependencies = [ [[package]] name = "lightning-net-tokio" version = "0.0.5" -source = "git+https://github.com/rust-bitcoin/rust-lightning?rev=ff00f6f8861419b73269e6c51d75ac9de75f1d1f#ff00f6f8861419b73269e6c51d75ac9de75f1d1f" -dependencies = [ - "bitcoin 0.24.0", - "lightning 0.0.12 (git+https://github.com/rust-bitcoin/rust-lightning?rev=ff00f6f8861419b73269e6c51d75ac9de75f1d1f)", - "tokio 1.2.0", -] - -[[package]] -name = "lightning-persister" -version = "0.0.1" -source = "git+https://github.com/rust-bitcoin/rust-lightning?rev=aa127f55edc4439b03426644d178e402397329e8#aa127f55edc4439b03426644d178e402397329e8" dependencies = [ - "bitcoin 0.24.0", - "libc", - "lightning 0.0.12 (git+https://github.com/rust-bitcoin/rust-lightning?rev=aa127f55edc4439b03426644d178e402397329e8)", - "winapi 0.3.9", + "bitcoin", + "lightning", + "tokio", ] [[package]] name = "lightning-persister" version = "0.0.1" -source = "git+https://github.com/rust-bitcoin/rust-lightning?rev=d6f41d3c0b38b9ec9e06a3acfdd9f4b1d007a27d#d6f41d3c0b38b9ec9e06a3acfdd9f4b1d007a27d" dependencies = [ - "bitcoin 0.24.0", + "bitcoin", "libc", - "lightning 0.0.12 (git+https://github.com/rust-bitcoin/rust-lightning?rev=d6f41d3c0b38b9ec9e06a3acfdd9f4b1d007a27d)", - "winapi 0.3.9", + "lightning", + "winapi", ] [[package]] @@ -423,7 +323,7 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -432,25 +332,6 @@ version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" -[[package]] -name = "mio" -version = "0.6.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" -dependencies = [ - "cfg-if 0.1.10", - "fuchsia-zircon", - "fuchsia-zircon-sys", - "iovec", - "kernel32-sys", - "libc", - "log", - "miow 0.2.2", - "net2", - "slab", - "winapi 0.2.8", -] - [[package]] name = "mio" version = "0.7.7" @@ -459,21 +340,9 @@ checksum = "e50ae3f04d169fcc9bde0b547d1c205219b7157e07ded9c5aff03e0637cb3ed7" dependencies = [ "libc", "log", - "miow 0.3.6", + "miow", "ntapi", - "winapi 0.3.9", -] - -[[package]] -name = "miow" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" -dependencies = [ - "kernel32-sys", - "net2", - "winapi 0.2.8", - "ws2_32-sys", + "winapi", ] [[package]] @@ -483,18 +352,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" dependencies = [ "socket2", - "winapi 0.3.9", -] - -[[package]] -name = "net2" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" -dependencies = [ - "cfg-if 0.1.10", - "libc", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -503,7 +361,7 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] @@ -531,12 +389,6 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" -[[package]] -name = "pin-project-lite" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" - [[package]] name = "pin-project-lite" version = "0.2.4" @@ -589,7 +441,7 @@ dependencies = [ "libc", "rand_core 0.3.1", "rdrand", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -731,9 +583,9 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -817,7 +669,7 @@ dependencies = [ "stdweb", "time-macros", "version_check", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -843,23 +695,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tokio" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092" -dependencies = [ - "bytes 0.5.6", - "fnv", - "iovec", - "lazy_static", - "memchr", - "mio 0.6.23", - "num_cpus", - "pin-project-lite 0.1.11", - "slab", -] - [[package]] name = "tokio" version = "1.2.0" @@ -867,12 +702,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8190d04c665ea9e6b6a0dc45523ade572c088d2e6566244c1122671dbf4ae3a" dependencies = [ "autocfg", - "bytes 1.0.1", + "bytes", "libc", "memchr", - "mio 0.7.7", + "mio", "num_cpus", - "pin-project-lite 0.2.4", + "pin-project-lite", "tokio-macros", ] @@ -905,7 +740,7 @@ version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen-macro", ] @@ -953,12 +788,6 @@ version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64" -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" - [[package]] name = "winapi" version = "0.3.9" @@ -969,12 +798,6 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" - [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -986,13 +809,3 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "ws2_32-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] diff --git a/Cargo.toml b/Cargo.toml index 154874a..b09f09c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,17 +7,24 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -background-processor = { git = "https://github.com/rust-bitcoin/rust-lightning", rev = "d6f41d3c0b38b9ec9e06a3acfdd9f4b1d007a27d" } +# background-processor = { git = "https://github.com/rust-bitcoin/rust-lightning", rev = "d6f41d3c0b38b9ec9e06a3acfdd9f4b1d007a27d" } +background-processor = { path = "../rust-lightning/background-processor" } base64 = "0.13.0" -bitcoin = "0.26" +bitcoin = "0.24" +bitcoin-bech32 = "0.7" hex = "0.3" -lightning = { git = "https://github.com/rust-bitcoin/rust-lightning", rev = "c35002fa9c16042badfa5e7bf819df5f1d2ae60a" } -# lightning-block-sync = { git = "https://github.com/valentinewallace/rust-lightning", rev = "21deebf64b4e6ba3f104dc6c68efa5d3abc4c0f7", features = ["tokio", "rpc-client"] } -lightning-block-sync = { path = "../rust-lightning/lightning-block-sync", features = ["rpc-client", "expose-rpc" ] } +# lightning = { git = "https://github.com/rust-bitcoin/rust-lightning", rev = "c35002fa9c16042badfa5e7bf819df5f1d2ae60a" } +lightning = { path = "../rust-lightning/lightning" } +lightning-block-sync = { path = "../rust-lightning/lightning-block-sync", features = ["rpc-client", "generic-rpc-client" ] } lightning-invoice = { git = "https://github.com/rust-bitcoin/rust-lightning-invoice", rev = "ea25dc7e46a6339493032c500db4fe3a8fdb1acd" } -lightning-net-tokio = { git = "https://github.com/rust-bitcoin/rust-lightning", rev = "ff00f6f8861419b73269e6c51d75ac9de75f1d1f" } -lightning-persister = { git = "https://github.com/rust-bitcoin/rust-lightning", rev = "aa127f55edc4439b03426644d178e402397329e8" } +# lightning-net-tokio = { git = "https://github.com/rust-bitcoin/rust-lightning", rev = "ff00f6f8861419b73269e6c51d75ac9de75f1d1f" } +lightning-net-tokio = { path = "../rust-lightning/lightning-net-tokio" } +# lightning-persister = { git = "https://github.com/rust-bitcoin/rust-lightning", rev = "aa127f55edc4439b03426644d178e402397329e8" } +lightning-persister = { path = "../rust-lightning/lightning-persister" } time = "0.2" rand = "0.4" serde_json = { version = "1.0" } -tokio = { version = "0.2", features = ["io-std", "io-util", "rt-threaded", "tcp", "time", "sync"] } +# tokio = { version = "0.2", features = ["io-std", "io-util", "rt-threaded", "tcp", "time", "sync"] } +# tokio = { version = "1.0", features = [ "full", "io-util", "macros", "rt", "rt-multi-thread", "sync", "net", "time" ] } +# tokio = { version = "1.0", features = [ "io-util", "macros", "rt", "rt-multi-thread", "sync", "net", "time" ] } +tokio = { version = "1.0", features = [ "io-util", "macros", "rt", "rt-multi-thread", "sync", "net", "time" ] } diff --git a/src/bitcoind_client.rs b/src/bitcoind_client.rs new file mode 100644 index 0000000..97e5556 --- /dev/null +++ b/src/bitcoind_client.rs @@ -0,0 +1,76 @@ +use base64; +use serde_json; + +use bitcoin::blockdata::transaction::Transaction; +use bitcoin::consensus::encode; +use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator}; +use lightning_block_sync::http::HttpEndpoint; +use lightning_block_sync::rpc::RpcClient; + +use std::sync::Mutex; + +pub struct BitcoindClient { + pub bitcoind_rpc_client: Mutex, +} + +impl BitcoindClient { + pub fn new(host: String, port: u16, path: Option, rpc_user: String, rpc_password: String) -> + std::io::Result + { + let mut http_endpoint = HttpEndpoint::for_host(host).with_port(port); + if let Some(p) = path { + http_endpoint = http_endpoint.with_path(p); + } + let rpc_credentials = base64::encode(format!("{}:{}", rpc_user, rpc_password)); + let bitcoind_rpc_client = RpcClient::new(&rpc_credentials, http_endpoint)?; + Ok(Self { + bitcoind_rpc_client: Mutex::new(bitcoind_rpc_client) + }) + } +} + +impl FeeEstimator for BitcoindClient { + fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u32 { + let mut rpc_client_guard = self.bitcoind_rpc_client.lock().unwrap(); + match confirmation_target { + ConfirmationTarget::Background => { + let conf_target = serde_json::json!(144); + let estimate_mode = serde_json::json!("ECONOMICAL"); + let resp = rpc_client_guard.call_method("estimatesmartfee", + &vec![conf_target, estimate_mode]).unwrap(); + if !resp["errors"].is_null() && resp["errors"].as_array().unwrap().len() > 0 { + return 253 + } + resp["feerate"].as_u64().unwrap() as u32 + }, + ConfirmationTarget::Normal => { + let conf_target = serde_json::json!(18); + let estimate_mode = serde_json::json!("ECONOMICAL"); + let resp = rpc_client_guard.call_method("estimatesmartfee", + &vec![conf_target, estimate_mode]).unwrap(); + if !resp["errors"].is_null() && resp["errors"].as_array().unwrap().len() > 0 { + return 253 + } + resp["feerate"].as_u64().unwrap() as u32 + }, + ConfirmationTarget::HighPriority => { + let conf_target = serde_json::json!(6); + let estimate_mode = serde_json::json!("CONSERVATIVE"); + let resp = rpc_client_guard.call_method("estimatesmartfee", + &vec![conf_target, estimate_mode]).unwrap(); + if !resp["errors"].is_null() && resp["errors"].as_array().unwrap().len() > 0 { + return 253 + } + resp["feerate"].as_u64().unwrap() as u32 + }, + } + } +} + +impl BroadcasterInterface for BitcoindClient { + fn broadcast_transaction(&self, tx: &Transaction) { + let mut rpc_client_guard = self.bitcoind_rpc_client.lock().unwrap(); + let tx_serialized = serde_json::json!(encode::serialize_hex(tx)); + rpc_client_guard.call_method("sendrawtransaction", &vec![tx_serialized]).unwrap(); + } +} diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..c046d0c --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,100 @@ +use bitcoin::secp256k1::key::PublicKey; +use crate::{FilesystemLogger, PeerManager, ChannelManager}; +use crate::utils; +use std::io; +use std::io::{BufRead, Write}; +use std::net::{SocketAddr, TcpStream}; +use std::sync::Arc; +use std::thread; +use std::time::Duration; +// use tokio::runtime::Runtime; +use tokio::runtime::{Builder, Runtime}; +use tokio::sync::mpsc; + +pub fn poll_for_user_input(peer_manager: Arc, channel_manager: Arc, event_notifier: mpsc::Sender<()>) { + println!("LDK startup successful. To view available commands: \"help\".\nLDK logs are available in the `logs` folder of the current directory."); + print!("> "); io::stdout().flush().unwrap(); // Without flushing, the `>` doesn't print + let stdin = io::stdin(); + for line in stdin.lock().lines() { + let line = line.unwrap(); + let mut words = line.split_whitespace(); + if let Some(word) = words.next() { + match word { + "help" => handle_help(), + "openchannel" => { + let peer_pubkey_and_ip_addr = words.next(); + let channel_value_sat = words.next(); + if peer_pubkey_and_ip_addr.is_none() || channel_value_sat.is_none() { + println!("ERROR: openchannel takes 2 arguments: `openchannel pubkey@host:port channel_amt_satoshis`") + } else { + let peer_pubkey_and_ip_addr = peer_pubkey_and_ip_addr.unwrap(); + let channel_value_sat = channel_value_sat.unwrap(); + let mut pubkey_and_addr = peer_pubkey_and_ip_addr.split("@"); + let pubkey = pubkey_and_addr.next(); + let peer_addr_str = pubkey_and_addr.next(); + if peer_addr_str.is_none() || peer_addr_str.is_none() { + println!("ERROR: incorrectly formatted peer info. Should be formatted as: `pubkey@host:port`"); + } else { + let pubkey = pubkey.unwrap(); + let peer_addr_str = peer_addr_str.unwrap(); + let peer_addr_res: Result = peer_addr_str.parse(); + let chan_amt: Result = channel_value_sat.parse(); + if let Some(pk) = utils::hex_to_compressed_pubkey(pubkey) { + if let Ok(addr) = peer_addr_res { + if let Ok(amt) = chan_amt { + handle_open_channel(pk, addr, amt, + peer_manager.clone(), + channel_manager.clone(), + event_notifier.clone()); + } else { + println!("ERROR: channel amount must be a number"); + } + } else { + println!("ERROR: couldn't parse + pubkey@host:port into a socket address"); + } + } else { + println!("ERROR: unable to parse given pubkey for node"); + } + } + } + }, + _ => println!("hello") + } + } + print!("> "); io::stdout().flush().unwrap(); + + } +} + +fn handle_help() { + println!("") +} + +fn handle_open_channel(peer_pubkey: PublicKey, peer_socket: SocketAddr, + channel_amt_sat: u64, peer_manager: Arc, + channel_manager: Arc, event_notifier: + mpsc::Sender<()>) +{ + // let runtime = Runtime::new().expect("Unable to create a runtime").enable_all(); + let runtime = Builder::new_multi_thread() + .worker_threads(3) + .enable_all() + .build() + .unwrap(); + match TcpStream::connect_timeout(&peer_socket, Duration::from_secs(10)) { + Ok(stream) => { + runtime.block_on(|| { + lightning_net_tokio::setup_outbound(peer_manager, + event_notifier, + peer_pubkey, + stream); + }); + match channel_manager.create_channel(peer_pubkey, channel_amt_sat, 0, 0, None) { + Ok(_) => println!("SUCCESS: channel created! Mine some blocks to open it."), + Err(e) => println!("ERROR: failed to open channel: {:?}", e) + } + }, + Err(e) => println!("ERROR: failed to connect to peer: {:?}", e) + } +} diff --git a/src/main.rs b/src/main.rs index d8b0f23..f740cd1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,60 +1,232 @@ -use base64; -use serde_json; +mod bitcoind_client; +mod cli; +mod utils; -use lightning::chain::chaininterface::{ConfirmationTarget, FeeEstimator}; +use background_processor::BackgroundProcessor; +use bitcoin::{BlockHash, Txid}; +use bitcoin::blockdata::constants::genesis_block; +use bitcoin::blockdata::transaction::Transaction; +use bitcoin::consensus::encode; +use bitcoin::hashes::hex::FromHex; +use bitcoin::network::constants::Network; +use bitcoin::secp256k1::Secp256k1; +use bitcoin::util::address::Address; +use bitcoin_bech32::WitnessProgram; +use crate::bitcoind_client::BitcoindClient; +use lightning::chain; +use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator}; +use lightning::chain::chainmonitor::ChainMonitor; +use lightning::chain::channelmonitor::ChannelMonitor; +use lightning::chain::Filter; +use lightning::chain::keysinterface::{InMemorySigner, KeysInterface, KeysManager}; +use lightning::chain::transaction::OutPoint; +use lightning::chain::Watch; +use lightning::ln::channelmanager; +use lightning::ln::channelmanager::{ChannelManagerReadArgs, PaymentHash, PaymentPreimage, + SimpleArcChannelManager}; +use lightning::ln::peer_handler::{MessageHandler, SimpleArcPeerManager}; +use lightning::util::config::UserConfig; +use lightning::util::events::{Event, EventsProvider}; use lightning::util::logger::{Logger, Record}; +use lightning::util::ser::{ReadableArgs, Writer}; +use lightning_block_sync::UnboundedCache; +use lightning_block_sync::SpvClient; use lightning_block_sync::http::HttpEndpoint; +use lightning_block_sync::init; +use lightning_block_sync::poll; +use lightning_block_sync::poll::{ChainTip, Poll}; use lightning_block_sync::rpc::RpcClient; +use lightning_net_tokio::SocketDescriptor; +use lightning_persister::FilesystemPersister; +use rand::{thread_rng, Rng}; +use lightning::routing::network_graph::NetGraphMsgHandler; +use std::cell::RefCell; +use std::collections::HashMap; +use std::fs; +use std::fs::File; +use std::io::Cursor; +use std::path::Path; +use std::str::FromStr; +use std::sync::{Arc, Mutex}; +use std::thread; +use std::time::{Duration, SystemTime}; +use time::OffsetDateTime; +use tokio::runtime::Runtime; +use tokio::sync::mpsc; -use std::sync::Mutex; +const NETWORK: Network = Network::Regtest; -pub struct BitcoindFeeEstimator { - bitcoind_rpc_client: Mutex, +pub struct FilesystemLogger{} +impl Logger for FilesystemLogger { + fn log(&self, record: &Record) { + let raw_log = record.args.to_string(); + let log = format!("{} {:<5} [{}:{}] {}", OffsetDateTime::now_utc().format("%F %T"), + record.level.to_string(), record.module_path, record.line, raw_log); + fs::create_dir_all("logs").unwrap(); + fs::OpenOptions::new().create(true).append(true).open("./logs/logs.txt").unwrap() + .write_all(log.as_bytes()).unwrap(); + } } -impl BitcoindFeeEstimator { - fn new(host: String, port: u16, path: Option, rpc_user: String, rpc_password: String) -> - std::io::Result - { - let mut http_endpoint = HttpEndpoint::for_host(host).with_port(port); - if let Some(p) = path { - http_endpoint = http_endpoint.with_path(p); +fn read_channelmonitors_from_disk(path: String, keys_manager: Arc) -> + Result)>, std::io::Error> +{ + if !Path::new(&path).exists() { + return Ok(HashMap::new()) + } + let mut outpoint_to_channelmonitor = HashMap::new(); + for file_option in fs::read_dir(path).unwrap() { + let file = file_option.unwrap(); + let owned_file_name = file.file_name(); + let filename = owned_file_name.to_str(); + if !filename.is_some() || !filename.unwrap().is_ascii() || filename.unwrap().len() < 65 { + return Err(std::io::Error::new(std::io::ErrorKind::Other, "Invalid ChannelMonitor file name")); + } + + let txid = Txid::from_hex(filename.unwrap().split_at(64).0); + if txid.is_err() { + return Err(std::io::Error::new(std::io::ErrorKind::Other, "Invalid tx ID in filename")); } - let rpc_credentials = base64::encode(format!("{}:{}", rpc_user, rpc_password)); - let bitcoind_rpc_client = RpcClient::new(&rpc_credentials, http_endpoint)?; - Ok(Self { - bitcoind_rpc_client: Mutex::new(bitcoind_rpc_client) - }) + + let index = filename.unwrap().split_at(65).1.split('.').next().unwrap().parse(); + if index.is_err() { + return Err(std::io::Error::new(std::io::ErrorKind::Other, "Invalid tx index in filename")); + } + + let contents = fs::read(&file.path())?; + + if let Ok((blockhash, channel_monitor)) = + <(BlockHash, ChannelMonitor)>::read(&mut Cursor::new(&contents), + &*keys_manager) + { + outpoint_to_channelmonitor.insert(OutPoint { txid: txid.unwrap(), index: index.unwrap() }, + (blockhash, channel_monitor)); + } else { + return Err(std::io::Error::new(std::io::ErrorKind::Other, + "Failed to deserialize ChannelMonitor")); + } } + Ok(outpoint_to_channelmonitor) +} + +type Invoice = String; + +enum HTLCDirection { + Inbound, + Outbound } -impl FeeEstimator for BitcoindFeeEstimator { - fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u32 { - let mut rpc_client_guard = self.bitcoind_rpc_client.lock().unwrap(); - match confirmation_target { - ConfirmationTarget::Background => { - let conf_target = serde_json::json!(144); - let estimate_mode = serde_json::json!("ECONOMICAL"); - let resp = rpc_client_guard.call_method("estimatesmartfee", - &vec![conf_target, estimate_mode]).unwrap(); - resp["feerate"].as_u64().unwrap() as u32 - }, - ConfirmationTarget::Normal => { - let conf_target = serde_json::json!(18); - let estimate_mode = serde_json::json!("ECONOMICAL"); - let resp = rpc_client_guard.call_method("estimatesmartfee", - &vec![conf_target, estimate_mode]).unwrap(); - resp["feerate"].as_u64().unwrap() as u32 - }, - ConfirmationTarget::HighPriority => { - let conf_target = serde_json::json!(6); - let estimate_mode = serde_json::json!("CONSERVATIVE"); - let resp = rpc_client_guard.call_method("estimatesmartfee", - &vec![conf_target, estimate_mode]).unwrap(); - resp["feerate"].as_u64().unwrap() as u32 - }, +type PaymentInfoStorage = Arc, HTLCDirection)>>>; + +type ArcChainMonitor = ChainMonitor, Arc, +Arc, Arc, Arc>; + +pub(crate) type PeerManager = SimpleArcPeerManager; + +pub(crate) type ChannelManager = SimpleArcChannelManager; + + +fn handle_ldk_events(peer_manager: Arc, channel_manager: Arc, + chain_monitor: Arc, bitcoind_rpc_client: Arc, + keys_manager: Arc, mut pending_txs: HashMap, + htlcs: PaymentInfoStorage) -> HashMap +{ + peer_manager.process_events(); + let mut check_for_more_events = true; + while check_for_more_events { + let loop_channel_manager = channel_manager.clone(); + check_for_more_events = false; + let mut events = channel_manager.get_and_clear_pending_events(); + events.append(&mut chain_monitor.get_and_clear_pending_events()); + let mut rpc = bitcoind_rpc_client.bitcoind_rpc_client.lock().unwrap(); + for event in events { + match event { + Event::FundingGenerationReady { temporary_channel_id, channel_value_satoshis, + output_script, .. } => { + let addr = WitnessProgram::from_scriptpubkey(&output_script[..], match NETWORK { + Network::Bitcoin => bitcoin_bech32::constants::Network::Bitcoin, + Network::Testnet => bitcoin_bech32::constants::Network::Testnet, + Network::Regtest => bitcoin_bech32::constants::Network::Regtest, + } + ).expect("Lightning funding tx should always be to a SegWit output").to_address(); + let outputs = format!("{{\"{}\": {}}}", addr, channel_value_satoshis as f64 / 1_000_000_00.0).to_string(); + let tx_hex = rpc.call_method("createrawtransaction", &vec![serde_json::json!(outputs)]).unwrap(); + let raw_tx = format!("\"{}\"", tx_hex.as_str().unwrap()).to_string(); + let funded_tx = rpc.call_method("fundrawtransaction", &vec![serde_json::json!(raw_tx)]).unwrap(); + let change_output_position = funded_tx["changepos"].as_i64().unwrap(); + assert!(change_output_position == 0 || change_output_position == 1); + let funded_tx = format!("\"{}\"", funded_tx["hex"].as_str().unwrap()).to_string(); + let signed_tx = rpc.call_method("signrawtransactionwithwallet", + &vec![serde_json::json!(funded_tx)]).unwrap(); + assert_eq!(signed_tx["complete"].as_bool().unwrap(), true); + let final_tx: Transaction = encode::deserialize(&utils::hex_to_vec(&signed_tx["hex"].as_str().unwrap()).unwrap()).unwrap(); + let outpoint = OutPoint { + txid: final_tx.txid(), + index: if change_output_position == 0 { 1 } else { 0 } + }; + loop_channel_manager.funding_transaction_generated(&temporary_channel_id, outpoint); + pending_txs.insert(outpoint, final_tx); + check_for_more_events = true; + }, + Event::FundingBroadcastSafe { funding_txo, .. } => { + let funding_tx = pending_txs.remove(&funding_txo).unwrap(); + bitcoind_rpc_client.broadcast_transaction(&funding_tx); + }, + Event::PaymentReceived { payment_hash, payment_secret, amt: amt_msat } => { + let payment_info = htlcs.lock().unwrap(); + if let Some(htlc_info) = payment_info.get(&payment_hash) { + assert!(loop_channel_manager.claim_funds(htlc_info.1.unwrap().clone(), + &payment_secret, amt_msat)); + } else { + loop_channel_manager.fail_htlc_backwards(&payment_hash, &payment_secret); + } + check_for_more_events = true; + }, + Event::PaymentSent { payment_preimage } => { + let payment_info = htlcs.lock().unwrap(); + for (invoice, preimage_option, _) in payment_info.values() { + if let Some(preimage) = preimage_option { + if payment_preimage == *preimage { + println!("NEW EVENT: successfully sent payment from invoice {} with preimage {}", + invoice, utils::hex_str(&payment_preimage.0)); + } + } + } + }, + Event::PaymentFailed { payment_hash, rejected_by_dest } => { + let payment_info = htlcs.lock().unwrap(); + let htlc_info = payment_info.get(&payment_hash).unwrap(); + print!("NEW EVENT: Failed to send payment to invoice {}:", htlc_info.0); + if rejected_by_dest { + println!("rejected by destination node"); + } else { + println!("route failed"); + } + }, + Event::PendingHTLCsForwardable { .. } => { + loop_channel_manager.process_pending_htlc_forwards(); + check_for_more_events = true; + }, + Event::SpendableOutputs { outputs } => { + let addr_args = vec![serde_json::json!("LDK output address")]; + let destination_address_str = rpc.call_method("getnewaddress", &addr_args).unwrap(); + let destination_address = Address::from_str(destination_address_str.as_str().unwrap()).unwrap(); + let output_descriptors = &outputs.iter().map(|a| a).collect::>(); + let tx_feerate = bitcoind_rpc_client.get_est_sat_per_1000_weight(ConfirmationTarget::Normal); + let spending_tx = keys_manager.spend_spendable_outputs(output_descriptors, + Vec::new(), + destination_address.script_pubkey(), + tx_feerate, &Secp256k1::new()).unwrap(); + bitcoind_rpc_client.broadcast_transaction(&spending_tx); + // XXX maybe need to rescan and blah? but contrary to what matt's saying, it + // looks like spend_spendable's got us covered + } + } } } + pending_txs } fn main() { @@ -62,7 +234,191 @@ fn main() { let bitcoind_port = 18443; let rpc_user = "polaruser".to_string(); let rpc_password = "polarpass".to_string(); - let fee_estimator = BitcoindFeeEstimator::new(bitcoind_host, bitcoind_port, None, rpc_user, rpc_password).unwrap(); - let normal_fee = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Normal); - println!("VMW: {}", normal_fee); + let bitcoind_client = Arc::new(BitcoindClient::new(bitcoind_host.clone(), bitcoind_port, None, + rpc_user.clone(), rpc_password.clone()).unwrap()); + + // ## Setup + // Step 1: Initialize the FeeEstimator + let fee_estimator = bitcoind_client.clone(); + + // Step 2: Initialize the Logger + let logger = Arc::new(FilesystemLogger{}); + + // Step 3: Initialize the BroadcasterInterface + let broadcaster = bitcoind_client.clone(); + + // Step 4: Initialize Persist + let persister = Arc::new(FilesystemPersister::new(".".to_string())); + + // Step 5: Initialize the ChainMonitor + let chain_monitor: Arc = Arc::new(ChainMonitor::new(None, broadcaster.clone(), + logger.clone(), fee_estimator.clone(), + persister.clone())); + + // Step 6: Initialize the KeysManager + let node_privkey = if let Ok(seed) = fs::read("./key_seed") { // the private key that corresponds + assert_eq!(seed.len(), 32); // to our lightning node's pubkey + let mut key = [0; 32]; + key.copy_from_slice(&seed); + key + } else { + let mut key = [0; 32]; + thread_rng().fill_bytes(&mut key); + let mut f = File::create("./key_seed").unwrap(); + f.write_all(&key).expect("Failed to write seed to disk"); + f.sync_all().expect("Failed to sync seed to disk"); + key + }; + let cur = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); + let keys_manager = Arc::new(KeysManager::new(&node_privkey, cur.as_secs(), cur.subsec_nanos())); + + // Step 7: Read ChannelMonitor state from disk + let mut outpoint_to_channelmonitor = read_channelmonitors_from_disk("./monitors".to_string(), + keys_manager.clone()).unwrap(); + + // Step 9: Read ChannelManager state from disk + let user_config = UserConfig::default(); + let mut channel_manager: ChannelManager; + let mut channel_manager_last_blockhash: Option = None; + if let Ok(mut f) = fs::File::open("./manager") { + let (last_block_hash_option, channel_manager_from_disk) = { + let mut channel_monitor_mut_references = Vec::new(); + for (_, channel_monitor) in outpoint_to_channelmonitor.iter_mut() { + channel_monitor_mut_references.push(&mut channel_monitor.1); + } + let read_args = ChannelManagerReadArgs::new(keys_manager.clone(), fee_estimator.clone(), + chain_monitor.clone(), broadcaster.clone(), + logger.clone(), user_config, + channel_monitor_mut_references); + <(Option, ChannelManager)>::read(&mut f, read_args).unwrap() + }; + channel_manager = channel_manager_from_disk; + channel_manager_last_blockhash = last_block_hash_option; + } else { + let mut bitcoind_rpc_client = bitcoind_client.bitcoind_rpc_client.lock().unwrap(); + let current_chain_height: usize = bitcoind_rpc_client + .call_method("getblockchaininfo", &vec![]).unwrap()["blocks"].as_u64().unwrap() as usize; + channel_manager = channelmanager::ChannelManager::new(Network::Regtest, fee_estimator.clone(), + chain_monitor.clone(), broadcaster.clone(), + logger.clone(), keys_manager.clone(), + user_config, current_chain_height); + } + + // Step 10: Sync ChannelMonitors to chain tip if restarting + let mut chain_tip = None; + let mut chain_listener_channel_monitors = Vec::new(); + let mut cache = UnboundedCache::new(); + let rpc_credentials = base64::encode(format!("{}:{}", rpc_user, rpc_password)); + let mut block_source = RpcClient::new(&rpc_credentials, HttpEndpoint::for_host(bitcoind_host) + .with_port(bitcoind_port)).unwrap(); + let runtime = Runtime::new().expect("Unable to create a runtime"); + if outpoint_to_channelmonitor.len() > 0 { + for (outpoint, blockhash_and_monitor) in outpoint_to_channelmonitor.drain() { + let blockhash = blockhash_and_monitor.0; + let channel_monitor = blockhash_and_monitor.1; + chain_listener_channel_monitors.push((blockhash, (RefCell::new(channel_monitor), + broadcaster.clone(), fee_estimator.clone(), + logger.clone()), outpoint)); + } + + let mut chain_listeners = Vec::new(); + for monitor_listener_info in chain_listener_channel_monitors.iter_mut() { + chain_listeners.push((monitor_listener_info.0, + &mut monitor_listener_info.1 as &mut dyn chain::Listen)); + } + // Because `sync_listeners` is an async function and we want to run it synchronously, + // we run it in a tokio Runtime. + chain_tip = Some(runtime.block_on(init::sync_listeners(&mut block_source, Network::Regtest, + &mut cache, chain_listeners)).unwrap()); + } + + // Step 11: Give ChannelMonitors to ChainMonitor + if chain_listener_channel_monitors.len() > 0 { + for item in chain_listener_channel_monitors.drain(..) { + let channel_monitor = item.1.0.into_inner(); + let funding_outpoint = item.2; + chain_monitor.watch_channel(funding_outpoint, channel_monitor).unwrap(); + } + } + + // Step 12: Sync ChannelManager to chain tip if restarting + if let Some(channel_manager_blockhash) = channel_manager_last_blockhash { + let chain_listener = vec![ + (channel_manager_blockhash, &mut channel_manager as &mut dyn chain::Listen)]; + chain_tip = Some(runtime.block_on(init::sync_listeners(&mut block_source, Network::Regtest, + &mut cache, chain_listener)).unwrap()); + } + + // Step 13: Optional: Initialize the NetGraphMsgHandler + // XXX persist routing data + let genesis = genesis_block(Network::Regtest).header.block_hash(); + let router = Arc::new(NetGraphMsgHandler::new(genesis, None::>, logger.clone())); + + // Step 14: Initialize the PeerManager + let channel_manager = Arc::new(channel_manager); + let mut ephemeral_bytes = [0; 32]; + rand::thread_rng().fill_bytes(&mut ephemeral_bytes); + let lightning_msg_handler = MessageHandler { chan_handler: channel_manager.clone(), + route_handler: router.clone() }; + let peer_manager: Arc = Arc::new(PeerManager::new(lightning_msg_handler, + keys_manager.get_node_secret(), + &ephemeral_bytes, logger.clone())); + + // ## Running LDK + // Step 15: Initialize LDK Event Handling + let (event_ntfn_sender, mut event_ntfn_receiver) = mpsc::channel(2); + let peer_manager_event_listener = peer_manager.clone(); + let channel_manager_event_listener = channel_manager.clone(); + let chain_monitor_event_listener = chain_monitor.clone(); + let payment_info: PaymentInfoStorage = Arc::new(Mutex::new(HashMap::new())); + let payment_info_for_events = payment_info.clone(); + thread::spawn(move || async move { + let mut pending_txs = HashMap::new(); + loop { + event_ntfn_receiver.recv().await.unwrap(); + pending_txs = handle_ldk_events(peer_manager_event_listener.clone(), + channel_manager_event_listener.clone(), + chain_monitor_event_listener.clone(), + bitcoind_client.clone(), keys_manager.clone(), + pending_txs, payment_info_for_events.clone()); + } + }); + + // Step 16: Initialize Peer Connection Handling + let peer_manager_connection_handler = peer_manager.clone(); + let event_notifier = event_ntfn_sender.clone(); + thread::spawn(move || async move { + let listener = std::net::TcpListener::bind("0.0.0.0:9735").unwrap(); + loop { + let tcp_stream = listener.accept().unwrap().0; + lightning_net_tokio::setup_inbound(peer_manager_connection_handler.clone(), + event_notifier.clone(), tcp_stream).await; + } + }); + + // Step 17: Connect and Disconnect Blocks + let mut chain_poller = poll::ChainPoller::new(&mut block_source, Network::Regtest); + if chain_tip.is_none() { + match runtime.block_on(chain_poller.poll_chain_tip(None)).unwrap() { + ChainTip::Better(header) => chain_tip = Some(header), + _ => panic!("Unexpected chain tip") + } + } + let chain_listener = (chain_monitor.clone(), channel_manager.clone()); + let _spv_client = SpvClient::new(chain_tip.unwrap(), chain_poller, &mut cache, &chain_listener); + + // Step 17 & 18: Initialize ChannelManager persistence & Once Per Minute: ChannelManager's + // timer_chan_freshness_every_min() and PeerManager's timer_tick_occurred + let persist_channel_manager_callback = move |node: &ChannelManager| { + FilesystemPersister::persist_manager("./".to_string(), &*node) + }; + BackgroundProcessor::start(persist_channel_manager_callback, channel_manager.clone(), logger.clone()); + let peer_manager_processor = peer_manager.clone(); + thread::spawn(move || { + loop { + peer_manager_processor.timer_tick_occured(); + thread::sleep(Duration::new(60, 0)); + } + }); + cli::poll_for_user_input(peer_manager.clone(), channel_manager.clone(), event_ntfn_sender.clone()); } diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..5ca6a4d --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,42 @@ +use bitcoin::secp256k1::key::PublicKey; + +pub fn hex_to_vec(hex: &str) -> Option> { + let mut out = Vec::with_capacity(hex.len() / 2); + + let mut b = 0; + for (idx, c) in hex.as_bytes().iter().enumerate() { + b <<= 4; + match *c { + b'A'..=b'F' => b |= c - b'A' + 10, + b'a'..=b'f' => b |= c - b'a' + 10, + b'0'..=b'9' => b |= c - b'0', + _ => return None, + } + if (idx & 1) == 1 { + out.push(b); + b = 0; + } + } + + Some(out) +} + +#[inline] +pub fn hex_str(value: &[u8]) -> String { + let mut res = String::with_capacity(64); + for v in value { + res += &format!("{:02x}", v); + } + res +} + +pub fn hex_to_compressed_pubkey(hex: &str) -> Option { + let data = match hex_to_vec(&hex[0..33*2]) { + Some(bytes) => bytes, + None => return None + }; + match PublicKey::from_slice(&data) { + Ok(pk) => Some(pk), + Err(_) => None, + } +} -- 2.39.5