diff --git a/Cargo.lock b/Cargo.lock index 6dd32815..b45f4807 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,35 +3,14 @@ version = 3 [[package]] -name = "aes-ctr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7729c3cde54d67063be556aeac75a81330d802f0259500ca40cb52967f975763" -dependencies = [ - "aes-soft", - "aesni", - "cipher", - "ctr", -] - -[[package]] -name = "aes-soft" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" -dependencies = [ - "cipher", - "opaque-debug", -] - -[[package]] -name = "aesni" -version = "0.10.0" +name = "aes" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ + "cfg-if", "cipher", - "opaque-debug", + "cpufeatures", ] [[package]] @@ -63,7 +42,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -72,6 +51,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "149f75bbec1827618262e0855a68f0f9a7f2edc13faebf33c4f16d6725edb6a9" +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "atty" version = "0.2.14" @@ -89,28 +74,146 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base58" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" + +[[package]] +name = "base58ck" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" +dependencies = [ + "bitcoin-internals 0.3.0", + "bitcoin_hashes 0.14.0", +] + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bech32" -version = "0.9.1" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" + +[[package]] +name = "bip324" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +checksum = "b443a76f86143c093b211628be683ee592a097d316db6b90f723ed816bde1a49" +dependencies = [ + "bitcoin", + "bitcoin_hashes 0.15.0", + "chacha20-poly1305", + "rand", +] [[package]] name = "bitcoin" -version = "0.29.2" +version = "0.32.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0694ea59225b0c5f3cb405ff3f670e4828358ed26aec49dc352f730f0cb1a8a3" +checksum = "ce6bc65742dea50536e35ad42492b234c27904a27f0abdcbce605015cb4ea026" dependencies = [ + "base58ck", "bech32", - "bitcoin_hashes", + "bitcoin-internals 0.3.0", + "bitcoin-io 0.1.3", + "bitcoin-units", + "bitcoin_hashes 0.14.0", + "hex-conservative 0.2.1", + "hex_lit", "secp256k1", ] +[[package]] +name = "bitcoin-internals" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" + +[[package]] +name = "bitcoin-internals" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" + +[[package]] +name = "bitcoin-internals" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b854212e29b96c8f0fe04cab11d57586c8f3257de0d146c76cb3b42b3eb9118" + +[[package]] +name = "bitcoin-io" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" + +[[package]] +name = "bitcoin-io" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26792cd2bf245069a1c5acb06aa7ad7abe1de69b507c90b490bca81e0665d0ee" +dependencies = [ + "bitcoin-internals 0.4.0", +] + +[[package]] +name = "bitcoin-units" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" +dependencies = [ + "bitcoin-internals 0.3.0", +] + [[package]] name = "bitcoin_hashes" -version = "0.11.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" +checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" +dependencies = [ + "bitcoin-internals 0.2.0", + "hex-conservative 0.1.2", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "bitcoin-io 0.1.3", + "hex-conservative 0.2.1", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0982261c82a50d89d1a411602afee0498b3e0debe3d36693f0c661352809639" +dependencies = [ + "bitcoin-io 0.2.0", + "hex-conservative 0.3.0", +] + +[[package]] +name = "bitcoin_num" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "125b6a0228ba7c5f2462f6481db417fff24c4646cf77ee0d44202fd87dbd584a" [[package]] name = "bitflags" @@ -118,6 +221,15 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "cc" version = "1.0.74" @@ -130,6 +242,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chacha20-poly1305" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ac8be588b1de2b7f1537ed39ba453a388d2cce60ce78ef5db449f71bebe58ba" + [[package]] name = "chrono" version = "0.4.22" @@ -142,22 +260,27 @@ dependencies = [ [[package]] name = "cipher" -version = "0.2.5" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "generic-array", + "crypto-common", + "inout", ] [[package]] name = "coldcard" -version = "0.5.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a4b7b647ac3591f204635a704dcbf23b74f0b2be60dd23c9b2aa63caac4eb19" +checksum = "9aaaf3f7409edc40001c30a4c1337f21558a8ceba2a4afe807da841a38ce83d6" dependencies = [ - "aes-ctr", - "bitcoin", + "aes", + "base58", + "bitcoin_hashes 0.13.0", + "ctr", "hidapi", + "k256", + "rand", ] [[package]] @@ -171,6 +294,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "crossbeam-channel" version = "0.5.6" @@ -190,15 +328,103 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "ctr" -version = "0.6.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ "cipher", ] +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.103", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "env_logger" version = "0.8.4" @@ -211,13 +437,23 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + [[package]] name = "generic-array" version = "0.14.6" @@ -226,6 +462,7 @@ checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -239,6 +476,17 @@ dependencies = [ "wasi", ] +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + [[package]] name = "heck" version = "0.4.0" @@ -254,15 +502,65 @@ dependencies = [ "libc", ] +[[package]] +name = "hex-conservative" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" + +[[package]] +name = "hex-conservative" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "hex-conservative" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afe881d0527571892c4034822e59bb10c6c991cce6abe8199b6f5cf10766f55" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + [[package]] name = "hidapi" -version = "1.4.2" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d26e1151deaab68f34fbfd16d491a2a0170cf98d69d3efa23873b567a4199e1" +checksum = "03b876ecf37e86b359573c16c8366bc3eba52b689884a0fc42ba3f67203d2a8b" dependencies = [ "cc", + "cfg-if", "libc", "pkg-config", + "windows-sys", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", ] [[package]] @@ -274,6 +572,20 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -282,9 +594,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.137" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "log" @@ -318,7 +630,7 @@ checksum = "6e3afa8ee462bb9ed735b544476a9e4ece0f10be76864ff9b80a6a525b837232" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -347,7 +659,7 @@ dependencies = [ "quickcheck", "quickcheck_macros", "tempfile", - "thiserror", + "thiserror 1.0.37", ] [[package]] @@ -367,7 +679,7 @@ dependencies = [ "quickcheck", "quickcheck_macros", "tempfile", - "thiserror", + "thiserror 1.0.37", ] [[package]] @@ -375,13 +687,14 @@ name = "nakamoto-common" version = "0.4.0" dependencies = [ "bitcoin", - "bitcoin_hashes", + "bitcoin_hashes 0.14.0", + "bitcoin_num", "fastrand", "log", "microserde", "nakamoto-net", "nonempty", - "thiserror", + "thiserror 2.0.11", ] [[package]] @@ -392,18 +705,21 @@ dependencies = [ "fastrand", "log", "quickcheck", - "thiserror", + "thiserror 1.0.37", ] [[package]] name = "nakamoto-net-poll" version = "0.4.0" dependencies = [ + "bip324", "crossbeam-channel", "fastrand", "libc", "log", + "nakamoto-common", "nakamoto-net", + "nakamoto-p2p", "popol", "quickcheck", "quickcheck_macros", @@ -420,8 +736,9 @@ dependencies = [ "colored", "log", "nakamoto-client", + "nakamoto-common", "nakamoto-net-poll", - "thiserror", + "thiserror 1.0.37", ] [[package]] @@ -429,6 +746,7 @@ name = "nakamoto-p2p" version = "0.4.0" dependencies = [ "crossbeam-channel", + "derivative", "fastrand", "log", "microserde", @@ -439,7 +757,7 @@ dependencies = [ "quickcheck", "quickcheck_macros", "tempfile", - "thiserror", + "thiserror 1.0.37", ] [[package]] @@ -474,14 +792,14 @@ dependencies = [ "sqlite3-src", "sqlite3-sys", "termion", - "thiserror", + "thiserror 1.0.37", ] [[package]] name = "nonempty" -version = "0.7.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" +checksum = "549e471b99ccaf2f89101bec68f4d244457d5a95a9c3d0672e9564124397741d" [[package]] name = "num-integer" @@ -510,15 +828,19 @@ checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" [[package]] name = "once_cell" -version = "1.17.1" +version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] -name = "opaque-debug" -version = "0.3.0" +name = "pkcs8" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] [[package]] name = "pkg-config" @@ -543,9 +865,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -569,14 +891,14 @@ checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -655,26 +977,60 @@ dependencies = [ "winapi", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "sec1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48518a2b5775ba8ca5b46596aae011caa431e6ce7e4a67ead66d92f08884220e" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "secp256k1" -version = "0.24.1" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff55dc09d460954e9ef2fa8a7ced735a964be9981fd50e870b2b3b0705e14964" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ - "bitcoin_hashes", - "rand", + "bitcoin_hashes 0.14.0", "secp256k1-sys", ] [[package]] name = "secp256k1-sys" -version = "0.6.1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" dependencies = [ "cc", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "signal-hook" version = "0.3.14" @@ -694,6 +1050,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + [[package]] name = "socket2" version = "0.4.7" @@ -704,6 +1070,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "sqlite" version = "0.28.1" @@ -734,6 +1110,12 @@ dependencies = [ "sqlite3-src", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "1.0.103" @@ -745,6 +1127,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tempfile" version = "3.3.0" @@ -777,7 +1170,16 @@ version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.37", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl 2.0.11", ] [[package]] @@ -788,7 +1190,18 @@ checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", ] [[package]] @@ -836,3 +1249,75 @@ 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 = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/client/src/client.rs b/client/src/client.rs index 85c301be..f2b1f657 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -18,15 +18,15 @@ use nakamoto_chain::filter::cache::FilterCache; use nakamoto_chain::filter::cache::StoredHeader; use nakamoto_chain::{block::cache::BlockCache, filter::BlockFilter}; -use nakamoto_common::bitcoin::network::constants::ServiceFlags; -use nakamoto_common::bitcoin::network::message::NetworkMessage; -use nakamoto_common::bitcoin::network::Address; -use nakamoto_common::bitcoin::util::uint::Uint256; +use nakamoto_common::bitcoin::p2p::message::NetworkMessage; +use nakamoto_common::bitcoin::p2p::Address; +use nakamoto_common::bitcoin::p2p::ServiceFlags; use nakamoto_common::bitcoin::Txid; +use nakamoto_common::bitcoin_num::uint::Uint256; use nakamoto_common::block::store::{Genesis as _, Store as _}; use nakamoto_common::block::time::{AdjustedTime, RefClock}; use nakamoto_common::block::tree::{self, BlockReader, ImportResult}; -use nakamoto_common::block::{BlockHash, BlockHeader, Height, Transaction}; +use nakamoto_common::block::{BlockHash, Header, Height, Transaction}; use nakamoto_common::nonempty::NonEmpty; use nakamoto_common::p2p::peer::{Source, Store as _}; use nakamoto_p2p::fsm; @@ -70,6 +70,8 @@ pub struct Config { pub services: ServiceFlags, /// Configured limits. pub limits: Limits, + /// P2P_v2 + pub p2p_v2: bool, } /// Configuration for loading event handling. @@ -126,6 +128,7 @@ impl Default for Config { hooks: Hooks::default(), limits: Limits::default(), services: ServiceFlags::NONE, + p2p_v2: false, } } } @@ -165,7 +168,7 @@ where /// Runs a pre-loaded client. pub struct ClientRunner { service: Service< - BlockCache>, + BlockCache>, FilterCache>, peer::Cache, RefClock>, @@ -249,7 +252,7 @@ impl Client { /// Load the client configuration. Takes a loading handler that can optionally receive /// loading events. pub fn load( - self, + mut self, config: Config, loading: impl Into, ) -> Result, Error> { @@ -379,6 +382,9 @@ impl Client { log::info!(target: "client", "{} seeds added to address book", peers.len()); } + self.reactor + .configure_network(config.network.to_str(), config.p2p_v2); + Ok(ClientRunner { listen, commands: self.commands, @@ -413,6 +419,11 @@ impl Client { pub fn handle(&self) -> Handle { self.handle.clone() } + + /// Configures network and p2p version used by the client + pub fn configure_network(&mut self, network: String, is_v2: bool) { + self.reactor.configure_network(network, is_v2); + } } /// An instance of [`handle::Handle`] for [`Client`]. @@ -473,21 +484,21 @@ impl Handle { } impl handle::Handle for Handle { - fn get_tip(&self) -> Result<(Height, BlockHeader, Uint256), handle::Error> { - let (transmit, receive) = chan::bounded::<(Height, BlockHeader, Uint256)>(1); + fn get_tip(&self) -> Result<(Height, Header, Uint256), handle::Error> { + let (transmit, receive) = chan::bounded::<(Height, Header, Uint256)>(1); self._command(Command::GetTip(transmit))?; Ok(receive.recv()?) } - fn get_block(&self, hash: &BlockHash) -> Result, handle::Error> { + fn get_block(&self, hash: &BlockHash) -> Result, handle::Error> { let (transmit, receive) = chan::bounded(1); self._command(Command::GetBlockByHash(*hash, transmit))?; Ok(receive.recv()?) } - fn get_block_by_height(&self, height: Height) -> Result, handle::Error> { + fn get_block_by_height(&self, height: Height) -> Result, handle::Error> { let (sender, recvr) = chan::bounded(1); self._command(Command::GetBlockByHeight(height, sender))?; @@ -508,7 +519,7 @@ impl handle::Handle for Handle { fn find_branch( &self, to: &BlockHash, - ) -> Result)>, handle::Error> { + ) -> Result)>, handle::Error> { let to = *to; let (transmit, receive) = chan::bounded(1); @@ -602,22 +613,6 @@ impl handle::Handle for Handle { Ok(()) } - fn import_headers( - &self, - headers: Vec, - ) -> Result, handle::Error> { - let (transmit, receive) = chan::bounded::>(1); - self.command(Command::ImportHeaders(headers, transmit))?; - - Ok(receive.recv()?) - } - - fn import_addresses(&self, addrs: Vec
) -> Result<(), handle::Error> { - self.command(Command::ImportAddresses(addrs))?; - - Ok(()) - } - fn submit_transaction( &self, tx: Transaction, @@ -634,6 +629,22 @@ impl handle::Handle for Handle { Ok(receive.recv()?) } + fn import_headers( + &self, + headers: Vec
, + ) -> Result, handle::Error> { + let (transmit, receive) = chan::bounded::>(1); + self.command(Command::ImportHeaders(headers, transmit))?; + + Ok(receive.recv()?) + } + + fn import_addresses(&self, addrs: Vec
) -> Result<(), handle::Error> { + self.command(Command::ImportAddresses(addrs))?; + + Ok(()) + } + fn wait(&self, f: F) -> Result where F: FnMut(fsm::Event) -> Option, diff --git a/client/src/tests.rs b/client/src/tests.rs index ef473147..adb9451d 100644 --- a/client/src/tests.rs +++ b/client/src/tests.rs @@ -40,9 +40,10 @@ fn network( let genesis = cfg.network.genesis(); let params = cfg.network.params(); - let node = Client::::new()?; + let mut node = Client::::new()?; let mut handle = node.handle(); - handle.set_timeout(time::Duration::from_secs(5)); + handle.set_timeout(time::Duration::from_secs(7)); + node.configure_network(cfg.network.to_str(), cfg.p2p_v2); let t = thread::spawn({ let params = params.clone(); @@ -114,6 +115,85 @@ fn test_full_sync() { } } +#[test] +fn test_full_sync_v2() { + logger::init(log::Level::Debug); + + let cfgs = vec![ + Config { + services: ServiceFlags::NETWORK | ServiceFlags::P2P_V2, + p2p_v2: true, + ..Config::default() + }; + 3 + ]; + let nodes = network(&cfgs).unwrap(); + let (handle, _, _) = nodes.last().unwrap(); + let headers = BITCOIN_HEADERS.tail.clone(); + let height = headers.len() as Height; + let hash = headers.last().unwrap().block_hash(); + + // Ensure all peers are connected to misha, + // so that misha can effectively send blocks to + // all peers on time. + handle.wait_for_peers(2, Services::Chain).unwrap(); + + handle + .import_headers(headers) + .expect("command is successful") + .expect("chain is valid"); + + for (mut node, _, thread) in nodes.into_iter() { + node.set_timeout(std::time::Duration::from_secs(5)); + assert_eq!(node.wait_for_height(height).unwrap(), hash); + + node.shutdown().unwrap(); + thread.join().unwrap(); + } +} + +#[test] +#[ignore = "failing"] +fn test_full_sync_v1_v2() { + logger::init(log::Level::Debug); // true true false falla / true false false / false true false / + + let cfgs = vec![ + Config { + services: ServiceFlags::NETWORK | ServiceFlags::P2P_V2, + p2p_v2: true, + ..Config::default() + }, + Config { + services: ServiceFlags::NETWORK | ServiceFlags::BLOOM, + p2p_v2: false, + ..Config::default() + }, + ]; + let nodes = network(&cfgs).unwrap(); + let (handle, _, _) = nodes.last().unwrap(); + let headers = BITCOIN_HEADERS.tail.clone(); + let height = headers.len() as Height; + let hash = headers.last().unwrap().block_hash(); + + // Ensure all peers are connected to misha, + // so that misha can effectively send blocks to + // all peers on time. + handle.wait_for_peers(1, Services::Chain).unwrap(); + + handle + .import_headers(headers) + .expect("command is successful") + .expect("chain is valid"); + + for (mut node, _, thread) in nodes.into_iter() { + node.set_timeout(std::time::Duration::from_secs(6)); + assert_eq!(node.wait_for_height(height).unwrap(), hash); + + node.shutdown().unwrap(); + thread.join().unwrap(); + } +} + #[test] fn test_wait_for_peers() { logger::init(log::Level::Debug); diff --git a/common/src/network.rs b/common/src/network.rs index 7ad0e519..b070e0e3 100644 --- a/common/src/network.rs +++ b/common/src/network.rs @@ -96,6 +96,16 @@ impl Network { } } + /// Converts the network to String + pub fn to_str(self) -> String { + match self { + Network::Mainnet => String::from("mainnet"), + Network::Testnet => String::from("testnet"), + Network::Regtest => String::from("regtest"), + Network::Signet => String::from("signet"), + } + } + /// Blockchain checkpoints. pub fn checkpoints(&self) -> Box> { use crate::block::checkpoints; diff --git a/net/poll/Cargo.toml b/net/poll/Cargo.toml index 6e1d5898..66098b04 100644 --- a/net/poll/Cargo.toml +++ b/net/poll/Cargo.toml @@ -10,11 +10,14 @@ license = "MIT" [dependencies] nakamoto-net = { version = "0.4.0", path = ".." } +nakamoto-common = { version = "0.4.0", path = "../../common" } +nakamoto-p2p = { version = "0.4.0", path = "../../p2p" } crossbeam-channel = { version = "0.5.6" } popol = { version = "2" } socket2 = { version = "0.4" } libc = { version = "0.2" } log = { version = "0.4" } +bip324 = "0.6.0" [dev-dependencies] fastrand = "1.3.5" diff --git a/net/poll/src/bip324_info.rs b/net/poll/src/bip324_info.rs new file mode 100644 index 00000000..0c5b173f --- /dev/null +++ b/net/poll/src/bip324_info.rs @@ -0,0 +1,33 @@ +use bip324::{Handshake, PacketHandler}; +use nakamoto_net::{LocalDuration, LocalTime}; + +/// Version content is always empty for the current version of the protocol. +pub const VERSION_CONTENT: [u8; 0] = []; +/// Number of bytes for the garbage terminator. +pub const GARBAGE_TERMINATOR_BYTES: usize = 16; +/// Number of bytes used to indicate size when decrypting a message +pub const DEFAULT_SIZE_BYTES_V2: usize = 3; +/// Size of an ElliSwift key +pub const ELLI_SWIFT_KEY_SIZE: usize = 64; +/// Maxi possible size of the buffer containing the Elliswift key +pub const MAX_GARBAGE_BUFFER_BYTES: usize = 36; +/// Duration after a P2P_V2 should fall back to V1 +pub const BIP324_HANDSHAKE_TIMEOUT: LocalDuration = LocalDuration::from_secs(5); + +#[derive(Default)] +pub struct Bip324Info { + pub key_sent: Option>, + pub key_received: Option>, + pub terminator_sent: Option>, + pub garbage_received: Vec, + pub packet_handler: Option, + pub handshake: Option>>, + pub message_buffer: MessageBuffer, + pub handshake_started: Option, +} + +#[derive(Default)] +pub struct MessageBuffer { + pub pending_bytes: usize, + pub buffer: Vec, +} diff --git a/net/poll/src/lib.rs b/net/poll/src/lib.rs index 54a6416e..734e6153 100644 --- a/net/poll/src/lib.rs +++ b/net/poll/src/lib.rs @@ -34,6 +34,7 @@ pub mod time; pub use reactor::{Reactor, Waker}; +mod bip324_info; #[cfg(test)] mod fallible; diff --git a/net/poll/src/reactor.rs b/net/poll/src/reactor.rs index 436f606b..b20338a7 100644 --- a/net/poll/src/reactor.rs +++ b/net/poll/src/reactor.rs @@ -9,25 +9,29 @@ use nakamoto_net::{Link, Service}; use log::*; +use crate::bip324_info; +use crate::bip324_info::Bip324Info; +use crate::fallible; +use crate::socket::Socket; +use crate::time::TimeoutManager; +use bip324::{Handshake, Network, PacketType, PacketWriter, Role}; +use nakamoto_p2p::{DisconnectReason, Event}; use std::borrow::Cow; use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use std::io; use std::io::prelude::*; use std::net; +use std::net::TcpStream; use std::os::unix::io::AsRawFd; use std::sync::Arc; use std::time; use std::time::SystemTime; -use crate::fallible; -use crate::socket::Socket; -use crate::time::TimeoutManager; - /// Maximum time to wait when reading from a socket. -const READ_TIMEOUT: time::Duration = time::Duration::from_secs(6); +const READ_TIMEOUT: time::Duration = time::Duration::from_secs(3); /// Maximum time to wait when writing to a socket. -const WRITE_TIMEOUT: time::Duration = time::Duration::from_secs(3); +const WRITE_TIMEOUT: time::Duration = time::Duration::from_secs(6); /// Maximum amount of time to wait for i/o. const WAIT_TIMEOUT: LocalDuration = LocalDuration::from_mins(60); /// Socket read buffer size. @@ -59,6 +63,7 @@ impl nakamoto_net::Waker for Waker { /// A single-threaded non-blocking reactor. pub struct Reactor { + is_v2: bool, peers: HashMap>, connecting: HashSet, sources: popol::Sources>, @@ -66,6 +71,8 @@ pub struct Reactor { timeouts: TimeoutManager<()>, shutdown: chan::Receiver<()>, listening: chan::Sender, + pub network: Network, + bip324_info: HashMap, } /// The `R` parameter represents the underlying stream type, eg. `net::TcpStream`. @@ -76,7 +83,10 @@ impl Reactor { self.sources .register(Source::Peer(addr.clone()), &stream, popol::interest::ALL); self.peers - .insert(addr, Socket::from(stream, socket_addr, link)); + .insert(addr.clone(), Socket::from(stream, socket_addr, link)); + if self.is_v2 { + self.bip324_info.insert(addr, Bip324Info::default()); + } } /// Unregister a peer from the reactor. @@ -91,9 +101,20 @@ impl Reactor { self.connecting.remove(&addr); self.peers.remove(&addr); self.sources.unregister(&Source::Peer(addr.clone())); + if self.is_v2 { + self.bip324_info.remove(&addr); + } service.disconnected(&addr, reason); } + + fn set_p2p_v2(&mut self, flag: bool) { + self.is_v2 = flag; + } + + fn set_network(&mut self, network: Network) { + self.network = network; + } } impl nakamoto_net::Reactor for Reactor { @@ -105,11 +126,14 @@ impl nakamoto_net::Reactor for Reactor { listening: chan::Sender, ) -> Result { let peers = HashMap::new(); - + let bip324_info = HashMap::new(); + let is_v2 = false; let mut sources = popol::Sources::new(); let waker = Waker::new(&mut sources)?; let timeouts = TimeoutManager::new(LocalDuration::from_secs(1)); let connecting = HashSet::new(); + // This is the default, but it will be set via set_network() + let network = Network::Testnet; Ok(Self { peers, @@ -119,6 +143,9 @@ impl nakamoto_net::Reactor for Reactor { timeouts, shutdown, listening, + bip324_info, + is_v2, + network, }) } @@ -155,7 +182,15 @@ impl nakamoto_net::Reactor for Reactor { let local_time = SystemTime::now().into(); service.initialize(local_time); - self.process(&mut service, &mut publisher, local_time); + info!( + target: "reactor", + "P2P mode: v{} running on network: {}", + if self.is_v2 { "2" } else { "1" }, + self.network + ); + + let mut io_queue: Vec, Event, DisconnectReason, Id>> = Vec::new(); + self.process(&mut service, &mut publisher, local_time, &mut io_queue); // I/O readiness events populated by `popol::Sources::wait_timeout`. let mut events = Vec::with_capacity(32); @@ -202,7 +237,6 @@ impl nakamoto_net::Reactor for Reactor { self.sources.unregister(&ev.key); continue; } - if ev.is_writable() { self.handle_writable(addr.clone(), &ev.key, &mut service)?; } @@ -230,9 +264,8 @@ impl nakamoto_net::Reactor for Reactor { let local_addr = conn.local_addr()?; let link = Link::Inbound; - self.register_peer(addr.clone(), conn, link); - - service.connected(addr, &local_addr, link); + self.register_peer(addr.clone(), conn.try_clone()?, link); + service.connected(addr.clone(), &local_addr, link); } }, Source::Waker => { @@ -270,7 +303,7 @@ impl nakamoto_net::Reactor for Reactor { } Err(err) => return Err(err.into()), } - self.process(&mut service, &mut publisher, local_time); + self.process(&mut service, &mut publisher, local_time, &mut io_queue); } } @@ -280,25 +313,64 @@ impl nakamoto_net::Reactor for Reactor { fn waker(&self) -> Self::Waker { self.waker.clone() } + + fn configure_network(&mut self, network: String, is_v2: bool) { + let network = match network.as_str() { + "mainnet" => Network::Bitcoin, + "testnet" => Network::Testnet, + "regtest" => Network::Regtest, + "signet" => Network::Signet, + _ => Network::Bitcoin, + }; + self.set_network(network); + self.set_p2p_v2(is_v2); + } } impl Reactor { /// Process service state machine outputs. - fn process(&mut self, service: &mut S, publisher: &mut E, local_time: LocalTime) - where + fn process( + &mut self, + service: &mut S, + publisher: &mut E, + local_time: LocalTime, + io_queue: &mut Vec, Event, DisconnectReason, Id>>, + ) where S: Service, E: Publisher, S::DisconnectReason: Into>, { + if self.is_v2 { + self.check_and_handle_handshake_timeouts(local_time, io_queue); + self.filter_io_queue(io_queue); + } + // Note that there may be messages destined for a peer that has since been // disconnected. while let Some(out) = service.next() { match out { Io::Write(addr, bytes) => { - if let Some(socket) = self.peers.get_mut(&addr) { - if let Some(source) = self.sources.get_mut(&Source::Peer(addr)) { - socket.push(&bytes); - source.set(popol::interest::WRITE); + if let Some(socket) = self.peers.get_mut(&addr.clone()) { + if let Some(source) = self.sources.get_mut(&Source::Peer(addr.clone())) { + if let Some(bip324_info) = &mut self.bip324_info.get_mut(&addr) { + if let Some(packet_handler) = &mut bip324_info.packet_handler { + let packet = packet_handler + .writer() + .encrypt_packet( + bytes.clone().as_slice(), + None, + PacketType::Genuine, + ) + .unwrap(); + socket.push(&packet); + } else { + io_queue.push(Io::Write(addr, bytes)); + } + source.set(popol::interest::WRITE); + } else { + socket.push(&bytes); + source.set(popol::interest::WRITE); + } } } } @@ -309,10 +381,22 @@ impl Reactor { match self::dial(&socket_addr) { Ok(stream) => { trace!("{:#?}", stream); - - self.register_peer(addr.clone(), stream, Link::Outbound); + let link = Link::Outbound; + self.register_peer(addr.clone(), stream.try_clone().unwrap(), link); self.connecting.insert(addr.clone()); + if self.is_v2 { + let mut socket = Socket::from(stream, socket_addr, link); + if let Some(bip324_info) = self.bip324_info.get_mut(&addr) { + Self::send_elli_swift( + bip324_info, + Role::Initiator, + &mut socket, + LocalTime::now(), + ); + } + } + service.attempted(&addr); } Err(e) if e.kind() == io::ErrorKind::AlreadyExists => { @@ -351,58 +435,6 @@ impl Reactor { } } - fn handle_readable(&mut self, addr: Id, service: &mut S) - where - S: Service, - { - // Nb. If the socket was readable and writable at the same time, and it was disconnected - // during an attempt to write, it will no longer be registered and hence available - // for reads. - if let Some(socket) = self.peers.get_mut(&addr) { - let mut buffer = [0; READ_BUFFER_SIZE]; - - let socket_addr = addr.to_socket_addr(); - trace!("{}: Socket is readable", socket_addr); - - // Nb. Since `poll`, which this reactor is based on, is *level-triggered*, - // we will be notified again if there is still data to be read on the socket. - // Hence, there is no use in putting this socket read in a loop, as the second - // invocation would likely block. - match socket.read(&mut buffer) { - Ok(count) => { - if count > 0 { - trace!("{}: Read {} bytes", socket_addr, count); - - service.message_received(&addr, Cow::Borrowed(&buffer[..count])); - } else { - trace!("{}: Read 0 bytes", socket_addr); - // If we get zero bytes read as a return value, it means the peer has - // performed an orderly shutdown. - socket.disconnect().ok(); - self.unregister_peer( - addr, - Disconnect::ConnectionError(Arc::new(io::Error::from( - io::ErrorKind::ConnectionReset, - ))), - service, - ); - } - } - Err(err) if err.kind() == io::ErrorKind::WouldBlock => { - // This shouldn't normally happen, since this function is only called - // when there's data on the socket. We leave it here in case external - // conditions change. - } - Err(err) => { - trace!("{}: Read error: {}", socket_addr, err.to_string()); - - socket.disconnect().ok(); - self.unregister_peer(addr, Disconnect::ConnectionError(Arc::new(err)), service); - } - } - } - } - fn handle_writable>( &mut self, addr: Id, @@ -425,7 +457,6 @@ impl Reactor { service.connected(addr.clone(), &local_addr, socket.link); } - match socket.flush() { // In this case, we've written all the data, we // are no longer interested in writing to this @@ -450,6 +481,357 @@ impl Reactor { } Ok(()) } + + fn handle_readable(&mut self, addr: Id, service: &mut S) + where + S: Service, + { + // Nb. If the socket was readable and writable at the same time, and it was disconnected + // during an attempt to write, it will no longer be registered and hence available + // for reads. + if let Some(socket) = self.peers.get_mut(&addr) { + let mut buffer = [0; READ_BUFFER_SIZE]; + + let socket_addr = addr.to_socket_addr(); + trace!("{}: Socket is readable", socket_addr); + + // Nb. Since `poll`, which this reactor is based on, is *level-triggered*, + // we will be notified again if there is still data to be read on the socket. + // Hence, there is no use in putting this socket read in a loop, as the second + // invocation would likely block. + + match socket.read(&mut buffer) { + Ok(0) => { + trace!("{}: Read 0 bytes", socket_addr); + // Peer has performed an orderly shutdown + socket.disconnect().ok(); + self.unregister_peer( + addr.clone(), + Disconnect::ConnectionError(Arc::new(io::Error::from( + io::ErrorKind::ConnectionReset, + ))), + service, + ); + } + Ok(mut count) => { + trace!("{}: Read {} bytes", socket_addr, count); + if let Some(bip324_info) = self.bip324_info.get_mut(&addr) { + while count > 0 { + if bip324_info.key_received.is_none() { + if self.network.magic() + == nakamoto_common::bitcoin::p2p::Magic::from_bytes( + buffer[..4].try_into().expect("first 4 bytes"), + ) + { + self.bip324_info.remove(&addr); + service + .message_received(&addr, Cow::Borrowed(&buffer[..count])); + return; + } + bip324_info.key_received = + Some(buffer[..bip324_info::ELLI_SWIFT_KEY_SIZE].to_vec()); + if socket.link == Link::Inbound { + Self::send_elli_swift( + bip324_info, + Role::Responder, + socket, + LocalTime::now(), + ); + } + Self::create_and_send_terminator(bip324_info, socket); + count -= bip324_info::ELLI_SWIFT_KEY_SIZE; + buffer.rotate_left(bip324_info::ELLI_SWIFT_KEY_SIZE); + } else if bip324_info.garbage_received.len() + < bip324_info::MAX_GARBAGE_BUFFER_BYTES + && bip324_info.packet_handler.is_none() + { + match Self::handle_garbage(&mut buffer, &mut count, bip324_info) { + Ok(()) => { + info!(target: "reactor", "handshake done with {}", socket_addr); + } + Err(bip324::Error::CiphertextTooSmall) => {} + Err(_) => { + self.bip324_info.remove(&addr); + break; + } + } + } else { + Self::handle_encrypted_message( + &buffer, + count, + bip324_info, + &addr, + service, + ); + count = 0; + } + } + } else { + service.message_received(&addr, Cow::Borrowed(&buffer[..count])); + } + } + Err(err) if err.kind() == io::ErrorKind::WouldBlock => { + // This shouldn't normally happen, since this function is only called + // when there's data on the socket. We leave it here in case external + // conditions change. + } + Err(err) => { + trace!("{}: Read error: {}", socket_addr, err); + socket.disconnect().ok(); + self.unregister_peer(addr, Disconnect::ConnectionError(Arc::new(err)), service); + } + } + } + } + + fn handle_garbage( + buffer: &mut [u8], + count: &mut usize, + bip324_info: &mut Bip324Info, + ) -> Result<(), bip324::Error> { + let reading_bytes_number = bip324_info::MAX_GARBAGE_BUFFER_BYTES.min(*count); + bip324_info + .garbage_received + .extend(buffer[..reading_bytes_number].to_vec()); + + if let Some(mut handshake) = bip324_info.handshake.take() { + return match handshake.authenticate_garbage_and_version(&bip324_info.garbage_received) { + Ok(()) => { + bip324_info.packet_handler = Some(handshake.finalize()?); + *count -= reading_bytes_number; + buffer.rotate_left(reading_bytes_number); + Ok(()) + } + Err(bip324::Error::CiphertextTooSmall) => { + *count -= reading_bytes_number; + // remove rotate left to optimize + buffer.rotate_left(reading_bytes_number); + Err(bip324::Error::CiphertextTooSmall) + } + Err(_) => Err(bip324::Error::HandshakeOutOfOrder), + }; + } + if bip324_info.garbage_received.len() < bip324_info::MAX_GARBAGE_BUFFER_BYTES { + Err(bip324::Error::CiphertextTooSmall) + } else { + Err(bip324::Error::TooMuchGarbage) + } + } + + fn handle_encrypted_message( + buffer: &[u8], + count: usize, + bip324_info: &mut Bip324Info, + addr: &Id, + service: &mut S, + ) where + S: Service, + { + if let Some(packet_handler) = &mut bip324_info.packet_handler { + let message = &buffer[..count]; + let pending_bytes = bip324_info.message_buffer.pending_bytes; + let mut index = 0; + let mut result_buffer = vec![]; + + if pending_bytes > 0 { + if pending_bytes <= count { + bip324_info + .message_buffer + .buffer + .extend_from_slice(&message[..pending_bytes]); + + if let Ok(result_message) = packet_handler.reader().decrypt_payload( + bip324_info.message_buffer.buffer.clone()[..] + .try_into() + .unwrap(), + None, + ) { + if result_message.packet_type() == PacketType::Genuine { + result_buffer.extend_from_slice(result_message.contents()); + } + } + + bip324_info.message_buffer.buffer.clear(); + bip324_info.message_buffer.pending_bytes = 0; + index = pending_bytes; + } else { + bip324_info.message_buffer.buffer.extend_from_slice(message); + bip324_info.message_buffer.pending_bytes -= count; + index = count; + } + } + + while index < count { + if index + bip324_info::DEFAULT_SIZE_BYTES_V2 > count { + bip324_info.message_buffer.pending_bytes = count - index; + bip324_info + .message_buffer + .buffer + .extend_from_slice(&message[index..count]); + break; + } + + let mut current_buffer = vec![]; + + current_buffer + .extend_from_slice(&message[index..index + bip324_info::DEFAULT_SIZE_BYTES_V2]); + + let length = packet_handler + .reader() + .decypt_len(current_buffer[..].try_into().unwrap()); + current_buffer.clear(); + + let next_index = index + bip324_info::DEFAULT_SIZE_BYTES_V2; + let finish = next_index + length; + + if finish > count { + bip324_info.message_buffer.pending_bytes = finish - count; + bip324_info + .message_buffer + .buffer + .extend_from_slice(&message[next_index..count]); + break; + } else { + current_buffer.extend_from_slice(&message[next_index..finish]); + + if let Ok(result_message) = packet_handler + .reader() + .decrypt_payload(current_buffer[..].try_into().unwrap(), None) + { + result_buffer.extend_from_slice(result_message.contents()); + } + + index = finish; + current_buffer.clear(); + } + } + + if !result_buffer.is_empty() { + service.message_received(addr, Cow::Borrowed(&result_buffer)); + } + } else { + service.message_received(addr, Cow::Borrowed(&buffer[..count])); + } + } + + fn send_elli_swift( + bip324_info: &mut Bip324Info, + role: Role, + socket: &mut Socket, + time: LocalTime, + ) { + let mut elli_swift_buffer = vec![0u8; 64]; + if let Ok(handshake) = Handshake::new(Network::Regtest, role, None, &mut elli_swift_buffer) + { + bip324_info.key_sent = Some(elli_swift_buffer.to_vec()); + bip324_info.handshake = Some(Box::from(handshake)); + bip324_info.handshake_started = Some(time); + + socket.push(&elli_swift_buffer); + } + } + + fn create_and_send_terminator(bip324_info: &mut Bip324Info, socket: &mut Socket) { + let num_version_packet_bytes = + PacketWriter::required_packet_allocation(&bip324_info::VERSION_CONTENT); + let mut terminator_and_version_buffer = + vec![0u8; bip324_info::GARBAGE_TERMINATOR_BYTES + num_version_packet_bytes]; + if let Some(handshake) = &mut bip324_info.handshake { + handshake + .complete_materials( + bip324_info + .key_received + .clone() + .unwrap() + .try_into() + .unwrap(), + &mut terminator_and_version_buffer, + None, + ) + .unwrap(); + } + socket.push(&terminator_and_version_buffer); + socket.flush().expect("flushed"); + bip324_info.terminator_sent = Some(terminator_and_version_buffer.to_vec()); + } + + fn check_and_handle_handshake_timeouts( + &mut self, + local_time: LocalTime, + io_queue: &mut Vec, Event, DisconnectReason, Id>>, + ) { + let mut fallback_peers = Vec::new(); + + for (addr, bip324_info) in &mut self.bip324_info { + if bip324_info.key_received.is_some() || bip324_info.handshake_started.is_none() { + continue; + } + + let started_at = bip324_info.handshake_started.unwrap(); + let elapsed = local_time - started_at; + + if elapsed >= bip324_info::BIP324_HANDSHAKE_TIMEOUT { + fallback_peers.push(addr.clone()); + } + } + + for addr in fallback_peers { + if let Some(socket) = self.peers.get_mut(&addr) { + if let Some(source) = self.sources.get_mut(&Source::Peer(addr.clone())) { + self.bip324_info.remove(&addr); + + io_queue.retain(|io| match io { + Io::Write(io_addr, bytes) if io_addr == &addr => { + socket.push(bytes); + socket.flush().expect("flushed"); + source.set(popol::interest::WRITE); + false + } + _ => true, + }); + + info!(target: "net", "{}: Fallback to v1 protocol after handshake timeout", + addr.to_socket_addr()); + } + } + } + } + + fn filter_io_queue(&mut self, io_queue: &mut Vec, Event, DisconnectReason, Id>>) { + io_queue.retain(|io| match io { + Io::Write(addr, bytes) => { + let socket = match self.peers.get_mut(addr) { + Some(socket) => socket, + None => return false, + }; + + let source = match self.sources.get_mut(&Source::Peer(addr.clone())) { + Some(source) => source, + None => return false, + }; + + if let Some(bip324_info) = &mut self.bip324_info.get_mut(addr) { + if let Some(packet_handler) = &mut bip324_info.packet_handler { + let packet = packet_handler + .writer() + .encrypt_packet(bytes.clone().as_slice(), None, PacketType::Genuine) + .unwrap(); + + socket.push(&packet); + source.set(popol::interest::WRITE); + false + } else { + true + } + } else { + socket.push(bytes); + source.set(popol::interest::WRITE); + false + } + } + _ => false, + }); + } } /// Connect to a peer given a remote address. diff --git a/node/Cargo.toml b/node/Cargo.toml index f1c5f7f1..8c1d9c2a 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -12,6 +12,7 @@ license = "MIT" [dependencies] nakamoto-client = { version = "0.4.0", path = "../client" } nakamoto-net-poll = { version = "0.4.0", path = "../net/poll" } +nakamoto-common = { version = "0.4.0", path = "../common" } argh = "0.1.3" colored = "1.9" atty = { version = "0.2" } diff --git a/node/src/lib.rs b/node/src/lib.rs index 17af4083..85291600 100644 --- a/node/src/lib.rs +++ b/node/src/lib.rs @@ -6,6 +6,7 @@ use std::path::PathBuf; pub use nakamoto_client::{Client, Config, Error, Network}; pub use nakamoto_client::{Domain, LoadingHandler}; +pub use nakamoto_common::bitcoin::p2p::ServiceFlags; pub mod logger; @@ -30,6 +31,7 @@ pub fn run( } else { listen.to_vec() }, + p2p_v2, ..Config::default() }; if let Some(path) = root { @@ -38,6 +40,9 @@ pub fn run( if !connect.is_empty() { cfg.limits.max_outbound_peers = connect.len(); } + if p2p_v2 { + cfg.services |= ServiceFlags::P2P_V2 + } Client::::new()?.run(cfg) } diff --git a/node/src/main.rs b/node/src/main.rs index 499b71b1..19afda75 100644 --- a/node/src/main.rs +++ b/node/src/main.rs @@ -29,6 +29,10 @@ pub struct Options { #[argh(switch)] pub regtest: bool, + /// use P2P v2 + #[argh(switch)] + pub p2p_v2: bool, + /// only connect to IPv4 addresses (default: false) #[argh(switch, short = '4')] pub ipv4: bool,