]> git.bitcoin.ninja Git - ldk-sample/commitdiff
Initial node
authorValentine Wallace <vwallace@protonmail.com>
Tue, 2 Mar 2021 20:54:17 +0000 (15:54 -0500)
committerValentine Wallace <vwallace@protonmail.com>
Mon, 3 May 2021 22:29:24 +0000 (18:29 -0400)
Cargo.lock
Cargo.toml
src/bitcoind_client.rs [new file with mode: 0644]
src/cli.rs [new file with mode: 0644]
src/main.rs
src/utils.rs [new file with mode: 0644]

index f167acc1c6b65ab3dfb427f1a46c334fe4e11602..cdd933575df20acef24de9a971ebdd6a0530f8ff 100644 (file)
@@ -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",
-]
index 154874a4b7b7d13a6db4db7cdd98bc482f6ec442..b09f09cbdfe523066d0516c146a9e6cb53ad9ffa 100644 (file)
@@ -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 (file)
index 0000000..97e5556
--- /dev/null
@@ -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<RpcClient>,
+}
+
+impl BitcoindClient {
+    pub fn new(host: String, port: u16, path: Option<String>, rpc_user: String, rpc_password: String) ->
+        std::io::Result<Self>
+    {
+        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 (file)
index 0000000..c046d0c
--- /dev/null
@@ -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<PeerManager>, channel_manager: Arc<ChannelManager>, 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<SocketAddr, _> = peer_addr_str.parse();
+                            let chan_amt: Result<u64, _> = 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<PeerManager>,
+                       channel_manager: Arc<ChannelManager>, 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)
+    }
+}
index d8b0f23b415305d6a4c07c7889067c782ba0c1ad..f740cd1d604c32e1c379ebcc2a53106b99dc4533 100644 (file)
-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<RpcClient>,
+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<String>, rpc_user: String, rpc_password: String) ->
-        std::io::Result<Self>
-    {
-        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<KeysManager>) ->
+    Result<HashMap<OutPoint, (BlockHash, ChannelMonitor<InMemorySigner>)>, 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<InMemorySigner>)>::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<Mutex<HashMap<PaymentHash, (Invoice, Option<PaymentPreimage>, HTLCDirection)>>>;
+
+type ArcChainMonitor = ChainMonitor<InMemorySigner, Arc<dyn Filter>, Arc<BitcoindClient>,
+Arc<BitcoindClient>, Arc<FilesystemLogger>, Arc<FilesystemPersister>>;
+
+pub(crate) type PeerManager = SimpleArcPeerManager<SocketDescriptor, ArcChainMonitor, BitcoindClient,
+BitcoindClient, dyn chain::Access, FilesystemLogger>;
+
+pub(crate) type ChannelManager = SimpleArcChannelManager<ArcChainMonitor, BitcoindClient, BitcoindClient,
+FilesystemLogger>;
+
+
+fn handle_ldk_events(peer_manager: Arc<PeerManager>, channel_manager: Arc<ChannelManager>,
+                     chain_monitor: Arc<ArcChainMonitor>, bitcoind_rpc_client: Arc<BitcoindClient>,
+                     keys_manager: Arc<KeysManager>, mut pending_txs: HashMap<OutPoint, Transaction>,
+                     htlcs: PaymentInfoStorage) -> HashMap<OutPoint, Transaction>
+{
+    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::<Vec<_>>();
+                    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<ArcChainMonitor> = 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<BlockHash> = 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<BlockHash>, 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::<Arc<dyn chain::Access>>, 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<PeerManager> = 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 (file)
index 0000000..5ca6a4d
--- /dev/null
@@ -0,0 +1,42 @@
+use bitcoin::secp256k1::key::PublicKey;
+
+pub fn hex_to_vec(hex: &str) -> Option<Vec<u8>> {
+         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<PublicKey> {
+         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,
+         }
+}