From 26323fc73772d74d8941620a3c4be4119176715e Mon Sep 17 00:00:00 2001 From: aidan46 Date: Fri, 27 Dec 2024 13:50:19 +0800 Subject: [PATCH 01/34] feat: Add P2P functionality to storage server Add P2P bootstrap and register behaviour to the polka-storage-provider-server. --- Cargo.lock | 651 ++++++++++++++++-- Cargo.toml | 2 + storage-provider/server/Cargo.toml | 6 + .../server/p2p-example-config/bootstrap.toml | 2 + .../server/p2p-example-config/private.pem | 3 + .../server/p2p-example-config/public.pem | 3 + .../server/p2p-example-config/register.toml | 3 + storage-provider/server/src/main.rs | 39 +- storage-provider/server/src/p2p.rs | 220 ++++++ storage-provider/server/src/p2p/bootstrap.rs | 49 ++ storage-provider/server/src/p2p/register.rs | 53 ++ 11 files changed, 964 insertions(+), 67 deletions(-) create mode 100644 storage-provider/server/p2p-example-config/bootstrap.toml create mode 100644 storage-provider/server/p2p-example-config/private.pem create mode 100644 storage-provider/server/p2p-example-config/public.pem create mode 100644 storage-provider/server/p2p-example-config/register.toml create mode 100644 storage-provider/server/src/p2p.rs create mode 100644 storage-provider/server/src/p2p/bootstrap.rs create mode 100644 storage-provider/server/src/p2p/register.rs diff --git a/Cargo.lock b/Cargo.lock index 356615726..a23fb424b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,6 +1160,19 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "asynchronous-codec" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a860072022177f903e59730004fb5dc13db9275b79bb2aef7ba8ce831956c233" +dependencies = [ + "bytes", + "futures-sink", + "futures-util", + "memchr", + "pin-project-lite", +] + [[package]] name = "atomic-take" version = "1.1.0" @@ -1420,6 +1433,12 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "bimap" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" + [[package]] name = "binary-merkle-tree" version = "13.0.0" @@ -4072,6 +4091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", + "pem-rfc7468", "zeroize", ] @@ -5864,6 +5884,16 @@ dependencies = [ "futures-util", ] +[[package]] +name = "futures-bounded" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91f328e7fb845fc832912fb6a34f40cf6d1888c92f974d1893a54e97b5ff542e" +dependencies = [ + "futures-timer", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.31" @@ -5947,6 +5977,17 @@ dependencies = [ "rustls 0.21.12", ] +[[package]] +name = "futures-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f2f12607f92c69b12ed746fabf9ca4f5c482cba46679c1a75b874ed7c26adb" +dependencies = [ + "futures-io", + "rustls 0.23.20", + "rustls-pki-types", +] + [[package]] name = "futures-sink" version = "0.3.31" @@ -6038,8 +6079,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -6331,6 +6374,7 @@ dependencies = [ "ipnet", "once_cell", "rand", + "socket2 0.5.8", "thiserror 1.0.69", "tinyvec", "tokio", @@ -7822,25 +7866,57 @@ dependencies = [ "futures-timer", "getrandom", "instant", - "libp2p-allow-block-list", - "libp2p-connection-limits", - "libp2p-core", - "libp2p-dns", - "libp2p-identify", + "libp2p-allow-block-list 0.2.0", + "libp2p-connection-limits 0.2.1", + "libp2p-core 0.40.1", + "libp2p-dns 0.40.1", + "libp2p-identify 0.43.1", "libp2p-identity", "libp2p-kad", - "libp2p-mdns", - "libp2p-metrics", - "libp2p-noise", + "libp2p-mdns 0.44.0", + "libp2p-metrics 0.13.1", + "libp2p-noise 0.43.2", "libp2p-ping", - "libp2p-quic", - "libp2p-request-response", - "libp2p-swarm", - "libp2p-tcp", - "libp2p-upnp", + "libp2p-quic 0.9.3", + "libp2p-request-response 0.25.3", + "libp2p-swarm 0.43.7", + "libp2p-tcp 0.40.1", + "libp2p-upnp 0.1.1", "libp2p-wasm-ext", "libp2p-websocket", - "libp2p-yamux", + "libp2p-yamux 0.44.1", + "multiaddr 0.18.2", + "pin-project", + "rw-stream-sink", + "thiserror 1.0.69", +] + +[[package]] +name = "libp2p" +version = "0.54.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbe80f9c7e00526cd6b838075b9c171919404a4732cb2fa8ece0a093223bfc4" +dependencies = [ + "bytes", + "either", + "futures", + "futures-timer", + "getrandom", + "libp2p-allow-block-list 0.4.0", + "libp2p-connection-limits 0.4.0", + "libp2p-core 0.42.0", + "libp2p-dns 0.42.0", + "libp2p-identify 0.45.0", + "libp2p-identity", + "libp2p-mdns 0.46.0", + "libp2p-metrics 0.15.0", + "libp2p-noise 0.45.0", + "libp2p-quic 0.11.1", + "libp2p-rendezvous", + "libp2p-swarm 0.45.1", + "libp2p-tcp 0.42.0", + "libp2p-upnp 0.3.0", + "libp2p-yamux 0.46.0", "multiaddr 0.18.2", "pin-project", "rw-stream-sink", @@ -7853,9 +7929,21 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55b46558c5c0bf99d3e2a1a38fd54ff5476ca66dd1737b12466a1824dd219311" dependencies = [ - "libp2p-core", + "libp2p-core 0.40.1", "libp2p-identity", - "libp2p-swarm", + "libp2p-swarm 0.43.7", + "void", +] + +[[package]] +name = "libp2p-allow-block-list" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1027ccf8d70320ed77e984f273bc8ce952f623762cb9bf2d126df73caef8041" +dependencies = [ + "libp2p-core 0.42.0", + "libp2p-identity", + "libp2p-swarm 0.45.1", "void", ] @@ -7865,9 +7953,21 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f5107ad45cb20b2f6c3628c7b6014b996fcb13a88053f4569c872c6e30abf58" dependencies = [ - "libp2p-core", + "libp2p-core 0.40.1", + "libp2p-identity", + "libp2p-swarm 0.43.7", + "void", +] + +[[package]] +name = "libp2p-connection-limits" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d003540ee8baef0d254f7b6bfd79bac3ddf774662ca0abf69186d517ef82ad8" +dependencies = [ + "libp2p-core 0.42.0", "libp2p-identity", - "libp2p-swarm", + "libp2p-swarm 0.45.1", "void", ] @@ -7899,6 +7999,34 @@ dependencies = [ "void", ] +[[package]] +name = "libp2p-core" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a61f26c83ed111104cd820fe9bc3aaabbac5f1652a1d213ed6e900b7918a1298" +dependencies = [ + "either", + "fnv", + "futures", + "futures-timer", + "libp2p-identity", + "multiaddr 0.18.2", + "multihash 0.19.3", + "multistream-select", + "once_cell", + "parking_lot 0.12.3", + "pin-project", + "quick-protobuf", + "rand", + "rw-stream-sink", + "smallvec", + "thiserror 1.0.69", + "tracing", + "unsigned-varint 0.8.0", + "void", + "web-time", +] + [[package]] name = "libp2p-dns" version = "0.40.1" @@ -7907,7 +8035,7 @@ checksum = "e6a18db73084b4da2871438f6239fef35190b05023de7656e877c18a00541a3b" dependencies = [ "async-trait", "futures", - "libp2p-core", + "libp2p-core 0.40.1", "libp2p-identity", "log", "parking_lot 0.12.3", @@ -7915,29 +8043,68 @@ dependencies = [ "trust-dns-resolver", ] +[[package]] +name = "libp2p-dns" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97f37f30d5c7275db282ecd86e54f29dd2176bd3ac656f06abf43bedb21eb8bd" +dependencies = [ + "async-trait", + "futures", + "hickory-resolver", + "libp2p-core 0.42.0", + "libp2p-identity", + "parking_lot 0.12.3", + "smallvec", + "tracing", +] + [[package]] name = "libp2p-identify" version = "0.43.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45a96638a0a176bec0a4bcaebc1afa8cf909b114477209d7456ade52c61cd9cd" dependencies = [ - "asynchronous-codec", + "asynchronous-codec 0.6.2", "either", "futures", - "futures-bounded", + "futures-bounded 0.1.0", "futures-timer", - "libp2p-core", + "libp2p-core 0.40.1", "libp2p-identity", - "libp2p-swarm", + "libp2p-swarm 0.43.7", "log", "lru 0.12.5", "quick-protobuf", - "quick-protobuf-codec", + "quick-protobuf-codec 0.2.0", "smallvec", "thiserror 1.0.69", "void", ] +[[package]] +name = "libp2p-identify" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1711b004a273be4f30202778856368683bd9a83c4c7dcc8f848847606831a4e3" +dependencies = [ + "asynchronous-codec 0.7.0", + "either", + "futures", + "futures-bounded 0.2.4", + "futures-timer", + "libp2p-core 0.42.0", + "libp2p-identity", + "libp2p-swarm 0.45.1", + "lru 0.12.5", + "quick-protobuf", + "quick-protobuf-codec 0.3.1", + "smallvec", + "thiserror 1.0.69", + "tracing", + "void", +] + [[package]] name = "libp2p-identity" version = "0.2.10" @@ -7963,19 +8130,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16ea178dabba6dde6ffc260a8e0452ccdc8f79becf544946692fff9d412fc29d" dependencies = [ "arrayvec 0.7.6", - "asynchronous-codec", + "asynchronous-codec 0.6.2", "bytes", "either", "fnv", "futures", "futures-timer", "instant", - "libp2p-core", + "libp2p-core 0.40.1", "libp2p-identity", - "libp2p-swarm", + "libp2p-swarm 0.43.7", "log", "quick-protobuf", - "quick-protobuf-codec", + "quick-protobuf-codec 0.2.0", "rand", "sha2 0.10.8", "smallvec", @@ -7994,9 +8161,9 @@ dependencies = [ "data-encoding", "futures", "if-watch", - "libp2p-core", + "libp2p-core 0.40.1", "libp2p-identity", - "libp2p-swarm", + "libp2p-swarm 0.43.7", "log", "rand", "smallvec", @@ -8006,6 +8173,27 @@ dependencies = [ "void", ] +[[package]] +name = "libp2p-mdns" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b8546b6644032565eb29046b42744aee1e9f261ed99671b2c93fb140dba417" +dependencies = [ + "data-encoding", + "futures", + "hickory-proto", + "if-watch", + "libp2p-core 0.42.0", + "libp2p-identity", + "libp2p-swarm 0.45.1", + "rand", + "smallvec", + "socket2 0.5.8", + "tokio", + "tracing", + "void", +] + [[package]] name = "libp2p-metrics" version = "0.13.1" @@ -8013,14 +8201,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "239ba7d28f8d0b5d77760dc6619c05c7e88e74ec8fbbe97f856f20a56745e620" dependencies = [ "instant", - "libp2p-core", - "libp2p-identify", + "libp2p-core 0.40.1", + "libp2p-identify 0.43.1", "libp2p-identity", "libp2p-kad", "libp2p-ping", - "libp2p-swarm", + "libp2p-swarm 0.43.7", "once_cell", - "prometheus-client", + "prometheus-client 0.21.2", +] + +[[package]] +name = "libp2p-metrics" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ebafa94a717c8442d8db8d3ae5d1c6a15e30f2d347e0cd31d057ca72e42566" +dependencies = [ + "futures", + "libp2p-core 0.42.0", + "libp2p-identify 0.45.0", + "libp2p-identity", + "libp2p-swarm 0.45.1", + "pin-project", + "prometheus-client 0.22.3", + "web-time", ] [[package]] @@ -8032,7 +8236,7 @@ dependencies = [ "bytes", "curve25519-dalek 4.1.3", "futures", - "libp2p-core", + "libp2p-core 0.40.1", "libp2p-identity", "log", "multiaddr 0.18.2", @@ -8048,6 +8252,32 @@ dependencies = [ "zeroize", ] +[[package]] +name = "libp2p-noise" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36b137cb1ae86ee39f8e5d6245a296518912014eaa87427d24e6ff58cfc1b28c" +dependencies = [ + "asynchronous-codec 0.7.0", + "bytes", + "curve25519-dalek 4.1.3", + "futures", + "libp2p-core 0.42.0", + "libp2p-identity", + "multiaddr 0.18.2", + "multihash 0.19.3", + "once_cell", + "quick-protobuf", + "rand", + "sha2 0.10.8", + "snow", + "static_assertions", + "thiserror 1.0.69", + "tracing", + "x25519-dalek", + "zeroize", +] + [[package]] name = "libp2p-ping" version = "0.43.1" @@ -8058,9 +8288,9 @@ dependencies = [ "futures", "futures-timer", "instant", - "libp2p-core", + "libp2p-core 0.40.1", "libp2p-identity", - "libp2p-swarm", + "libp2p-swarm 0.43.7", "log", "rand", "void", @@ -8076,12 +8306,12 @@ dependencies = [ "futures", "futures-timer", "if-watch", - "libp2p-core", + "libp2p-core 0.40.1", "libp2p-identity", - "libp2p-tls", + "libp2p-tls 0.2.1", "log", "parking_lot 0.12.3", - "quinn", + "quinn 0.10.2", "rand", "ring 0.16.20", "rustls 0.21.12", @@ -8090,6 +8320,54 @@ dependencies = [ "tokio", ] +[[package]] +name = "libp2p-quic" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46352ac5cd040c70e88e7ff8257a2ae2f891a4076abad2c439584a31c15fd24e" +dependencies = [ + "bytes", + "futures", + "futures-timer", + "if-watch", + "libp2p-core 0.42.0", + "libp2p-identity", + "libp2p-tls 0.5.0", + "parking_lot 0.12.3", + "quinn 0.11.6", + "rand", + "ring 0.17.8", + "rustls 0.23.20", + "socket2 0.5.8", + "thiserror 1.0.69", + "tokio", + "tracing", +] + +[[package]] +name = "libp2p-rendezvous" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b7e42a1a3315589649590228bbea53fa980e7e8e523dbe6edfbd9c1eb26de3e" +dependencies = [ + "async-trait", + "asynchronous-codec 0.7.0", + "bimap", + "futures", + "futures-timer", + "libp2p-core 0.42.0", + "libp2p-identity", + "libp2p-request-response 0.27.0", + "libp2p-swarm 0.45.1", + "quick-protobuf", + "quick-protobuf-codec 0.3.1", + "rand", + "thiserror 1.0.69", + "tracing", + "void", + "web-time", +] + [[package]] name = "libp2p-request-response" version = "0.25.3" @@ -8099,15 +8377,35 @@ dependencies = [ "async-trait", "futures", "instant", - "libp2p-core", + "libp2p-core 0.40.1", "libp2p-identity", - "libp2p-swarm", + "libp2p-swarm 0.43.7", "log", "rand", "smallvec", "void", ] +[[package]] +name = "libp2p-request-response" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1356c9e376a94a75ae830c42cdaea3d4fe1290ba409a22c809033d1b7dcab0a6" +dependencies = [ + "async-trait", + "futures", + "futures-bounded 0.2.4", + "futures-timer", + "libp2p-core 0.42.0", + "libp2p-identity", + "libp2p-swarm 0.45.1", + "rand", + "smallvec", + "tracing", + "void", + "web-time", +] + [[package]] name = "libp2p-swarm" version = "0.43.7" @@ -8119,9 +8417,9 @@ dependencies = [ "futures", "futures-timer", "instant", - "libp2p-core", + "libp2p-core 0.40.1", "libp2p-identity", - "libp2p-swarm-derive", + "libp2p-swarm-derive 0.33.0", "log", "multistream-select", "once_cell", @@ -8131,6 +8429,30 @@ dependencies = [ "void", ] +[[package]] +name = "libp2p-swarm" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7dd6741793d2c1fb2088f67f82cf07261f25272ebe3c0b0c311e0c6b50e851a" +dependencies = [ + "either", + "fnv", + "futures", + "futures-timer", + "libp2p-core 0.42.0", + "libp2p-identity", + "libp2p-swarm-derive 0.35.0", + "lru 0.12.5", + "multistream-select", + "once_cell", + "rand", + "smallvec", + "tokio", + "tracing", + "void", + "web-time", +] + [[package]] name = "libp2p-swarm-derive" version = "0.33.0" @@ -8144,6 +8466,18 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "libp2p-swarm-derive" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206e0aa0ebe004d778d79fb0966aa0de996c19894e2c0605ba2f8524dd4443d8" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "libp2p-tcp" version = "0.40.1" @@ -8154,13 +8488,30 @@ dependencies = [ "futures-timer", "if-watch", "libc", - "libp2p-core", + "libp2p-core 0.40.1", "libp2p-identity", "log", "socket2 0.5.8", "tokio", ] +[[package]] +name = "libp2p-tcp" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad964f312c59dcfcac840acd8c555de8403e295d39edf96f5240048b5fcaa314" +dependencies = [ + "futures", + "futures-timer", + "if-watch", + "libc", + "libp2p-core 0.42.0", + "libp2p-identity", + "socket2 0.5.8", + "tokio", + "tracing", +] + [[package]] name = "libp2p-tls" version = "0.2.1" @@ -8168,10 +8519,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8218d1d5482b122ccae396bbf38abdcb283ecc96fa54760e1dfd251f0546ac61" dependencies = [ "futures", - "futures-rustls", - "libp2p-core", + "futures-rustls 0.24.0", + "libp2p-core 0.40.1", "libp2p-identity", - "rcgen", + "rcgen 0.10.0", "ring 0.16.20", "rustls 0.21.12", "rustls-webpki 0.101.7", @@ -8180,6 +8531,25 @@ dependencies = [ "yasna", ] +[[package]] +name = "libp2p-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b23dddc2b9c355f73c1e36eb0c3ae86f7dc964a3715f0731cfad352db4d847" +dependencies = [ + "futures", + "futures-rustls 0.26.0", + "libp2p-core 0.42.0", + "libp2p-identity", + "rcgen 0.11.3", + "ring 0.17.8", + "rustls 0.23.20", + "rustls-webpki 0.101.7", + "thiserror 1.0.69", + "x509-parser 0.16.0", + "yasna", +] + [[package]] name = "libp2p-upnp" version = "0.1.1" @@ -8189,13 +8559,29 @@ dependencies = [ "futures", "futures-timer", "igd-next", - "libp2p-core", - "libp2p-swarm", + "libp2p-core 0.40.1", + "libp2p-swarm 0.43.7", "log", "tokio", "void", ] +[[package]] +name = "libp2p-upnp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01bf2d1b772bd3abca049214a3304615e6a36fa6ffc742bdd1ba774486200b8f" +dependencies = [ + "futures", + "futures-timer", + "igd-next", + "libp2p-core 0.42.0", + "libp2p-swarm 0.45.1", + "tokio", + "tracing", + "void", +] + [[package]] name = "libp2p-wasm-ext" version = "0.40.0" @@ -8204,7 +8590,7 @@ checksum = "1e5d8e3a9e07da0ef5b55a9f26c009c8fb3c725d492d8bb4b431715786eea79c" dependencies = [ "futures", "js-sys", - "libp2p-core", + "libp2p-core 0.40.1", "send_wrapper", "wasm-bindgen", "wasm-bindgen-futures", @@ -8218,8 +8604,8 @@ checksum = "004ee9c4a4631435169aee6aad2f62e3984dc031c43b6d29731e8e82a016c538" dependencies = [ "either", "futures", - "futures-rustls", - "libp2p-core", + "futures-rustls 0.24.0", + "libp2p-core 0.40.1", "libp2p-identity", "log", "parking_lot 0.12.3", @@ -8238,10 +8624,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eedcb62824c4300efb9cfd4e2a6edaf3ca097b9e68b36dabe45a44469fd6a85" dependencies = [ "futures", - "libp2p-core", + "libp2p-core 0.40.1", "log", "thiserror 1.0.69", - "yamux", + "yamux 0.12.1", +] + +[[package]] +name = "libp2p-yamux" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "788b61c80789dba9760d8c669a5bedb642c8267555c803fabd8396e4ca5c5882" +dependencies = [ + "either", + "futures", + "libp2p-core 0.42.0", + "thiserror 1.0.69", + "tracing", + "yamux 0.12.1", + "yamux 0.13.4", ] [[package]] @@ -8427,7 +8828,7 @@ dependencies = [ "prost 0.12.6", "prost-build", "rand", - "rcgen", + "rcgen 0.10.0", "ring 0.16.20", "rustls 0.20.9", "serde", @@ -13146,6 +13547,15 @@ dependencies = [ "serde", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -13454,10 +13864,12 @@ dependencies = [ "axum", "cid 0.11.1", "clap", + "ed25519-dalek", "futures", "hex", "hyper 1.5.2", "jsonrpsee 0.24.7", + "libp2p 0.54.1", "mater", "parity-scale-codec", "polka-storage-proofs", @@ -15704,6 +16116,18 @@ dependencies = [ "prometheus-client-derive-encode", ] +[[package]] +name = "prometheus-client" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504ee9ff529add891127c4827eb481bd69dc0ebc72e9a682e187db4caa60c3ca" +dependencies = [ + "dtoa", + "itoa", + "parking_lot 0.12.3", + "prometheus-client-derive-encode", +] + [[package]] name = "prometheus-client-derive-encode" version = "0.4.2" @@ -15855,13 +16279,26 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ededb1cd78531627244d51dd0c7139fbe736c7d57af0092a76f0ffb2f56e98" dependencies = [ - "asynchronous-codec", + "asynchronous-codec 0.6.2", "bytes", "quick-protobuf", "thiserror 1.0.69", "unsigned-varint 0.7.2", ] +[[package]] +name = "quick-protobuf-codec" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15a0580ab32b169745d7a39db2ba969226ca16738931be152a3209b409de2474" +dependencies = [ + "asynchronous-codec 0.7.0", + "bytes", + "quick-protobuf", + "thiserror 1.0.69", + "unsigned-varint 0.8.0", +] + [[package]] name = "quinn" version = "0.10.2" @@ -15871,8 +16308,8 @@ dependencies = [ "bytes", "futures-io", "pin-project-lite", - "quinn-proto", - "quinn-udp", + "quinn-proto 0.10.6", + "quinn-udp 0.4.1", "rustc-hash 1.1.0", "rustls 0.21.12", "thiserror 1.0.69", @@ -15880,6 +16317,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "quinn" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" +dependencies = [ + "bytes", + "futures-io", + "pin-project-lite", + "quinn-proto 0.11.9", + "quinn-udp 0.5.9", + "rustc-hash 2.1.0", + "rustls 0.23.20", + "socket2 0.5.8", + "thiserror 2.0.8", + "tokio", + "tracing", +] + [[package]] name = "quinn-proto" version = "0.10.6" @@ -15897,6 +16353,26 @@ dependencies = [ "tracing", ] +[[package]] +name = "quinn-proto" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" +dependencies = [ + "bytes", + "getrandom", + "rand", + "ring 0.17.8", + "rustc-hash 2.1.0", + "rustls 0.23.20", + "rustls-pki-types", + "slab", + "thiserror 2.0.8", + "tinyvec", + "tracing", + "web-time", +] + [[package]] name = "quinn-udp" version = "0.4.1" @@ -15910,6 +16386,20 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "quinn-udp" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904" +dependencies = [ + "cfg_aliases 0.2.1", + "libc", + "once_cell", + "socket2 0.5.8", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "quote" version = "1.0.37" @@ -16036,6 +16526,18 @@ dependencies = [ "yasna", ] +[[package]] +name = "rcgen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c4f3084aa3bc7dfbba4eff4fab2a54db4324965d8872ab933565e6fbd83bc6" +dependencies = [ + "pem 3.0.4", + "ring 0.16.20", + "time", + "yasna", +] + [[package]] name = "reconnecting-jsonrpsee-ws-client" version = "0.4.3" @@ -16795,6 +17297,9 @@ name = "rustls-pki-types" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" +dependencies = [ + "web-time", +] [[package]] name = "rustls-platform-verifier" @@ -16979,7 +17484,7 @@ dependencies = [ "futures", "futures-timer", "ip_network", - "libp2p", + "libp2p 0.52.4", "linked_hash_set", "log", "multihash 0.19.3", @@ -17639,7 +18144,7 @@ dependencies = [ "array-bytes", "async-channel 1.9.0", "async-trait", - "asynchronous-codec", + "asynchronous-codec 0.6.2", "bytes", "cid 0.9.0", "either", @@ -17647,7 +18152,7 @@ dependencies = [ "futures", "futures-timer", "ip_network", - "libp2p", + "libp2p 0.52.4", "linked_hash_set", "litep2p", "log", @@ -18101,7 +18606,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412 dependencies = [ "chrono", "futures", - "libp2p", + "libp2p 0.52.4", "log", "parking_lot 0.12.3", "pin-project", @@ -23553,7 +24058,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" dependencies = [ - "asynchronous-codec", + "asynchronous-codec 0.6.2", "bytes", "futures-io", "futures-util", @@ -24850,6 +25355,22 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "yamux" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17610762a1207ee816c6fadc29220904753648aba0a9ed61c7b8336e80a559c4" +dependencies = [ + "futures", + "log", + "nohash-hasher", + "parking_lot 0.12.3", + "pin-project", + "rand", + "static_assertions", + "web-time", +] + [[package]] name = "yansi" version = "1.0.1" @@ -25020,7 +25541,7 @@ dependencies = [ "futures", "glob-match", "hex", - "libp2p", + "libp2p 0.52.4", "libsecp256k1", "multiaddr 0.18.2", "rand", diff --git a/Cargo.toml b/Cargo.toml index d69a879d1..025d80b8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,6 +67,7 @@ color-print = "0.3.4" criterion = "0.5.1" digest = "0.10.7" docify = { version = "0.2.8" } +ed25519-dalek = { version = "2.1.1", default-features = false } env_logger = "0.11.2" ff = "0.13.0" futures = "0.3.28" @@ -79,6 +80,7 @@ ipld-core = "0.4.1" ipld-dagpb = "0.2.1" itertools = "0.13.0" jsonrpsee = { version = "0.24.7" } +libp2p = { version = "0.54", default-features = false } log = { version = "0.4.21", default-features = false } multihash-codetable = { version = "0.1.1", default-features = false } num-bigint = { version = "0.4.5", default-features = false } diff --git a/storage-provider/server/Cargo.toml b/storage-provider/server/Cargo.toml index 9409f9afa..ddbc0b036 100644 --- a/storage-provider/server/Cargo.toml +++ b/storage-provider/server/Cargo.toml @@ -24,10 +24,12 @@ axum = { workspace = true, features = ["macros", "multipart"] } cid = { workspace = true, features = ["serde", "std"] } clap = { workspace = true, features = ["derive"] } codec = { workspace = true } +ed25519-dalek = { workspace = true, features = ["pem", "std"] } futures = { workspace = true } hex = { workspace = true, features = ["std"] } hyper = { workspace = true } jsonrpsee = { workspace = true, features = ["http-client", "macros", "server", "ws-client"] } +libp2p = { workspace = true, features = ["identify", "macros", "noise", "rendezvous", "tcp", "tokio", "yamux"] } rand = { workspace = true } rocksdb = { workspace = true } sc-cli = { workspace = true } @@ -38,7 +40,11 @@ tempfile = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true } tokio-util = { workspace = true, features = ["rt"] } +<<<<<<< HEAD toml = { workspace = true } +======= +toml.workspace = true +>>>>>>> 55251242 (feat: Add P2P functionality to storage server) tower = { workspace = true } tower-http = { workspace = true, features = ["trace"] } tracing = { workspace = true } diff --git a/storage-provider/server/p2p-example-config/bootstrap.toml b/storage-provider/server/p2p-example-config/bootstrap.toml new file mode 100644 index 000000000..5d54ad567 --- /dev/null +++ b/storage-provider/server/p2p-example-config/bootstrap.toml @@ -0,0 +1,2 @@ +address = "/ip4/127.0.0.1/tcp/62649" +key_path = "storage-provider/server/p2p-example-config/private.pem" diff --git a/storage-provider/server/p2p-example-config/private.pem b/storage-provider/server/p2p-example-config/private.pem new file mode 100644 index 000000000..6fbe8aad3 --- /dev/null +++ b/storage-provider/server/p2p-example-config/private.pem @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIKo5QJZ40QKfNdrdMyjJeYagahGMgT7ibZsIuUeC3Yrw +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/storage-provider/server/p2p-example-config/public.pem b/storage-provider/server/p2p-example-config/public.pem new file mode 100644 index 000000000..139ecf733 --- /dev/null +++ b/storage-provider/server/p2p-example-config/public.pem @@ -0,0 +1,3 @@ +-----BEGIN PUBLIC KEY----- +MCowBQYDK2VwAyEA8noBMkYU3PmEVHWpu78JrIoeKjZcC8jX1SgRI8ZBPok= +-----END PUBLIC KEY----- diff --git a/storage-provider/server/p2p-example-config/register.toml b/storage-provider/server/p2p-example-config/register.toml new file mode 100644 index 000000000..2b5a96903 --- /dev/null +++ b/storage-provider/server/p2p-example-config/register.toml @@ -0,0 +1,3 @@ +key_path = "storage-provider/server/p2p-example-config/private.pem" +rendezvous_point = "12D3KooWS8tjUoiMgh2hZ12EyDjnJuEoa1zxq7G7EQtqafRUT8fr" +rendezvous_point_address = "/ip4/127.0.0.1/tcp/62649" diff --git a/storage-provider/server/src/main.rs b/storage-provider/server/src/main.rs index 34170ff36..5d841b4fa 100644 --- a/storage-provider/server/src/main.rs +++ b/storage-provider/server/src/main.rs @@ -4,6 +4,7 @@ mod config; mod db; +mod p2p; mod pipeline; mod rpc; mod storage; @@ -12,6 +13,7 @@ use std::{env::temp_dir, net::SocketAddr, path::PathBuf, sync::Arc, time::Durati use clap::Parser; use config::ConfigurationArgs; +use p2p::{start_p2p_node, NodeType, P2PError}; use pipeline::types::PipelineMessage; use polka_storage_proofs::{ porep::{self, PoRepParameters}, @@ -165,6 +167,9 @@ pub enum ServerError { #[error(transparent)] Json(#[from] serde_json::Error), + + #[error(transparent)] + P2P(#[from] P2PError), } /// The server arguments, as passed by the user, unvalidated. @@ -181,6 +186,14 @@ pub struct ServerCli { /// Path to the server configuration file. #[arg(long)] config: Option, + + /// P2P Node type, can be either a bootstrap node or a registration node. + #[arg(long)] + node_type: NodeType, + + /// Path to P2P config file + #[arg(long)] + p2p_config: PathBuf, } /// A valid server configuration. To be created using [`ServerConfiguration::try_from`]. @@ -223,6 +236,12 @@ pub struct Server { /// The number of prove commits to be run in parallel. parallel_prove_commits: usize, + + /// P2P Network node type, can either be a bootstrap or registration node + node_type: NodeType, + + /// P2P Network node configuration file path + p2p_config: PathBuf, } impl TryFrom for Server { @@ -300,12 +319,16 @@ impl TryFrom for Server { porep_parameters, post_parameters, parallel_prove_commits: args.parallel_prove_commits.get(), + node_type: value.node_type, + p2p_config: value.p2p_config, }) } } impl Server { pub async fn run(self) -> Result<(), ServerError> { + let config = self.p2p_config.clone(); + let node_type = self.node_type; let SetupOutput { storage_state, rpc_state, @@ -328,6 +351,11 @@ impl Server { pipeline_rx, cancellation_token.child_token(), )); + let p2p_task = tokio::spawn(start_p2p_node( + node_type, + config, + cancellation_token.child_token(), + )); // Wait for SIGTERM on the main thread and once received "unblock" tokio::signal::ctrl_c() @@ -339,8 +367,8 @@ impl Server { tracing::info!("sent shutdown signal"); // Wait for the tasks to finish - let (upload_result, rpc_task, pipeline_task) = - tokio::join!(storage_task, rpc_task, pipeline_task); + let (upload_result, rpc_task, pipeline_task, p2p_task) = + tokio::join!(storage_task, rpc_task, pipeline_task, p2p_task); // Log errors let upload_result = upload_result @@ -360,10 +388,17 @@ impl Server { let _ = ok.as_ref().inspect_err(|err| tracing::error!(%err)); }); + let p2p_task = p2p_task + .inspect_err(|err| tracing::error!(%err)) + .inspect(|ok| { + let _ = ok.as_ref().inspect_err(|err| tracing::error!(%err)); + }); + // Exit with error upload_result??; rpc_task??; pipeline_task??; + p2p_task??; Ok(()) } diff --git a/storage-provider/server/src/p2p.rs b/storage-provider/server/src/p2p.rs new file mode 100644 index 000000000..d6f245eeb --- /dev/null +++ b/storage-provider/server/src/p2p.rs @@ -0,0 +1,220 @@ +use std::{ + fs::read_to_string, + path::{Path, PathBuf}, +}; + +use bootstrap::{BootstrapBehaviour, BootstrapBehaviourEvent, BootstrapConfig}; +use clap::ValueEnum; +use ed25519_dalek::{pkcs8::DecodePrivateKey, SigningKey}; +use libp2p::{ + futures::StreamExt, identify, identity::Keypair, rendezvous, rendezvous::Namespace, + swarm::SwarmEvent, Multiaddr, PeerId, Swarm, +}; +use register::{RegisterBehaviour, RegisterBehaviourEvent, RegisterConfig}; +use tokio_util::{sync::CancellationToken, task::TaskTracker}; +use tracing::{error, info}; + +mod bootstrap; +mod register; + +#[derive(Debug, Clone, Copy, ValueEnum)] +pub enum NodeType { + Bootstrap, + Register, +} + +#[derive(Debug, thiserror::Error)] +pub enum P2PError { + #[error(transparent)] + SigningKeyError(#[from] ed25519_dalek::pkcs8::Error), + #[error(transparent)] + DecodingError(#[from] libp2p::identity::DecodingError), + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + DialError(#[from] libp2p::swarm::DialError), + #[error("Invalid TCP config for swarm")] + InvalidTcpConfig, + #[error("Invalid behaviour config for swarm")] + InvalidBehaviourConfig, + #[error(transparent)] + TOMLError(#[from] toml::de::Error), + #[error("Failed to register at rendezvous point {0}")] + RegistrationFailed(PeerId), + #[error(transparent)] + P2PTransportError(#[from] libp2p::TransportError), +} + +fn create_keypair + std::fmt::Debug>(path: P) -> Result { + info!("Creating keypair from pem file at {path:?}"); + let key = SigningKey::read_pkcs8_pem_file(path)?; + let keypair = Keypair::ed25519_from_bytes(key.to_bytes())?; + + Ok(keypair) +} + +pub async fn start_p2p_node( + node_type: NodeType, + config: PathBuf, + token: CancellationToken, +) -> Result<(), P2PError> { + info!("Starting P2P node"); + let tracker = TaskTracker::new(); + + tokio::select! { + res = run_p2p_node(node_type, config) => { + if let Err(e) = res { + error!("Failed to start P2P node. Reason: {e}"); + return Err(e); + } + }, + _ = token.cancelled() => { + tracing::info!("P2P node has been stopped by the cancellation token..."); + }, + } + + tracker.close(); + tracker.wait().await; + + Ok(()) +} + +async fn run_p2p_node(node_type: NodeType, config: PathBuf) -> Result<(), P2PError> { + match node_type { + NodeType::Bootstrap => { + let contents = read_to_string(config)?; + let config: BootstrapConfig = toml::from_str(&contents)?; + let (swarm, addr) = config.create_swarm()?; + + bootstrap(swarm, addr).await + } + NodeType::Register => { + let contents = read_to_string(config)?; + let config: RegisterConfig = toml::from_str(&contents)?; + let (swarm, rendezvous_point_address, rendezvous_point) = config.create_swarm()?; + + register( + swarm, + rendezvous_point, + rendezvous_point_address, + None, + Namespace::from_static("rendezvous"), + ) + .await + } + } +} + +/// Register the peer with the rendezvous point. +/// The ttl is how long the peer will remain registered in seconds. +async fn register( + mut swarm: Swarm, + rendezvous_point: PeerId, + rendezvous_point_address: Multiaddr, + ttl: Option, + namespace: Namespace, +) -> Result<(), P2PError> { + info!("Attempting to register with rendezvous point {rendezvous_point} at {rendezvous_point_address}"); + swarm.dial(rendezvous_point_address.clone())?; + + while let Some(event) = swarm.next().await { + match event { + SwarmEvent::NewListenAddr { address, .. } => { + info!("Listening on {}", address); + } + SwarmEvent::ConnectionClosed { + peer_id, + cause: Some(error), + .. + } if peer_id == rendezvous_point => { + info!("Lost connection to rendezvous point {}", error); + } + // once `/identify` did its job, we know our external address and can register + SwarmEvent::Behaviour(RegisterBehaviourEvent::Identify( + identify::Event::Received { info, .. }, + )) => { + // Register our external address. + info!("Registering external address {}", info.observed_addr); + swarm.add_external_address(info.observed_addr); + if let Err(error) = swarm.behaviour_mut().rendezvous.register( + namespace.clone(), + rendezvous_point, + ttl, + ) { + error!("Failed to register: {error}"); + return Err(P2PError::RegistrationFailed(rendezvous_point)); + } + } + SwarmEvent::Behaviour(RegisterBehaviourEvent::Rendezvous( + rendezvous::client::Event::Registered { + namespace, + ttl, + rendezvous_node, + }, + )) => { + info!( + "Registered for namespace '{}' at rendezvous point {} for the next {} seconds", + namespace, rendezvous_node, ttl + ); + return Ok(()); + } + SwarmEvent::Behaviour(RegisterBehaviourEvent::Rendezvous( + rendezvous::client::Event::RegisterFailed { + rendezvous_node, + namespace, + error, + }, + )) => { + error!( + "Failed to register: rendezvous_node={}, namespace={}, error_code={:?}", + rendezvous_node, namespace, error + ); + return Err(P2PError::RegistrationFailed(rendezvous_node)); + } + _other => {} + } + } + + Ok(()) +} + +/// Run the rendezvous point (bootstrap node). +/// Listens on the given [`Multiaddr`] +async fn bootstrap(mut swarm: Swarm, addr: Multiaddr) -> Result<(), P2PError> { + info!("Starting P2P bootstrap node at {addr}"); + swarm.listen_on(addr)?; + while let Some(event) = swarm.next().await { + match event { + SwarmEvent::ConnectionEstablished { peer_id, .. } => { + info!("Connected to {}", peer_id); + } + SwarmEvent::ConnectionClosed { peer_id, .. } => { + info!("Disconnected from {}", peer_id); + } + SwarmEvent::Behaviour(BootstrapBehaviourEvent::Rendezvous( + rendezvous::server::Event::PeerRegistered { peer, registration }, + )) => { + info!( + "Peer {} registered for namespace '{}' for {} seconds", + peer, registration.namespace, registration.ttl + ); + } + SwarmEvent::Behaviour(BootstrapBehaviourEvent::Rendezvous( + rendezvous::server::Event::DiscoverServed { + enquirer, + registrations, + }, + )) => { + if !registrations.is_empty() { + info!( + "Served peer {} with {} new registrations", + enquirer, + registrations.len() + ); + } + } + _other => {} + } + } + Ok(()) +} diff --git a/storage-provider/server/src/p2p/bootstrap.rs b/storage-provider/server/src/p2p/bootstrap.rs new file mode 100644 index 000000000..887dc48cf --- /dev/null +++ b/storage-provider/server/src/p2p/bootstrap.rs @@ -0,0 +1,49 @@ +use std::{path::PathBuf, time::Duration}; + +use libp2p::{ + identify, noise, rendezvous, swarm::NetworkBehaviour, tcp, yamux, Multiaddr, Swarm, + SwarmBuilder, +}; +use serde::Deserialize; + +use super::{create_keypair, P2PError}; + +#[derive(NetworkBehaviour)] +pub struct BootstrapBehaviour { + pub rendezvous: rendezvous::server::Behaviour, + pub identify: identify::Behaviour, +} + +#[derive(Deserialize)] +pub struct BootstrapConfig { + address: Multiaddr, + key_path: PathBuf, +} + +impl BootstrapConfig { + pub fn create_swarm(self) -> Result<(Swarm, Multiaddr), P2PError> { + let keypair = create_keypair(&self.key_path)?; + let swarm = SwarmBuilder::with_existing_identity(keypair) + .with_tokio() + .with_tcp( + tcp::Config::default(), + noise::Config::new, + yamux::Config::default, + ) + .map_err(|_| P2PError::InvalidTcpConfig)? + .with_behaviour(|key| BootstrapBehaviour { + rendezvous: rendezvous::server::Behaviour::new( + rendezvous::server::Config::default(), + ), + identify: identify::Behaviour::new(identify::Config::new( + "identify/1.0.0".to_string(), + key.public(), + )), + }) + .map_err(|_| P2PError::InvalidBehaviourConfig)? + .with_swarm_config(|cfg| cfg.with_idle_connection_timeout(Duration::from_secs(10))) + .build(); + + Ok((swarm, self.address)) + } +} diff --git a/storage-provider/server/src/p2p/register.rs b/storage-provider/server/src/p2p/register.rs new file mode 100644 index 000000000..6933b84ff --- /dev/null +++ b/storage-provider/server/src/p2p/register.rs @@ -0,0 +1,53 @@ +use std::{path::PathBuf, str::FromStr, time::Duration}; + +use libp2p::{ + identify, noise, rendezvous, swarm::NetworkBehaviour, tcp, yamux, Multiaddr, PeerId, Swarm, + SwarmBuilder, +}; +use serde::{de, Deserialize}; + +use super::{create_keypair, P2PError}; +#[derive(NetworkBehaviour)] +pub struct RegisterBehaviour { + pub identify: identify::Behaviour, + pub rendezvous: rendezvous::client::Behaviour, +} + +fn string_to_peer_id<'de, D: de::Deserializer<'de>>(d: D) -> Result { + let s: String = de::Deserialize::deserialize(d)?; + PeerId::from_str(&s).map_err(de::Error::custom) +} + +#[derive(Deserialize)] +pub struct RegisterConfig { + key_path: PathBuf, + rendezvous_point_address: Multiaddr, + #[serde(deserialize_with = "string_to_peer_id")] + rendezvous_point: PeerId, +} + +impl RegisterConfig { + pub fn create_swarm(self) -> Result<(Swarm, Multiaddr, PeerId), P2PError> { + let keypair = create_keypair(&self.key_path)?; + let swarm = SwarmBuilder::with_existing_identity(keypair) + .with_tokio() + .with_tcp( + tcp::Config::default(), + noise::Config::new, + yamux::Config::default, + ) + .map_err(|_| P2PError::InvalidTcpConfig)? + .with_behaviour(|key| RegisterBehaviour { + identify: identify::Behaviour::new(identify::Config::new( + "identify/1.0.0".to_string(), + key.public(), + )), + rendezvous: rendezvous::client::Behaviour::new(key.clone()), + }) + .map_err(|_| P2PError::InvalidBehaviourConfig)? + .with_swarm_config(|cfg| cfg.with_idle_connection_timeout(Duration::from_secs(10))) + .build(); + + Ok((swarm, self.rendezvous_point_address, self.rendezvous_point)) + } +} From 5871fa68a3aac91e4b0d1af328f5794985fd8012 Mon Sep 17 00:00:00 2001 From: aidan46 Date: Tue, 7 Jan 2025 12:48:21 +0800 Subject: [PATCH 02/34] feat: Add peer_id command --- Cargo.lock | 2 ++ storage-provider/client/Cargo.toml | 2 ++ storage-provider/client/src/commands/mod.rs | 40 +++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index a23fb424b..85d828b78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13818,8 +13818,10 @@ dependencies = [ "bls12_381", "cid 0.11.1", "clap", + "ed25519-dalek", "hex", "jsonrpsee 0.24.7", + "libp2p 0.54.1", "mater", "parity-scale-codec", "polka-storage-proofs", diff --git a/storage-provider/client/Cargo.toml b/storage-provider/client/Cargo.toml index bc78d0d09..c3b8a9ec3 100644 --- a/storage-provider/client/Cargo.toml +++ b/storage-provider/client/Cargo.toml @@ -20,8 +20,10 @@ bls12_381 = { workspace = true } cid = { workspace = true, features = ["std"] } clap = { workspace = true, features = ["derive"] } codec = { workspace = true } +ed25519-dalek = { workspace = true, features = ["pem", "pkcs8"] } hex = { workspace = true } jsonrpsee = { workspace = true, features = ["http-client", "macros", "server", "ws-client"] } +libp2p.workspace = true sc-cli = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } diff --git a/storage-provider/client/src/commands/mod.rs b/storage-provider/client/src/commands/mod.rs index a6dd9b0d0..f0b8df20f 100644 --- a/storage-provider/client/src/commands/mod.rs +++ b/storage-provider/client/src/commands/mod.rs @@ -1,8 +1,16 @@ mod proofs; mod wallet; +use std::{ + fs, + io::{self, Write}, + path::PathBuf, +}; + use clap::Parser; +use ed25519_dalek::pkcs8::{DecodePublicKey, PublicKeyBytes}; use jsonrpsee::core::ClientError; +use libp2p::{identity::ed25519::PublicKey as EdPubKey, PeerId}; use polka_storage_provider_common::rpc::StorageProviderRpcClient; use storagext::{ deser::DeserializablePath, @@ -47,6 +55,12 @@ pub enum CliError { #[error("no signer key was provider")] NoSigner, + + #[error(transparent)] + PubKeyError(#[from] ed25519_dalek::pkcs8::spki::Error), + + #[error(transparent)] + DecodingError(#[from] libp2p::identity::DecodingError), } /// A CLI application that facilitates management operations over a running full @@ -98,6 +112,18 @@ pub(crate) enum Cli { #[command(flatten)] signer_key: MultiPairArgs, }, + + /// Generate a Peer ID from a ED25519 public key pem file + GeneratePeerID { + /// Path to the ED25519 public key pem file + #[arg(long)] + pubkey: PathBuf, + + /// Path to the output file to write the peer ID to. + /// If `None` the peer ID will be printed to stdout. + #[arg(long)] + file: Option, + }, } impl Cli { @@ -131,6 +157,7 @@ impl Cli { deal_proposal, signer_key, } => Self::sign_deal(deal_proposal, signer_key), + Self::GeneratePeerID { pubkey, file } => Self::generate_peer_id(pubkey, file), } } @@ -182,4 +209,17 @@ impl Cli { ); Ok(()) } + + fn generate_peer_id(path: PathBuf, file: Option) -> Result<(), CliError> { + let pubkey_bytes = PublicKeyBytes::read_public_key_pem_file(path)?; + let pubkey = EdPubKey::try_from_bytes(&pubkey_bytes.to_bytes())?; + let key = libp2p::identity::PublicKey::from(pubkey); + let peer_id = PeerId::from_public_key(&key).to_string(); + + match file { + None => io::stdout().lock().write_all(&peer_id.as_bytes())?, + Some(path) => fs::write(path, &peer_id.as_bytes())?, + } + Ok(()) + } } From d8f682b7e402b13acf8d5891c9f057803ecf8adf Mon Sep 17 00:00:00 2001 From: aidan46 Date: Tue, 7 Jan 2025 19:59:50 +0800 Subject: [PATCH 03/34] fix: Change peer ID printing --- storage-provider/client/src/commands/mod.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/storage-provider/client/src/commands/mod.rs b/storage-provider/client/src/commands/mod.rs index f0b8df20f..50cd7f7e2 100644 --- a/storage-provider/client/src/commands/mod.rs +++ b/storage-provider/client/src/commands/mod.rs @@ -1,11 +1,7 @@ mod proofs; mod wallet; -use std::{ - fs, - io::{self, Write}, - path::PathBuf, -}; +use std::{fs, path::PathBuf}; use clap::Parser; use ed25519_dalek::pkcs8::{DecodePublicKey, PublicKeyBytes}; @@ -214,11 +210,11 @@ impl Cli { let pubkey_bytes = PublicKeyBytes::read_public_key_pem_file(path)?; let pubkey = EdPubKey::try_from_bytes(&pubkey_bytes.to_bytes())?; let key = libp2p::identity::PublicKey::from(pubkey); - let peer_id = PeerId::from_public_key(&key).to_string(); + let peer_id = PeerId::from_public_key(&key); match file { - None => io::stdout().lock().write_all(&peer_id.as_bytes())?, - Some(path) => fs::write(path, &peer_id.as_bytes())?, + None => println!("{peer_id}"), + Some(path) => fs::write(path, &peer_id.to_string().as_bytes())?, } Ok(()) } From 990bc09ae8a34c3fc8345f181ecdc4e47b6475d7 Mon Sep 17 00:00:00 2001 From: aidan46 Date: Tue, 7 Jan 2025 20:36:37 +0800 Subject: [PATCH 04/34] feat: Add default p2p node type --- storage-provider/server/src/main.rs | 2 +- storage-provider/server/src/p2p.rs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/storage-provider/server/src/main.rs b/storage-provider/server/src/main.rs index 5d841b4fa..9bc347b39 100644 --- a/storage-provider/server/src/main.rs +++ b/storage-provider/server/src/main.rs @@ -188,7 +188,7 @@ pub struct ServerCli { config: Option, /// P2P Node type, can be either a bootstrap node or a registration node. - #[arg(long)] + #[arg(long, default_value = "bootstrap")] node_type: NodeType, /// Path to P2P config file diff --git a/storage-provider/server/src/p2p.rs b/storage-provider/server/src/p2p.rs index d6f245eeb..a04a6e21f 100644 --- a/storage-provider/server/src/p2p.rs +++ b/storage-provider/server/src/p2p.rs @@ -1,4 +1,5 @@ use std::{ + fmt::Display, fs::read_to_string, path::{Path, PathBuf}, }; @@ -23,6 +24,12 @@ pub enum NodeType { Register, } +impl Display for NodeType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + #[derive(Debug, thiserror::Error)] pub enum P2PError { #[error(transparent)] From 187abbb064d1abf6698c69a7b310e3cf4fafc5f2 Mon Sep 17 00:00:00 2001 From: aidan46 Date: Tue, 7 Jan 2025 20:38:55 +0800 Subject: [PATCH 05/34] fix: Move example keys and config into central example folder --- .../p2p-example-config => examples/p2p-config}/bootstrap.toml | 0 .../p2p-example-config => examples/p2p-config}/register.toml | 0 .../server/p2p-example-config => examples/p2p-keys}/private.pem | 0 .../server/p2p-example-config => examples/p2p-keys}/public.pem | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename {storage-provider/server/p2p-example-config => examples/p2p-config}/bootstrap.toml (100%) rename {storage-provider/server/p2p-example-config => examples/p2p-config}/register.toml (100%) rename {storage-provider/server/p2p-example-config => examples/p2p-keys}/private.pem (100%) rename {storage-provider/server/p2p-example-config => examples/p2p-keys}/public.pem (100%) diff --git a/storage-provider/server/p2p-example-config/bootstrap.toml b/examples/p2p-config/bootstrap.toml similarity index 100% rename from storage-provider/server/p2p-example-config/bootstrap.toml rename to examples/p2p-config/bootstrap.toml diff --git a/storage-provider/server/p2p-example-config/register.toml b/examples/p2p-config/register.toml similarity index 100% rename from storage-provider/server/p2p-example-config/register.toml rename to examples/p2p-config/register.toml diff --git a/storage-provider/server/p2p-example-config/private.pem b/examples/p2p-keys/private.pem similarity index 100% rename from storage-provider/server/p2p-example-config/private.pem rename to examples/p2p-keys/private.pem diff --git a/storage-provider/server/p2p-example-config/public.pem b/examples/p2p-keys/public.pem similarity index 100% rename from storage-provider/server/p2p-example-config/public.pem rename to examples/p2p-keys/public.pem From 9eda31f6f4d2be0445b1159dfa3685d230e0a46d Mon Sep 17 00:00:00 2001 From: aidan46 Date: Thu, 9 Jan 2025 19:19:14 +0800 Subject: [PATCH 06/34] fix: Parse p2p config before running --- examples/p2p-config/bootstrap.toml | 2 +- examples/p2p-config/register.toml | 2 +- storage-provider/server/Cargo.toml | 4 -- storage-provider/server/src/main.rs | 33 ++++++--- storage-provider/server/src/p2p.rs | 76 ++++++++++++-------- storage-provider/server/src/p2p/bootstrap.rs | 14 ++-- storage-provider/server/src/p2p/register.rs | 15 ++-- 7 files changed, 86 insertions(+), 60 deletions(-) diff --git a/examples/p2p-config/bootstrap.toml b/examples/p2p-config/bootstrap.toml index 5d54ad567..462f5166b 100644 --- a/examples/p2p-config/bootstrap.toml +++ b/examples/p2p-config/bootstrap.toml @@ -1,2 +1,2 @@ address = "/ip4/127.0.0.1/tcp/62649" -key_path = "storage-provider/server/p2p-example-config/private.pem" +keypair = "examples/p2p-keys/private.pem" diff --git a/examples/p2p-config/register.toml b/examples/p2p-config/register.toml index 2b5a96903..018cf3211 100644 --- a/examples/p2p-config/register.toml +++ b/examples/p2p-config/register.toml @@ -1,3 +1,3 @@ -key_path = "storage-provider/server/p2p-example-config/private.pem" +keypair = "storage-provider/server/p2p-example-config/private.pem" rendezvous_point = "12D3KooWS8tjUoiMgh2hZ12EyDjnJuEoa1zxq7G7EQtqafRUT8fr" rendezvous_point_address = "/ip4/127.0.0.1/tcp/62649" diff --git a/storage-provider/server/Cargo.toml b/storage-provider/server/Cargo.toml index ddbc0b036..820d185dd 100644 --- a/storage-provider/server/Cargo.toml +++ b/storage-provider/server/Cargo.toml @@ -40,11 +40,7 @@ tempfile = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true } tokio-util = { workspace = true, features = ["rt"] } -<<<<<<< HEAD toml = { workspace = true } -======= -toml.workspace = true ->>>>>>> 55251242 (feat: Add P2P functionality to storage server) tower = { workspace = true } tower-http = { workspace = true, features = ["trace"] } tracing = { workspace = true } diff --git a/storage-provider/server/src/main.rs b/storage-provider/server/src/main.rs index 9bc347b39..aa3b68a72 100644 --- a/storage-provider/server/src/main.rs +++ b/storage-provider/server/src/main.rs @@ -9,11 +9,15 @@ mod pipeline; mod rpc; mod storage; -use std::{env::temp_dir, net::SocketAddr, path::PathBuf, sync::Arc, time::Duration}; +use std::{ + env::temp_dir, fs::read_to_string, net::SocketAddr, path::PathBuf, sync::Arc, + time::Duration, +}; use clap::Parser; -use config::ConfigurationArgs; -use p2p::{start_p2p_node, NodeType, P2PError}; +use p2p::{ + run_bootstrap_node, run_register_node, BootstrapConfig, NodeType, P2PError, RegisterConfig, +}; use pipeline::types::PipelineMessage; use polka_storage_proofs::{ porep::{self, PoRepParameters}, @@ -45,6 +49,7 @@ use crate::{ pipeline::{start_pipeline, PipelineState}, rpc::{start_rpc_server, RpcServerState}, storage::{start_upload_server, StorageServerState}, + config::ConfigurationArgs, }; /// Default parachain node adress. @@ -84,6 +89,7 @@ struct SetupOutput { pipeline_state: PipelineState, pipeline_rx: UnboundedReceiver, } + fn main() -> Result<(), ServerError> { // Logger initialization. let file_appender = tracing_appender::rolling::daily("logs", "sp_server"); @@ -327,7 +333,7 @@ impl TryFrom for Server { impl Server { pub async fn run(self) -> Result<(), ServerError> { - let config = self.p2p_config.clone(); + let p2p_config = self.p2p_config.clone(); let node_type = self.node_type; let SetupOutput { storage_state, @@ -338,6 +344,20 @@ impl Server { let cancellation_token = CancellationToken::new(); + let p2p_task = match node_type { + NodeType::Bootstrap => { + let contents = read_to_string(p2p_config)?; + let config: BootstrapConfig = + toml::from_str(&contents).map_err(|e| P2PError::TOMLError(e))?; + tokio::spawn(run_bootstrap_node(config, cancellation_token.child_token())) + } + NodeType::Register => { + let contents = read_to_string(p2p_config)?; + let config: RegisterConfig = + toml::from_str(&contents).map_err(|e| P2PError::TOMLError(e))?; + tokio::spawn(run_register_node(config, cancellation_token.child_token())) + } + }; let rpc_task = tokio::spawn(start_rpc_server( rpc_state, cancellation_token.child_token(), @@ -351,11 +371,6 @@ impl Server { pipeline_rx, cancellation_token.child_token(), )); - let p2p_task = tokio::spawn(start_p2p_node( - node_type, - config, - cancellation_token.child_token(), - )); // Wait for SIGTERM on the main thread and once received "unblock" tokio::signal::ctrl_c() diff --git a/storage-provider/server/src/p2p.rs b/storage-provider/server/src/p2p.rs index a04a6e21f..b655ae1af 100644 --- a/storage-provider/server/src/p2p.rs +++ b/storage-provider/server/src/p2p.rs @@ -1,23 +1,26 @@ use std::{ fmt::Display, - fs::read_to_string, path::{Path, PathBuf}, }; -use bootstrap::{BootstrapBehaviour, BootstrapBehaviourEvent, BootstrapConfig}; +use bootstrap::{BootstrapBehaviour, BootstrapBehaviourEvent}; use clap::ValueEnum; use ed25519_dalek::{pkcs8::DecodePrivateKey, SigningKey}; use libp2p::{ futures::StreamExt, identify, identity::Keypair, rendezvous, rendezvous::Namespace, swarm::SwarmEvent, Multiaddr, PeerId, Swarm, }; -use register::{RegisterBehaviour, RegisterBehaviourEvent, RegisterConfig}; +use register::{RegisterBehaviour, RegisterBehaviourEvent}; +use serde::de; use tokio_util::{sync::CancellationToken, task::TaskTracker}; use tracing::{error, info}; mod bootstrap; mod register; +pub(crate) use bootstrap::BootstrapConfig; +pub(crate) use register::RegisterConfig; + #[derive(Debug, Clone, Copy, ValueEnum)] pub enum NodeType { Bootstrap, @@ -52,7 +55,7 @@ pub enum P2PError { P2PTransportError(#[from] libp2p::TransportError), } -fn create_keypair + std::fmt::Debug>(path: P) -> Result { +pub fn create_keypair + std::fmt::Debug>(path: P) -> Result { info!("Creating keypair from pem file at {path:?}"); let key = SigningKey::read_pkcs8_pem_file(path)?; let keypair = Keypair::ed25519_from_bytes(key.to_bytes())?; @@ -60,16 +63,21 @@ fn create_keypair + std::fmt::Debug>(path: P) -> Result>(d: D) -> Result { + let path: PathBuf = de::Deserialize::deserialize(d)?; + create_keypair(path).map_err(de::Error::custom) +} + +pub async fn run_bootstrap_node( + config: BootstrapConfig, token: CancellationToken, ) -> Result<(), P2PError> { - info!("Starting P2P node"); + info!("Starting P2P bootstrap node"); let tracker = TaskTracker::new(); + let (swarm, addr) = config.create_swarm()?; tokio::select! { - res = run_p2p_node(node_type, config) => { + res = bootstrap(swarm, addr) => { if let Err(e) = res { error!("Failed to start P2P node. Reason: {e}"); return Err(e); @@ -86,30 +94,36 @@ pub async fn start_p2p_node( Ok(()) } -async fn run_p2p_node(node_type: NodeType, config: PathBuf) -> Result<(), P2PError> { - match node_type { - NodeType::Bootstrap => { - let contents = read_to_string(config)?; - let config: BootstrapConfig = toml::from_str(&contents)?; - let (swarm, addr) = config.create_swarm()?; +pub async fn run_register_node( + config: RegisterConfig, + token: CancellationToken, +) -> Result<(), P2PError> { + info!("Starting P2P register node"); + let tracker = TaskTracker::new(); + let (swarm, rendezvous_point_address, rendezvous_point) = config.create_swarm()?; - bootstrap(swarm, addr).await - } - NodeType::Register => { - let contents = read_to_string(config)?; - let config: RegisterConfig = toml::from_str(&contents)?; - let (swarm, rendezvous_point_address, rendezvous_point) = config.create_swarm()?; - - register( - swarm, - rendezvous_point, - rendezvous_point_address, - None, - Namespace::from_static("rendezvous"), - ) - .await - } + tokio::select! { + res = register( + swarm, + rendezvous_point, + rendezvous_point_address, + None, + Namespace::from_static("polka-storage"), + ) => { + if let Err(e) = res { + error!("Failed to start P2P node. Reason: {e}"); + return Err(e); + } + }, + _ = token.cancelled() => { + tracing::info!("P2P node has been stopped by the cancellation token..."); + }, } + + tracker.close(); + tracker.wait().await; + + Ok(()) } /// Register the peer with the rendezvous point. diff --git a/storage-provider/server/src/p2p/bootstrap.rs b/storage-provider/server/src/p2p/bootstrap.rs index 887dc48cf..3e09534e3 100644 --- a/storage-provider/server/src/p2p/bootstrap.rs +++ b/storage-provider/server/src/p2p/bootstrap.rs @@ -1,12 +1,12 @@ -use std::{path::PathBuf, time::Duration}; +use std::time::Duration; use libp2p::{ - identify, noise, rendezvous, swarm::NetworkBehaviour, tcp, yamux, Multiaddr, Swarm, - SwarmBuilder, + identify, identity::Keypair, noise, rendezvous, swarm::NetworkBehaviour, tcp, yamux, Multiaddr, + Swarm, SwarmBuilder, }; use serde::Deserialize; -use super::{create_keypair, P2PError}; +use super::{path_to_keypair, P2PError}; #[derive(NetworkBehaviour)] pub struct BootstrapBehaviour { @@ -17,13 +17,13 @@ pub struct BootstrapBehaviour { #[derive(Deserialize)] pub struct BootstrapConfig { address: Multiaddr, - key_path: PathBuf, + #[serde(deserialize_with = "path_to_keypair")] + keypair: Keypair, } impl BootstrapConfig { pub fn create_swarm(self) -> Result<(Swarm, Multiaddr), P2PError> { - let keypair = create_keypair(&self.key_path)?; - let swarm = SwarmBuilder::with_existing_identity(keypair) + let swarm = SwarmBuilder::with_existing_identity(self.keypair) .with_tokio() .with_tcp( tcp::Config::default(), diff --git a/storage-provider/server/src/p2p/register.rs b/storage-provider/server/src/p2p/register.rs index 6933b84ff..1ad0553bb 100644 --- a/storage-provider/server/src/p2p/register.rs +++ b/storage-provider/server/src/p2p/register.rs @@ -1,12 +1,13 @@ -use std::{path::PathBuf, str::FromStr, time::Duration}; +use std::{str::FromStr, time::Duration}; use libp2p::{ - identify, noise, rendezvous, swarm::NetworkBehaviour, tcp, yamux, Multiaddr, PeerId, Swarm, - SwarmBuilder, + identify, identity::Keypair, noise, rendezvous, swarm::NetworkBehaviour, tcp, yamux, Multiaddr, + PeerId, Swarm, SwarmBuilder, }; use serde::{de, Deserialize}; -use super::{create_keypair, P2PError}; +use super::{path_to_keypair, P2PError}; + #[derive(NetworkBehaviour)] pub struct RegisterBehaviour { pub identify: identify::Behaviour, @@ -20,7 +21,8 @@ fn string_to_peer_id<'de, D: de::Deserializer<'de>>(d: D) -> Result Result<(Swarm, Multiaddr, PeerId), P2PError> { - let keypair = create_keypair(&self.key_path)?; - let swarm = SwarmBuilder::with_existing_identity(keypair) + let swarm = SwarmBuilder::with_existing_identity(self.keypair) .with_tokio() .with_tcp( tcp::Config::default(), From 21207ad61652987e9ff1f48aa4d0c86596fd32c7 Mon Sep 17 00:00:00 2001 From: aidan46 Date: Fri, 10 Jan 2025 17:18:50 +0800 Subject: [PATCH 07/34] fix: PeerId in storagext and pallets --- Cargo.lock | 2 ++ pallets/market/src/mock.rs | 2 +- pallets/storage-provider/src/tests/mod.rs | 2 +- runtime/src/configs/mod.rs | 2 +- storagext/cli/Cargo.toml | 1 + storagext/cli/src/cmd/storage_provider.rs | 7 ++++--- storagext/lib/Cargo.toml | 1 + storagext/lib/src/clients/storage_provider.rs | 7 ++++--- 8 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 85d828b78..b87ba986c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22421,6 +22421,7 @@ dependencies = [ "futures", "hex", "itertools 0.13.0", + "libp2p 0.54.1", "parity-scale-codec", "primitives", "serde", @@ -22444,6 +22445,7 @@ dependencies = [ "clap", "frame-support 28.0.0", "hex", + "libp2p 0.54.1", "parity-scale-codec", "primitives", "serde", diff --git a/pallets/market/src/mock.rs b/pallets/market/src/mock.rs index 00dc12f14..b16eb9a88 100644 --- a/pallets/market/src/mock.rs +++ b/pallets/market/src/mock.rs @@ -119,7 +119,7 @@ impl pallet_storage_provider::Config for Test { type RuntimeEvent = RuntimeEvent; type Randomness = DummyRandomnessGenerator; type AuthorVrfHistory = DummyRandomnessGenerator; - type PeerId = BoundedVec>; // Max length of SHA256 hash + type PeerId = BoundedVec>; // https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#peer-ids type Currency = Balances; type Market = Market; type ProofVerification = Proofs; diff --git a/pallets/storage-provider/src/tests/mod.rs b/pallets/storage-provider/src/tests/mod.rs index e5e36653a..088fafd99 100644 --- a/pallets/storage-provider/src/tests/mod.rs +++ b/pallets/storage-provider/src/tests/mod.rs @@ -198,7 +198,7 @@ impl pallet_storage_provider::Config for Test { type RuntimeEvent = RuntimeEvent; type Randomness = DummyRandomnessGenerator; type AuthorVrfHistory = DummyRandomnessGenerator; - type PeerId = BoundedVec>; // Max length of SHA256 hash + type PeerId = BoundedVec>; // https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#peer-ids type Currency = Balances; type Market = Market; type ProofVerification = DummyProofsVerification; diff --git a/runtime/src/configs/mod.rs b/runtime/src/configs/mod.rs index 712424055..ffa88195f 100644 --- a/runtime/src/configs/mod.rs +++ b/runtime/src/configs/mod.rs @@ -368,7 +368,7 @@ impl pallet_storage_provider::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Randomness = crate::Randomness; type AuthorVrfHistory = crate::Randomness; - type PeerId = BoundedVec>; // Max length of SHA256 hash + type PeerId = BoundedVec>; // https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#peer-ids type Currency = Balances; type Market = crate::Market; type ProofVerification = crate::Proofs; diff --git a/storagext/cli/Cargo.toml b/storagext/cli/Cargo.toml index 1fffaab93..e327c9d84 100644 --- a/storagext/cli/Cargo.toml +++ b/storagext/cli/Cargo.toml @@ -27,6 +27,7 @@ tokio = { workspace = true, features = ["rt-multi-thread"] } tracing = { workspace = true } tracing-subscriber = { workspace = true, features = ["env-filter"] } url = { workspace = true } +libp2p.workspace = true [lints] workspace = true diff --git a/storagext/cli/src/cmd/storage_provider.rs b/storagext/cli/src/cmd/storage_provider.rs index 25460bc27..8b81d79c5 100644 --- a/storagext/cli/src/cmd/storage_provider.rs +++ b/storagext/cli/src/cmd/storage_provider.rs @@ -1,6 +1,7 @@ use std::time::Duration; use clap::Subcommand; +use libp2p::PeerId; use primitives::{proofs::RegisteredPoStProof, sector::SectorNumber}; use storagext::{ deser::DeserializablePath, @@ -37,7 +38,7 @@ pub enum StorageProviderCommand { #[command(name = "register")] RegisterStorageProvider { /// PeerId in Storage Provider P2P network, can be any String. - peer_id: String, + peer_id: PeerId, /// Proof of Space Time type. /// Can only be "2KiB" meaning `RegisteredPoStProof::StackedDRGWindow2KiBV1P1`. #[arg(long, value_parser = parse_post_proof, default_value = "2KiB")] @@ -252,7 +253,7 @@ impl StorageProviderCommand { async fn register_storage_provider( client: Client, account_keypair: MultiPairSigner, - peer_id: String, + peer_id: PeerId, post_proof: RegisteredPoStProof, wait_for_finalization: bool, ) -> Result>, subxt::Error> @@ -262,7 +263,7 @@ impl StorageProviderCommand { let submission_result = client .register_storage_provider( &account_keypair, - peer_id.clone(), + peer_id, post_proof, wait_for_finalization, ) diff --git a/storagext/lib/Cargo.toml b/storagext/lib/Cargo.toml index 3f776749c..0357c7442 100644 --- a/storagext/lib/Cargo.toml +++ b/storagext/lib/Cargo.toml @@ -34,6 +34,7 @@ url = { workspace = true } # Optional dependencies clap = { workspace = true, optional = true, features = ["derive"] } +libp2p.workspace = true [dev-dependencies] # "Duplicated" so we can always test clap-dependent things diff --git a/storagext/lib/src/clients/storage_provider.rs b/storagext/lib/src/clients/storage_provider.rs index e2b79359c..ba32da503 100644 --- a/storagext/lib/src/clients/storage_provider.rs +++ b/storagext/lib/src/clients/storage_provider.rs @@ -1,5 +1,6 @@ use std::future::Future; +use libp2p::PeerId as P2PPeerId; use primitives::proofs::RegisteredPoStProof; use runtime::runtime_types::bounded_collections::bounded_vec::BoundedVec; use subxt::{ @@ -28,7 +29,7 @@ pub trait StorageProviderClientExt { fn register_storage_provider( &self, account_keypair: &Keypair, - peer_id: String, + peer_id: P2PPeerId, post_proof: RegisteredPoStProof, wait_for_finalization: bool, ) -> impl Future>, subxt::Error>> @@ -176,7 +177,7 @@ impl StorageProviderClientExt for crate::runtime::client::Client { async fn register_storage_provider( &self, account_keypair: &Keypair, - peer_id: String, + peer_id: P2PPeerId, post_proof: RegisteredPoStProof, wait_for_finalization: bool, ) -> Result>, subxt::Error> @@ -185,7 +186,7 @@ impl StorageProviderClientExt for crate::runtime::client::Client { { let payload = runtime::tx() .storage_provider() - .register_storage_provider(peer_id.into_bounded_byte_vec(), post_proof); + .register_storage_provider(peer_id.to_bytes().into_bounded_byte_vec(), post_proof); self.traced_submission(&payload, account_keypair, wait_for_finalization) .await From f1bd75a5b5949555e3df911a3247b0d00fca8962 Mon Sep 17 00:00:00 2001 From: aidan46 Date: Fri, 10 Jan 2025 18:12:02 +0800 Subject: [PATCH 08/34] feat: Add @ dialect to p2p key parsing --- examples/p2p-config/bootstrap.toml | 2 +- examples/p2p-config/register.toml | 2 +- storage-provider/server/src/p2p.rs | 33 +++++++++----------- storage-provider/server/src/p2p/bootstrap.rs | 4 +-- storage-provider/server/src/p2p/register.rs | 4 +-- storagext/cli/src/cmd/storage_provider.rs | 7 +---- 6 files changed, 21 insertions(+), 31 deletions(-) diff --git a/examples/p2p-config/bootstrap.toml b/examples/p2p-config/bootstrap.toml index 462f5166b..7ba97d23d 100644 --- a/examples/p2p-config/bootstrap.toml +++ b/examples/p2p-config/bootstrap.toml @@ -1,2 +1,2 @@ address = "/ip4/127.0.0.1/tcp/62649" -keypair = "examples/p2p-keys/private.pem" +keypair = "@examples/p2p-keys/private.pem" diff --git a/examples/p2p-config/register.toml b/examples/p2p-config/register.toml index 018cf3211..70ba447b3 100644 --- a/examples/p2p-config/register.toml +++ b/examples/p2p-config/register.toml @@ -1,3 +1,3 @@ -keypair = "storage-provider/server/p2p-example-config/private.pem" +keypair = "@storage-provider/server/p2p-example-config/private.pem" rendezvous_point = "12D3KooWS8tjUoiMgh2hZ12EyDjnJuEoa1zxq7G7EQtqafRUT8fr" rendezvous_point_address = "/ip4/127.0.0.1/tcp/62649" diff --git a/storage-provider/server/src/p2p.rs b/storage-provider/server/src/p2p.rs index b655ae1af..76ca495e6 100644 --- a/storage-provider/server/src/p2p.rs +++ b/storage-provider/server/src/p2p.rs @@ -1,7 +1,4 @@ -use std::{ - fmt::Display, - path::{Path, PathBuf}, -}; +use std::{fmt::Display, path::PathBuf, str::FromStr}; use bootstrap::{BootstrapBehaviour, BootstrapBehaviourEvent}; use clap::ValueEnum; @@ -35,10 +32,6 @@ impl Display for NodeType { #[derive(Debug, thiserror::Error)] pub enum P2PError { - #[error(transparent)] - SigningKeyError(#[from] ed25519_dalek::pkcs8::Error), - #[error(transparent)] - DecodingError(#[from] libp2p::identity::DecodingError), #[error(transparent)] Io(#[from] std::io::Error), #[error(transparent)] @@ -55,17 +48,19 @@ pub enum P2PError { P2PTransportError(#[from] libp2p::TransportError), } -pub fn create_keypair + std::fmt::Debug>(path: P) -> Result { - info!("Creating keypair from pem file at {path:?}"); - let key = SigningKey::read_pkcs8_pem_file(path)?; - let keypair = Keypair::ed25519_from_bytes(key.to_bytes())?; - - Ok(keypair) -} - -fn path_to_keypair<'de, D: de::Deserializer<'de>>(d: D) -> Result { - let path: PathBuf = de::Deserialize::deserialize(d)?; - create_keypair(path).map_err(de::Error::custom) +fn deser_keypair<'de, D: de::Deserializer<'de>>(d: D) -> Result { + let src: String = de::Deserialize::deserialize(d)?; + let key = if let Some(stripped) = src.strip_prefix('@') { + let path = PathBuf::from_str(stripped) + .map_err(de::Error::custom)? + .canonicalize() + .map_err(de::Error::custom)?; + SigningKey::read_pkcs8_pem_file(path).map_err(de::Error::custom)? + } else { + let hex_key = hex::decode(src).map_err(de::Error::custom)?; + SigningKey::try_from(hex_key.as_slice()).map_err(de::Error::custom)? + }; + Keypair::ed25519_from_bytes(key.to_bytes()).map_err(de::Error::custom) } pub async fn run_bootstrap_node( diff --git a/storage-provider/server/src/p2p/bootstrap.rs b/storage-provider/server/src/p2p/bootstrap.rs index 3e09534e3..ea813bcd5 100644 --- a/storage-provider/server/src/p2p/bootstrap.rs +++ b/storage-provider/server/src/p2p/bootstrap.rs @@ -6,7 +6,7 @@ use libp2p::{ }; use serde::Deserialize; -use super::{path_to_keypair, P2PError}; +use super::{deser_keypair, P2PError}; #[derive(NetworkBehaviour)] pub struct BootstrapBehaviour { @@ -17,7 +17,7 @@ pub struct BootstrapBehaviour { #[derive(Deserialize)] pub struct BootstrapConfig { address: Multiaddr, - #[serde(deserialize_with = "path_to_keypair")] + #[serde(deserialize_with = "deser_keypair")] keypair: Keypair, } diff --git a/storage-provider/server/src/p2p/register.rs b/storage-provider/server/src/p2p/register.rs index 1ad0553bb..2c5a78d0f 100644 --- a/storage-provider/server/src/p2p/register.rs +++ b/storage-provider/server/src/p2p/register.rs @@ -6,7 +6,7 @@ use libp2p::{ }; use serde::{de, Deserialize}; -use super::{path_to_keypair, P2PError}; +use super::{deser_keypair, P2PError}; #[derive(NetworkBehaviour)] pub struct RegisterBehaviour { @@ -21,7 +21,7 @@ fn string_to_peer_id<'de, D: de::Deserializer<'de>>(d: D) -> Result Date: Fri, 10 Jan 2025 18:16:04 +0800 Subject: [PATCH 09/34] fix: TOML formatting --- storagext/cli/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storagext/cli/Cargo.toml b/storagext/cli/Cargo.toml index e327c9d84..a8efa95ba 100644 --- a/storagext/cli/Cargo.toml +++ b/storagext/cli/Cargo.toml @@ -17,6 +17,7 @@ clap = { workspace = true, features = ["derive", "env"] } codec.workspace = true frame-support = { workspace = true, features = ["std"] } hex = { workspace = true, features = ["serde", "std"] } +libp2p.workspace = true primitives = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } @@ -27,7 +28,6 @@ tokio = { workspace = true, features = ["rt-multi-thread"] } tracing = { workspace = true } tracing-subscriber = { workspace = true, features = ["env-filter"] } url = { workspace = true } -libp2p.workspace = true [lints] workspace = true From 910802f5b7ee91612dd70dce9a7bf650e6c156d8 Mon Sep 17 00:00:00 2001 From: aidan46 Date: Mon, 13 Jan 2025 13:14:54 +0800 Subject: [PATCH 10/34] fix: Keypair path in register node config --- examples/p2p-config/register.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/p2p-config/register.toml b/examples/p2p-config/register.toml index 70ba447b3..500c3fe5c 100644 --- a/examples/p2p-config/register.toml +++ b/examples/p2p-config/register.toml @@ -1,3 +1,3 @@ -keypair = "@storage-provider/server/p2p-example-config/private.pem" +keypair = "@examples/p2p-keys/private.pem" rendezvous_point = "12D3KooWS8tjUoiMgh2hZ12EyDjnJuEoa1zxq7G7EQtqafRUT8fr" rendezvous_point_address = "/ip4/127.0.0.1/tcp/62649" From e1efcef93958fcf5852f74c9636a55ac84715964 Mon Sep 17 00:00:00 2001 From: aidan46 Date: Mon, 13 Jan 2025 13:33:03 +0800 Subject: [PATCH 11/34] fix: Add PEER_ID_MAX_BYTES const --- pallets/market/src/mock.rs | 4 ++-- pallets/storage-provider/src/tests/mod.rs | 4 ++-- primitives/src/lib.rs | 6 ++++++ runtime/src/configs/mod.rs | 3 ++- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/pallets/market/src/mock.rs b/pallets/market/src/mock.rs index b16eb9a88..61fcd1803 100644 --- a/pallets/market/src/mock.rs +++ b/pallets/market/src/mock.rs @@ -6,7 +6,7 @@ use frame_support::{ PalletId, }; use frame_system::pallet_prelude::BlockNumberFor; -use primitives::proofs::RegisteredPoStProof; +use primitives::{proofs::RegisteredPoStProof, PEER_ID_MAX_BYTES}; use sp_core::Pair; use sp_runtime::{ traits::{ConstU32, ConstU64, IdentifyAccount, IdentityLookup, Verify, Zero}, @@ -119,7 +119,7 @@ impl pallet_storage_provider::Config for Test { type RuntimeEvent = RuntimeEvent; type Randomness = DummyRandomnessGenerator; type AuthorVrfHistory = DummyRandomnessGenerator; - type PeerId = BoundedVec>; // https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#peer-ids + type PeerId = BoundedVec>; // https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#peer-ids type Currency = Balances; type Market = Market; type ProofVerification = Proofs; diff --git a/pallets/storage-provider/src/tests/mod.rs b/pallets/storage-provider/src/tests/mod.rs index 088fafd99..f75baea8c 100644 --- a/pallets/storage-provider/src/tests/mod.rs +++ b/pallets/storage-provider/src/tests/mod.rs @@ -21,7 +21,7 @@ use primitives::{ sector::SectorNumber, DealId, PartitionNumber, CID_SIZE_IN_BYTES, MAX_DEALS_PER_SECTOR, MAX_PARTITIONS_PER_DEADLINE, MAX_POST_PROOF_BYTES, MAX_PROOFS_PER_BLOCK, MAX_REPLICAS_PER_BLOCK, MAX_SEAL_PROOF_BYTES, - MAX_TERMINATIONS_PER_CALL, + MAX_TERMINATIONS_PER_CALL, PEER_ID_MAX_BYTES, }; use sp_arithmetic::traits::Zero; use sp_core::{bounded_vec, Pair}; @@ -198,7 +198,7 @@ impl pallet_storage_provider::Config for Test { type RuntimeEvent = RuntimeEvent; type Randomness = DummyRandomnessGenerator; type AuthorVrfHistory = DummyRandomnessGenerator; - type PeerId = BoundedVec>; // https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#peer-ids + type PeerId = BoundedVec>; // https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#peer-ids type Currency = Balances; type Market = Market; type ProofVerification = DummyProofsVerification; diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index da066eccc..b2436d14b 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -76,3 +76,9 @@ pub const MAX_SEAL_PROOF_BYTES: u32 = 1_920; /// References: /// * pub const MAX_POST_PROOF_BYTES: u32 = 192; + +/// The maximum amount of bytes of a libp2p Peer ID. +/// +/// References: +/// * https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#peer-ids +pub const PEER_ID_MAX_BYTES: u32 = 42; diff --git a/runtime/src/configs/mod.rs b/runtime/src/configs/mod.rs index ffa88195f..fdc358615 100644 --- a/runtime/src/configs/mod.rs +++ b/runtime/src/configs/mod.rs @@ -45,6 +45,7 @@ use parachains_common::message_queue::{NarrowOriginToSibling, ParaIdToSibling}; use polkadot_runtime_common::{ xcm_sender::NoPriceForMessageDelivery, BlockHashCount, SlowAdjustingFeeUpdate, }; +use primitives::PEER_ID_MAX_BYTES; use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_runtime::{traits::Verify, MultiSignature, Perbill}; use sp_version::RuntimeVersion; @@ -368,7 +369,7 @@ impl pallet_storage_provider::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Randomness = crate::Randomness; type AuthorVrfHistory = crate::Randomness; - type PeerId = BoundedVec>; // https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#peer-ids + type PeerId = BoundedVec>; // https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#peer-ids type Currency = Balances; type Market = crate::Market; type ProofVerification = crate::Proofs; From 74f3c52a423cd2346f0ec7a5c99b119ab6fa8445 Mon Sep 17 00:00:00 2001 From: aidan46 Date: Mon, 13 Jan 2025 13:40:50 +0800 Subject: [PATCH 12/34] fix: Add p2p namespace as a const value --- storage-provider/server/src/p2p.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/storage-provider/server/src/p2p.rs b/storage-provider/server/src/p2p.rs index 76ca495e6..f5020dd80 100644 --- a/storage-provider/server/src/p2p.rs +++ b/storage-provider/server/src/p2p.rs @@ -18,6 +18,8 @@ mod register; pub(crate) use bootstrap::BootstrapConfig; pub(crate) use register::RegisterConfig; +const P2P_NAMESPACE: &str = "polka-storage"; + #[derive(Debug, Clone, Copy, ValueEnum)] pub enum NodeType { Bootstrap, @@ -103,7 +105,7 @@ pub async fn run_register_node( rendezvous_point, rendezvous_point_address, None, - Namespace::from_static("polka-storage"), + Namespace::from_static(P2P_NAMESPACE), ) => { if let Err(e) = res { error!("Failed to start P2P node. Reason: {e}"); From cb97c2e1560b243747df1d96a0e6f6f1b3a1e677 Mon Sep 17 00:00:00 2001 From: aidan46 Date: Tue, 14 Jan 2025 20:29:43 +0800 Subject: [PATCH 13/34] fix: Add new config to polka-storage-server --- storage-provider/server/src/config.rs | 30 ++++++++- storage-provider/server/src/main.rs | 69 ++++++++++++-------- storage-provider/server/src/p2p.rs | 45 ++++++++++--- storage-provider/server/src/p2p/bootstrap.rs | 9 +-- storage-provider/server/src/p2p/register.rs | 25 +++---- 5 files changed, 123 insertions(+), 55 deletions(-) diff --git a/storage-provider/server/src/config.rs b/storage-provider/server/src/config.rs index 1e71eceeb..1c0bf4414 100644 --- a/storage-provider/server/src/config.rs +++ b/storage-provider/server/src/config.rs @@ -5,11 +5,15 @@ use std::{ }; use clap::Args; +use libp2p::{identity::Keypair, Multiaddr, PeerId}; use primitives::proofs::{RegisteredPoStProof, RegisteredSealProof}; -use serde::{Deserialize, Serialize}; +use serde::Deserialize; use url::Url; -use crate::DEFAULT_NODE_ADDRESS; +use crate::{ + p2p::{deser_keypair, keypair_value_parser, string_to_peer_id_option, NodeType}, + DEFAULT_NODE_ADDRESS, +}; /// Default address to bind the RPC server to. const fn default_rpc_listen_address() -> SocketAddr { @@ -31,7 +35,7 @@ fn default_node_address() -> Url { Url::parse(DEFAULT_NODE_ADDRESS).expect("DEFAULT_NODE_ADDRESS must be a valid Url") } -#[derive(Debug, Clone, Deserialize, Serialize, Args)] +#[derive(Debug, Clone, Deserialize, Args)] #[group(multiple = true, conflicts_with = "config")] #[serde(deny_unknown_fields)] pub struct ConfigurationArgs { @@ -98,4 +102,24 @@ pub struct ConfigurationArgs { /// **they need to be set** via an extrinsic pallet-proofs::set_post_verifyingkey. #[arg(long, required = false)] pub(crate) post_parameters: PathBuf, + + /// P2P Node type, can be either a bootstrap node or a registration node. + #[arg(long, default_value = "bootstrap")] + pub(crate) node_type: NodeType, + + /// P2P ED25519 private key + #[arg(long, value_parser = keypair_value_parser)] + #[serde(deserialize_with = "deser_keypair")] + pub(crate) p2p_key: Keypair, + + /// Rendezvous point address that the registration node connects to + /// or the bootstrap node binds to. + #[arg(long)] + pub(crate) rendezvous_point_address: Multiaddr, + + /// PeerID of the bootstrap node used by the registration node. + /// Optional because it is not used by the bootstrap node. + #[arg(long)] + #[serde(deserialize_with = "string_to_peer_id_option")] + pub(crate) rendezvous_point: Option, } diff --git a/storage-provider/server/src/main.rs b/storage-provider/server/src/main.rs index aa3b68a72..204bc433c 100644 --- a/storage-provider/server/src/main.rs +++ b/storage-provider/server/src/main.rs @@ -9,14 +9,13 @@ mod pipeline; mod rpc; mod storage; -use std::{ - env::temp_dir, fs::read_to_string, net::SocketAddr, path::PathBuf, sync::Arc, - time::Duration, -}; +use std::{env::temp_dir, net::SocketAddr, path::PathBuf, sync::Arc, time::Duration}; use clap::Parser; +use libp2p::{identity::Keypair, Multiaddr, PeerId}; use p2p::{ - run_bootstrap_node, run_register_node, BootstrapConfig, NodeType, P2PError, RegisterConfig, + run_bootstrap_node, run_register_node, BootstrapConfig, NodeType, P2PError, P2PState, + RegisterConfig, }; use pipeline::types::PipelineMessage; use polka_storage_proofs::{ @@ -45,11 +44,11 @@ use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, Env use url::Url; use crate::{ + config::ConfigurationArgs, db::{DBError, DealDB}, pipeline::{start_pipeline, PipelineState}, rpc::{start_rpc_server, RpcServerState}, storage::{start_upload_server, StorageServerState}, - config::ConfigurationArgs, }; /// Default parachain node adress. @@ -88,6 +87,7 @@ struct SetupOutput { rpc_state: RpcServerState, pipeline_state: PipelineState, pipeline_rx: UnboundedReceiver, + p2p_state: P2PState, } fn main() -> Result<(), ServerError> { @@ -192,14 +192,6 @@ pub struct ServerCli { /// Path to the server configuration file. #[arg(long)] config: Option, - - /// P2P Node type, can be either a bootstrap node or a registration node. - #[arg(long, default_value = "bootstrap")] - node_type: NodeType, - - /// Path to P2P config file - #[arg(long)] - p2p_config: PathBuf, } /// A valid server configuration. To be created using [`ServerConfiguration::try_from`]. @@ -246,8 +238,16 @@ pub struct Server { /// P2P Network node type, can either be a bootstrap or registration node node_type: NodeType, - /// P2P Network node configuration file path - p2p_config: PathBuf, + /// P2P ED25519 private key + p2p_key: Keypair, + + /// Rendezvous point address that the registration node connects to + /// or the bootstrap node binds to. + rendezvous_point_address: Multiaddr, + + /// PeerID of the bootstrap node used by the registration node. + /// Optional because it is not used by the bootstrap node. + rendezvous_point: Option, } impl TryFrom for Server { @@ -325,38 +325,44 @@ impl TryFrom for Server { porep_parameters, post_parameters, parallel_prove_commits: args.parallel_prove_commits.get(), - node_type: value.node_type, - p2p_config: value.p2p_config, + node_type: args.node_type, + p2p_key: args.p2p_key, + rendezvous_point_address: args.rendezvous_point_address, + rendezvous_point: args.rendezvous_point, }) } } impl Server { pub async fn run(self) -> Result<(), ServerError> { - let p2p_config = self.p2p_config.clone(); let node_type = self.node_type; let SetupOutput { storage_state, rpc_state, pipeline_state, pipeline_rx, + p2p_state, } = self.setup().await?; let cancellation_token = CancellationToken::new(); let p2p_task = match node_type { NodeType::Bootstrap => { - let contents = read_to_string(p2p_config)?; - let config: BootstrapConfig = - toml::from_str(&contents).map_err(|e| P2PError::TOMLError(e))?; + let config = + BootstrapConfig::new(p2p_state.p2p_key, p2p_state.rendezvous_point_address); tokio::spawn(run_bootstrap_node(config, cancellation_token.child_token())) } - NodeType::Register => { - let contents = read_to_string(p2p_config)?; - let config: RegisterConfig = - toml::from_str(&contents).map_err(|e| P2PError::TOMLError(e))?; - tokio::spawn(run_register_node(config, cancellation_token.child_token())) - } + NodeType::Register => match p2p_state.rendezvous_point { + Some(rendezvous_point) => { + let config = RegisterConfig::new( + p2p_state.p2p_key, + p2p_state.rendezvous_point_address, + rendezvous_point, + ); + tokio::spawn(run_register_node(config, cancellation_token.child_token())) + } + None => return Err(ServerError::P2P(P2PError::InvalidBehaviourConfig)), + }, }; let rpc_task = tokio::spawn(start_rpc_server( rpc_state, @@ -484,11 +490,18 @@ impl Server { prove_commit_throttle: Arc::new(Semaphore::new(self.parallel_prove_commits)), }; + let p2p_state = P2PState { + p2p_key: self.p2p_key, + rendezvous_point_address: self.rendezvous_point_address, + rendezvous_point: self.rendezvous_point, + }; + Ok(SetupOutput { storage_state, rpc_state, pipeline_state, pipeline_rx, + p2p_state, }) } diff --git a/storage-provider/server/src/p2p.rs b/storage-provider/server/src/p2p.rs index f5020dd80..52e93a375 100644 --- a/storage-provider/server/src/p2p.rs +++ b/storage-provider/server/src/p2p.rs @@ -8,7 +8,7 @@ use libp2p::{ swarm::SwarmEvent, Multiaddr, PeerId, Swarm, }; use register::{RegisterBehaviour, RegisterBehaviourEvent}; -use serde::de; +use serde::{de, Deserialize}; use tokio_util::{sync::CancellationToken, task::TaskTracker}; use tracing::{error, info}; @@ -20,7 +20,7 @@ pub(crate) use register::RegisterConfig; const P2P_NAMESPACE: &str = "polka-storage"; -#[derive(Debug, Clone, Copy, ValueEnum)] +#[derive(Debug, Clone, Copy, ValueEnum, Deserialize)] pub enum NodeType { Bootstrap, Register, @@ -50,19 +50,46 @@ pub enum P2PError { P2PTransportError(#[from] libp2p::TransportError), } -fn deser_keypair<'de, D: de::Deserializer<'de>>(d: D) -> Result { +pub(crate) struct P2PState { + /// P2P ED25519 private key + pub(crate) p2p_key: Keypair, + + /// Rendezvous point address that the registration node connects to + /// or the bootstrap node binds to. + pub(crate) rendezvous_point_address: Multiaddr, + + /// PeerID of the bootstrap node used by the registration node. + /// Optional because it is not used by the bootstrap node. + pub(crate) rendezvous_point: Option, +} + +pub(crate) fn deser_keypair<'de, D: de::Deserializer<'de>>(d: D) -> Result { let src: String = de::Deserialize::deserialize(d)?; + keypair_value_parser(&src).map_err(de::Error::custom) +} + +pub(crate) fn keypair_value_parser(src: &str) -> Result { let key = if let Some(stripped) = src.strip_prefix('@') { let path = PathBuf::from_str(stripped) - .map_err(de::Error::custom)? + .map_err(|e| e.to_string())? .canonicalize() - .map_err(de::Error::custom)?; - SigningKey::read_pkcs8_pem_file(path).map_err(de::Error::custom)? + .map_err(|e| e.to_string())?; + SigningKey::read_pkcs8_pem_file(path).map_err(|e| e.to_string())? } else { - let hex_key = hex::decode(src).map_err(de::Error::custom)?; - SigningKey::try_from(hex_key.as_slice()).map_err(de::Error::custom)? + let hex_key = hex::decode(src).map_err(|e| e.to_string())?; + SigningKey::try_from(hex_key.as_slice()).map_err(|e| e.to_string())? }; - Keypair::ed25519_from_bytes(key.to_bytes()).map_err(de::Error::custom) + Keypair::ed25519_from_bytes(key.to_bytes()).map_err(|e| e.to_string()) +} + +pub(crate) fn string_to_peer_id_option<'de, D: de::Deserializer<'de>>( + d: D, +) -> Result, D::Error> { + let s: Option = de::Deserialize::deserialize(d)?; + match s { + Some(s) => Ok(Some(PeerId::from_str(&s).map_err(de::Error::custom)?)), + None => Ok(None), + } } pub async fn run_bootstrap_node( diff --git a/storage-provider/server/src/p2p/bootstrap.rs b/storage-provider/server/src/p2p/bootstrap.rs index ea813bcd5..37ff23289 100644 --- a/storage-provider/server/src/p2p/bootstrap.rs +++ b/storage-provider/server/src/p2p/bootstrap.rs @@ -4,9 +4,8 @@ use libp2p::{ identify, identity::Keypair, noise, rendezvous, swarm::NetworkBehaviour, tcp, yamux, Multiaddr, Swarm, SwarmBuilder, }; -use serde::Deserialize; -use super::{deser_keypair, P2PError}; +use super::P2PError; #[derive(NetworkBehaviour)] pub struct BootstrapBehaviour { @@ -14,14 +13,16 @@ pub struct BootstrapBehaviour { pub identify: identify::Behaviour, } -#[derive(Deserialize)] pub struct BootstrapConfig { address: Multiaddr, - #[serde(deserialize_with = "deser_keypair")] keypair: Keypair, } impl BootstrapConfig { + pub fn new(keypair: Keypair, address: Multiaddr) -> Self { + Self { address, keypair } + } + pub fn create_swarm(self) -> Result<(Swarm, Multiaddr), P2PError> { let swarm = SwarmBuilder::with_existing_identity(self.keypair) .with_tokio() diff --git a/storage-provider/server/src/p2p/register.rs b/storage-provider/server/src/p2p/register.rs index 2c5a78d0f..b7c2ea1ae 100644 --- a/storage-provider/server/src/p2p/register.rs +++ b/storage-provider/server/src/p2p/register.rs @@ -1,12 +1,11 @@ -use std::{str::FromStr, time::Duration}; +use std::time::Duration; use libp2p::{ identify, identity::Keypair, noise, rendezvous, swarm::NetworkBehaviour, tcp, yamux, Multiaddr, PeerId, Swarm, SwarmBuilder, }; -use serde::{de, Deserialize}; -use super::{deser_keypair, P2PError}; +use super::P2PError; #[derive(NetworkBehaviour)] pub struct RegisterBehaviour { @@ -14,21 +13,25 @@ pub struct RegisterBehaviour { pub rendezvous: rendezvous::client::Behaviour, } -fn string_to_peer_id<'de, D: de::Deserializer<'de>>(d: D) -> Result { - let s: String = de::Deserialize::deserialize(d)?; - PeerId::from_str(&s).map_err(de::Error::custom) -} - -#[derive(Deserialize)] pub struct RegisterConfig { - #[serde(deserialize_with = "deser_keypair")] keypair: Keypair, rendezvous_point_address: Multiaddr, - #[serde(deserialize_with = "string_to_peer_id")] rendezvous_point: PeerId, } impl RegisterConfig { + pub fn new( + keypair: Keypair, + rendezvous_point_address: Multiaddr, + rendezvous_point: PeerId, + ) -> Self { + Self { + keypair, + rendezvous_point_address, + rendezvous_point, + } + } + pub fn create_swarm(self) -> Result<(Swarm, Multiaddr, PeerId), P2PError> { let swarm = SwarmBuilder::with_existing_identity(self.keypair) .with_tokio() From 8c84b155e3ab763b45901eb3c607ce4514d003c3 Mon Sep 17 00:00:00 2001 From: aidan46 Date: Wed, 15 Jan 2025 15:19:51 +0800 Subject: [PATCH 14/34] docs: Update polka-storage-provider-server docs --- docs/src/storage-provider-cli/server.md | 44 ++++++++++++++++++------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/docs/src/storage-provider-cli/server.md b/docs/src/storage-provider-cli/server.md index 1a4ae3ab5..df4af597c 100644 --- a/docs/src/storage-provider-cli/server.md +++ b/docs/src/storage-provider-cli/server.md @@ -81,22 +81,42 @@ The path to the PoRep proving parameters. They are shared across all of the node The path to the PoSt proving parameters. They are shared across all of the nodes in the network, as the chain stores corresponding Verifying Key parameters. +### `--node-type` + +The P2P node type that the server will be running. Can be either `bootstrap` or `registration` node. + +### `--p2p-key` + +The ED25519 private key used by the P2P node. + +### `--rendezvous-point-address` + +Rendezvous point address that the registration node connects to or the bootstrap node binds to. + +### `--rendezvous-point` + +Peer ID of the rendezvous point that the registration node connects to. Only needed if running a registration P2P node. + ### `--config` Takes in a path to a configuration file, it supports both JSON and TOML (files _must_ have the right extension). The supported configuration parameters are: -| Name | Default | -| ----------------------- | ------------------------------ | -| `upload-listen-address` | `127.0.0.1:8000` | -| `rpc-listen-address` | `127.0.0.1:8001` | -| `node-url` | `ws://127.0.0.1:42069` | -| `database-directory` | `/tmp//deals_database` | -| `storage-directory` | `/tmp//deals_storage` | -| `seal-proof` | 2KiB | -| `post-proof` | 2KiB | -| `porep_parameters` | NA | -| `post_parameters` | NA | +| Name | Default | +| -------------------------- | ------------------------------ | +| `upload-listen-address` | `127.0.0.1:8000` | +| `rpc-listen-address` | `127.0.0.1:8001` | +| `node-url` | `ws://127.0.0.1:42069` | +| `database-directory` | `/tmp//deals_database` | +| `storage-directory` | `/tmp//deals_storage` | +| `seal-proof` | 2KiB | +| `post-proof` | 2KiB | +| `porep_parameters` | NA | +| `post_parameters` | NA | +| `node_type` | `bootstrap` | +| `p2p_key` | NA | +| `rendezvous_point_address` | NA | +| `rendezvous_point` | `None` | #### Bare bones configuration @@ -104,5 +124,7 @@ The supported configuration parameters are: { "porep_parameters": "/home/storage_provider/porep.params", "post_parameters": "/home/storage_provider/post.params", + "p2p_key": "/home/storage_provider/private_key.pem", + "rendezvous_point_address": "/ip4/127.0.0.1/tcp/62649", } ``` From 731e867e9b0ba4af733d7086e6902dbff5d899ad Mon Sep 17 00:00:00 2001 From: aidan46 <47111423+aidan46@users.noreply.github.com> Date: Wed, 15 Jan 2025 09:19:49 +0100 Subject: [PATCH 15/34] fix: Add p2p node to example scripts (#673) --- examples/rpc_publish.sh | 17 +++++++++++++++-- examples/start_sp.sh | 15 +++++++++++++-- storage-provider/server/src/config.rs | 3 ++- storage-provider/server/src/p2p.rs | 3 ++- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/examples/rpc_publish.sh b/examples/rpc_publish.sh index d22693e42..5e2437445 100755 --- a/examples/rpc_publish.sh +++ b/examples/rpc_publish.sh @@ -22,11 +22,24 @@ PROVIDER="//Charlie" INPUT_FILE="$1" INPUT_FILE_NAME="$(basename "$INPUT_FILE")" INPUT_TMP_FILE="/tmp/$INPUT_FILE_NAME.car" +P2P_PUBLIC_KEY="/tmp/public.pem" +P2P_PRIVATE_KEY="/tmp/private.pem" +P2P_ADDRESS="/ip4/127.0.0.1/tcp/62649" +# Generate ED25519 private key +openssl genpkey -algorithm ED25519 -out "$P2P_PRIVATE_KEY" -outpubkey "$P2P_PUBLIC_KEY" target/release/mater-cli convert -q --overwrite "$INPUT_FILE" "$INPUT_TMP_FILE" && INPUT_COMMP="$(target/release/polka-storage-provider-client proofs commp "$INPUT_TMP_FILE")" PIECE_CID="$(echo "$INPUT_COMMP" | jq -r ".cid")" PIECE_SIZE="$(echo "$INPUT_COMMP" | jq ".size")" +PEER_ID="$(target/release/polka-storage-provider-client generate-peer-id --pubkey "$P2P_PUBLIC_KEY")" +CONFIG="/tmp/bootstrap.toml" +echo "seal_proof = '2KiB' +post_proof = '2KiB' +porep_parameters = '2KiB.porep.params' +post_parameters = '2KiB.post.params' +rendezvous_point_address = '$P2P_ADDRESS' +p2p_key = '@$P2P_PRIVATE_KEY'" > "$CONFIG" # Setup balances @@ -39,7 +52,7 @@ wait # It's a test setup based on the local verifying keys, everyone can run those extrinsics currently. # Each of the keys is different, because the processes are running in parallel. # If they were running in parallel on the same account, they'd conflict with each other on the transaction nonce. -target/release/storagext-cli --sr25519-key "//Charlie" storage-provider register "peer_id" & +target/release/storagext-cli --sr25519-key "//Charlie" storage-provider register "$PEER_ID" & target/release/storagext-cli --sr25519-key "//Alice" proofs set-porep-verifying-key @2KiB.porep.vk.scale & target/release/storagext-cli --sr25519-key "//Bob" proofs set-post-verifying-key @2KiB.post.vk.scale & @@ -64,7 +77,7 @@ DEAL_JSON=$( ) SIGNED_DEAL_JSON="$(RUST_LOG=error target/release/polka-storage-provider-client sign-deal --sr25519-key "$CLIENT" "$DEAL_JSON")" -(RUST_LOG=debug target/release/polka-storage-provider-server --sr25519-key "$PROVIDER" --seal-proof "2KiB" --post-proof "2KiB" --porep-parameters 2KiB.porep.params --post-parameters 2KiB.post.params) & +(RUST_LOG=debug target/release/polka-storage-provider-server --sr25519-key "$PROVIDER" --config "$CONFIG") & sleep 5 # gives time for the server to start DEAL_CID="$(RUST_LOG=error target/release/polka-storage-provider-client propose-deal "$DEAL_JSON")" diff --git a/examples/start_sp.sh b/examples/start_sp.sh index 47f48f416..4d10421e3 100755 --- a/examples/start_sp.sh +++ b/examples/start_sp.sh @@ -8,6 +8,15 @@ export DISABLE_XT_WAIT_WARNING=1 CLIENT="//Alice" PROVIDER="//Charlie" +P2P_PUBLIC_KEY="/tmp/public.pem" +P2P_PRIVATE_KEY="/tmp/private.pem" +P2P_ADDRESS="/ip4/127.0.0.1/tcp/62649" + +# Generate ED25519 private key +openssl genpkey -algorithm ED25519 -out "$P2P_PRIVATE_KEY" -outpubkey "$P2P_PUBLIC_KEY" + +# Generate Peer ID +PEER_ID="$(target/release/polka-storage-provider-client generate-peer-id --pubkey "$P2P_PUBLIC_KEY")" # Setup balances RUST_LOG=debug target/release/storagext-cli --sr25519-key "$CLIENT" market add-balance 250000000000 & @@ -19,7 +28,7 @@ wait # It's a test setup based on the local verifying keys, everyone can run those extrinsics currently. # Each of the keys is different, because the processes are running in parallel. # If they were running in parallel on the same account, they'd conflict with each other on the transaction nonce. -RUST_LOG=debug target/release/storagext-cli --sr25519-key "//Charlie" storage-provider register "peer_id" & +RUST_LOG=debug target/release/storagext-cli --sr25519-key "//Charlie" storage-provider register "$PEER_ID" & RUST_LOG=debug target/release/storagext-cli --sr25519-key "//Alice" proofs set-porep-verifying-key @2KiB.porep.vk.scale & RUST_LOG=debug target/release/storagext-cli --sr25519-key "//Bob" proofs set-post-verifying-key @2KiB.post.vk.scale & wait @@ -28,7 +37,9 @@ echo '{ "seal_proof": "2KiB", "post_proof": "2KiB", "porep_parameters": "2KiB.porep.params", - "post_parameters": "2KiB.post.params" + "post_parameters": "2KiB.post.params", + "p2p_key": "@/tmp/private.pem", + "rendezvous_point_address": "/ip4/127.0.0.1/tcp/62649" }' > /tmp/storage_provider.config.json RUST_LOG=debug target/release/polka-storage-provider-server \ --sr25519-key "$PROVIDER" \ diff --git a/storage-provider/server/src/config.rs b/storage-provider/server/src/config.rs index 1c0bf4414..2235cfb89 100644 --- a/storage-provider/server/src/config.rs +++ b/storage-provider/server/src/config.rs @@ -104,6 +104,7 @@ pub struct ConfigurationArgs { pub(crate) post_parameters: PathBuf, /// P2P Node type, can be either a bootstrap node or a registration node. + #[serde(default = "NodeType::default")] #[arg(long, default_value = "bootstrap")] pub(crate) node_type: NodeType, @@ -120,6 +121,6 @@ pub struct ConfigurationArgs { /// PeerID of the bootstrap node used by the registration node. /// Optional because it is not used by the bootstrap node. #[arg(long)] - #[serde(deserialize_with = "string_to_peer_id_option")] + #[serde(default, deserialize_with = "string_to_peer_id_option")] pub(crate) rendezvous_point: Option, } diff --git a/storage-provider/server/src/p2p.rs b/storage-provider/server/src/p2p.rs index 52e93a375..8821fec45 100644 --- a/storage-provider/server/src/p2p.rs +++ b/storage-provider/server/src/p2p.rs @@ -20,8 +20,9 @@ pub(crate) use register::RegisterConfig; const P2P_NAMESPACE: &str = "polka-storage"; -#[derive(Debug, Clone, Copy, ValueEnum, Deserialize)] +#[derive(Default, Debug, Clone, Copy, ValueEnum, Deserialize)] pub enum NodeType { + #[default] Bootstrap, Register, } From 7f8b7ad69158470ad24a638f784d34e584df7cdc Mon Sep 17 00:00:00 2001 From: aidan46 Date: Wed, 15 Jan 2025 16:23:02 +0800 Subject: [PATCH 16/34] fix: Remove peer ID writing to file --- storage-provider/client/src/commands/mod.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/storage-provider/client/src/commands/mod.rs b/storage-provider/client/src/commands/mod.rs index 50cd7f7e2..96b808b1e 100644 --- a/storage-provider/client/src/commands/mod.rs +++ b/storage-provider/client/src/commands/mod.rs @@ -1,7 +1,7 @@ mod proofs; mod wallet; -use std::{fs, path::PathBuf}; +use std::path::PathBuf; use clap::Parser; use ed25519_dalek::pkcs8::{DecodePublicKey, PublicKeyBytes}; @@ -114,11 +114,6 @@ pub(crate) enum Cli { /// Path to the ED25519 public key pem file #[arg(long)] pubkey: PathBuf, - - /// Path to the output file to write the peer ID to. - /// If `None` the peer ID will be printed to stdout. - #[arg(long)] - file: Option, }, } @@ -153,7 +148,7 @@ impl Cli { deal_proposal, signer_key, } => Self::sign_deal(deal_proposal, signer_key), - Self::GeneratePeerID { pubkey, file } => Self::generate_peer_id(pubkey, file), + Self::GeneratePeerID { pubkey } => Self::generate_peer_id(pubkey), } } @@ -206,16 +201,13 @@ impl Cli { Ok(()) } - fn generate_peer_id(path: PathBuf, file: Option) -> Result<(), CliError> { + fn generate_peer_id(path: PathBuf) -> Result<(), CliError> { let pubkey_bytes = PublicKeyBytes::read_public_key_pem_file(path)?; let pubkey = EdPubKey::try_from_bytes(&pubkey_bytes.to_bytes())?; let key = libp2p::identity::PublicKey::from(pubkey); let peer_id = PeerId::from_public_key(&key); - match file { - None => println!("{peer_id}"), - Some(path) => fs::write(path, &peer_id.to_string().as_bytes())?, - } + println!("{peer_id}"); Ok(()) } } From 30e5fc4d2cef69b253e4b0c6e21e33b70433244c Mon Sep 17 00:00:00 2001 From: aidan46 Date: Wed, 15 Jan 2025 16:27:26 +0800 Subject: [PATCH 17/34] fix: Use debug for NodeType Display --- storage-provider/server/src/p2p.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage-provider/server/src/p2p.rs b/storage-provider/server/src/p2p.rs index 8821fec45..69f8355f0 100644 --- a/storage-provider/server/src/p2p.rs +++ b/storage-provider/server/src/p2p.rs @@ -29,7 +29,7 @@ pub enum NodeType { impl Display for NodeType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self) + std::fmt::Debug::fmt(&self, f) } } From 1aab240c332dfb16abbc0c28db81da1e9d9383eb Mon Sep 17 00:00:00 2001 From: aidan46 Date: Wed, 15 Jan 2025 16:28:31 +0800 Subject: [PATCH 18/34] fix: P2PError members naming --- storage-provider/server/src/p2p.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage-provider/server/src/p2p.rs b/storage-provider/server/src/p2p.rs index 69f8355f0..190daf064 100644 --- a/storage-provider/server/src/p2p.rs +++ b/storage-provider/server/src/p2p.rs @@ -38,7 +38,7 @@ pub enum P2PError { #[error(transparent)] Io(#[from] std::io::Error), #[error(transparent)] - DialError(#[from] libp2p::swarm::DialError), + Dial(#[from] libp2p::swarm::DialError), #[error("Invalid TCP config for swarm")] InvalidTcpConfig, #[error("Invalid behaviour config for swarm")] @@ -48,7 +48,7 @@ pub enum P2PError { #[error("Failed to register at rendezvous point {0}")] RegistrationFailed(PeerId), #[error(transparent)] - P2PTransportError(#[from] libp2p::TransportError), + P2PTransport(#[from] libp2p::TransportError), } pub(crate) struct P2PState { From 54f32a51a548192df32173c79d55b4aa7f70080a Mon Sep 17 00:00:00 2001 From: aidan46 Date: Wed, 15 Jan 2025 16:31:53 +0800 Subject: [PATCH 19/34] fix: Fully qualify tracing logs --- storage-provider/server/src/p2p.rs | 45 +++++++++++++++++------------- storage-provider/server/src/rpc.rs | 6 ++-- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/storage-provider/server/src/p2p.rs b/storage-provider/server/src/p2p.rs index 190daf064..e4bcf8c6a 100644 --- a/storage-provider/server/src/p2p.rs +++ b/storage-provider/server/src/p2p.rs @@ -10,7 +10,6 @@ use libp2p::{ use register::{RegisterBehaviour, RegisterBehaviourEvent}; use serde::{de, Deserialize}; use tokio_util::{sync::CancellationToken, task::TaskTracker}; -use tracing::{error, info}; mod bootstrap; mod register; @@ -97,14 +96,14 @@ pub async fn run_bootstrap_node( config: BootstrapConfig, token: CancellationToken, ) -> Result<(), P2PError> { - info!("Starting P2P bootstrap node"); + tracing::info!("Starting P2P bootstrap node"); let tracker = TaskTracker::new(); let (swarm, addr) = config.create_swarm()?; tokio::select! { res = bootstrap(swarm, addr) => { if let Err(e) = res { - error!("Failed to start P2P node. Reason: {e}"); + tracing::error!("Failed to start P2P node. Reason: {e}"); return Err(e); } }, @@ -123,7 +122,7 @@ pub async fn run_register_node( config: RegisterConfig, token: CancellationToken, ) -> Result<(), P2PError> { - info!("Starting P2P register node"); + tracing::info!("Starting P2P register node"); let tracker = TaskTracker::new(); let (swarm, rendezvous_point_address, rendezvous_point) = config.create_swarm()?; @@ -136,7 +135,7 @@ pub async fn run_register_node( Namespace::from_static(P2P_NAMESPACE), ) => { if let Err(e) = res { - error!("Failed to start P2P node. Reason: {e}"); + tracing::error!("Failed to start P2P node. Reason: {e}"); return Err(e); } }, @@ -160,34 +159,34 @@ async fn register( ttl: Option, namespace: Namespace, ) -> Result<(), P2PError> { - info!("Attempting to register with rendezvous point {rendezvous_point} at {rendezvous_point_address}"); + tracing::info!("Attempting to register with rendezvous point {rendezvous_point} at {rendezvous_point_address}"); swarm.dial(rendezvous_point_address.clone())?; while let Some(event) = swarm.next().await { match event { SwarmEvent::NewListenAddr { address, .. } => { - info!("Listening on {}", address); + tracing::info!("Listening on {}", address); } SwarmEvent::ConnectionClosed { peer_id, cause: Some(error), .. } if peer_id == rendezvous_point => { - info!("Lost connection to rendezvous point {}", error); + tracing::info!("Lost connection to rendezvous point {}", error); } // once `/identify` did its job, we know our external address and can register SwarmEvent::Behaviour(RegisterBehaviourEvent::Identify( identify::Event::Received { info, .. }, )) => { // Register our external address. - info!("Registering external address {}", info.observed_addr); + tracing::info!("Registering external address {}", info.observed_addr); swarm.add_external_address(info.observed_addr); if let Err(error) = swarm.behaviour_mut().rendezvous.register( namespace.clone(), rendezvous_point, ttl, ) { - error!("Failed to register: {error}"); + tracing::error!("Failed to register: {error}"); return Err(P2PError::RegistrationFailed(rendezvous_point)); } } @@ -198,9 +197,11 @@ async fn register( rendezvous_node, }, )) => { - info!( + tracing::info!( "Registered for namespace '{}' at rendezvous point {} for the next {} seconds", - namespace, rendezvous_node, ttl + namespace, + rendezvous_node, + ttl ); return Ok(()); } @@ -211,9 +212,11 @@ async fn register( error, }, )) => { - error!( + tracing::error!( "Failed to register: rendezvous_node={}, namespace={}, error_code={:?}", - rendezvous_node, namespace, error + rendezvous_node, + namespace, + error ); return Err(P2PError::RegistrationFailed(rendezvous_node)); } @@ -227,22 +230,24 @@ async fn register( /// Run the rendezvous point (bootstrap node). /// Listens on the given [`Multiaddr`] async fn bootstrap(mut swarm: Swarm, addr: Multiaddr) -> Result<(), P2PError> { - info!("Starting P2P bootstrap node at {addr}"); + tracing::info!("Starting P2P bootstrap node at {addr}"); swarm.listen_on(addr)?; while let Some(event) = swarm.next().await { match event { SwarmEvent::ConnectionEstablished { peer_id, .. } => { - info!("Connected to {}", peer_id); + tracing::info!("Connected to {}", peer_id); } SwarmEvent::ConnectionClosed { peer_id, .. } => { - info!("Disconnected from {}", peer_id); + tracing::info!("Disconnected from {}", peer_id); } SwarmEvent::Behaviour(BootstrapBehaviourEvent::Rendezvous( rendezvous::server::Event::PeerRegistered { peer, registration }, )) => { - info!( + tracing::info!( "Peer {} registered for namespace '{}' for {} seconds", - peer, registration.namespace, registration.ttl + peer, + registration.namespace, + registration.ttl ); } SwarmEvent::Behaviour(BootstrapBehaviourEvent::Rendezvous( @@ -252,7 +257,7 @@ async fn bootstrap(mut swarm: Swarm, addr: Multiaddr) -> Res }, )) => { if !registrations.is_empty() { - info!( + tracing::info!( "Served peer {} with {} new registrations", enquirer, registrations.len() diff --git a/storage-provider/server/src/rpc.rs b/storage-provider/server/src/rpc.rs index 75760e966..ffbb1cd87 100644 --- a/storage-provider/server/src/rpc.rs +++ b/storage-provider/server/src/rpc.rs @@ -14,7 +14,7 @@ use subxt::tx::Signer; use tokio::sync::mpsc::UnboundedSender; use tokio_util::sync::CancellationToken; use tower_http::cors::{Any, CorsLayer}; -use tracing::{info, instrument}; +use tracing::instrument; use crate::{ db::DealDB, @@ -222,7 +222,7 @@ pub async fn start_rpc_server( state: RpcServerState, token: CancellationToken, ) -> Result<(), std::io::Error> { - info!("Starting RPC server at {}", state.listen_address); + tracing::info!("Starting RPC server at {}", state.listen_address); let cors = CorsLayer::new() .allow_methods([Method::POST]) @@ -238,7 +238,7 @@ pub async fn start_rpc_server( let rpc = StorageProviderRpcServer::into_rpc(state); let server_handle = server.start(rpc); - info!("RPC server started"); + tracing::info!("RPC server started"); token.cancelled_owned().await; tracing::trace!("shutdown signal received, stopping the RPC server"); From 8464af896ec9aaeeb3e04370138511fc7e76b38c Mon Sep 17 00:00:00 2001 From: aidan46 Date: Wed, 15 Jan 2025 16:42:35 +0800 Subject: [PATCH 20/34] docs: Add docs to p2p functions --- storage-provider/server/src/config.rs | 4 ++-- storage-provider/server/src/p2p.rs | 11 +++++++++++ storage-provider/server/src/p2p/bootstrap.rs | 2 ++ storage-provider/server/src/p2p/register.rs | 2 ++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/storage-provider/server/src/config.rs b/storage-provider/server/src/config.rs index 2235cfb89..12927355f 100644 --- a/storage-provider/server/src/config.rs +++ b/storage-provider/server/src/config.rs @@ -109,8 +109,8 @@ pub struct ConfigurationArgs { pub(crate) node_type: NodeType, /// P2P ED25519 private key - #[arg(long, value_parser = keypair_value_parser)] #[serde(deserialize_with = "deser_keypair")] + #[arg(long, value_parser = keypair_value_parser)] pub(crate) p2p_key: Keypair, /// Rendezvous point address that the registration node connects to @@ -120,7 +120,7 @@ pub struct ConfigurationArgs { /// PeerID of the bootstrap node used by the registration node. /// Optional because it is not used by the bootstrap node. - #[arg(long)] #[serde(default, deserialize_with = "string_to_peer_id_option")] + #[arg(long)] pub(crate) rendezvous_point: Option, } diff --git a/storage-provider/server/src/p2p.rs b/storage-provider/server/src/p2p.rs index e4bcf8c6a..4e5c17d17 100644 --- a/storage-provider/server/src/p2p.rs +++ b/storage-provider/server/src/p2p.rs @@ -63,11 +63,16 @@ pub(crate) struct P2PState { pub(crate) rendezvous_point: Option, } +/// Deserializes a ED25519 private key into a Keypair. +/// Can either be the private key as a string or the path of a PEM file with an @ prefixed +/// Calls `keypair_value_parser` after deserializing the source string pub(crate) fn deser_keypair<'de, D: de::Deserializer<'de>>(d: D) -> Result { let src: String = de::Deserialize::deserialize(d)?; keypair_value_parser(&src).map_err(de::Error::custom) } +/// Parses a ED25519 private key into a Keypair. +/// Takes in a private key or the path to a PEM file, depending on the @ prefix. pub(crate) fn keypair_value_parser(src: &str) -> Result { let key = if let Some(stripped) = src.strip_prefix('@') { let path = PathBuf::from_str(stripped) @@ -82,6 +87,8 @@ pub(crate) fn keypair_value_parser(src: &str) -> Result { Keypair::ed25519_from_bytes(key.to_bytes()).map_err(|e| e.to_string()) } +/// Parses a string to an optional Peer ID. +/// Used in the [`ConfigurationArgs`] rendezvous_point field. pub(crate) fn string_to_peer_id_option<'de, D: de::Deserializer<'de>>( d: D, ) -> Result, D::Error> { @@ -92,6 +99,8 @@ pub(crate) fn string_to_peer_id_option<'de, D: de::Deserializer<'de>>( } } +/// Runs a bootstrap node from the given config. +/// The `CancellationToken` is used for a graceful shutdown if the user presses ctrl+c pub async fn run_bootstrap_node( config: BootstrapConfig, token: CancellationToken, @@ -118,6 +127,8 @@ pub async fn run_bootstrap_node( Ok(()) } +/// Runs a registration node from the given config. +/// The `CancellationToken` is used for a graceful shutdown if the user presses ctrl+c pub async fn run_register_node( config: RegisterConfig, token: CancellationToken, diff --git a/storage-provider/server/src/p2p/bootstrap.rs b/storage-provider/server/src/p2p/bootstrap.rs index 37ff23289..d480561e8 100644 --- a/storage-provider/server/src/p2p/bootstrap.rs +++ b/storage-provider/server/src/p2p/bootstrap.rs @@ -33,9 +33,11 @@ impl BootstrapConfig { ) .map_err(|_| P2PError::InvalidTcpConfig)? .with_behaviour(|key| BootstrapBehaviour { + // Rendezvous server behaviour for serving new peers to connecting nodes. rendezvous: rendezvous::server::Behaviour::new( rendezvous::server::Config::default(), ), + // The identify behaviour is used to share the external address and the public key with connecting clients. identify: identify::Behaviour::new(identify::Config::new( "identify/1.0.0".to_string(), key.public(), diff --git a/storage-provider/server/src/p2p/register.rs b/storage-provider/server/src/p2p/register.rs index b7c2ea1ae..c93441031 100644 --- a/storage-provider/server/src/p2p/register.rs +++ b/storage-provider/server/src/p2p/register.rs @@ -42,10 +42,12 @@ impl RegisterConfig { ) .map_err(|_| P2PError::InvalidTcpConfig)? .with_behaviour(|key| RegisterBehaviour { + // The identify behaviour is used to receive the bootstrap node's external address and public key. identify: identify::Behaviour::new(identify::Config::new( "identify/1.0.0".to_string(), key.public(), )), + // The rendezvous client behaviour allows the bootstrap node to share peer information with us. rendezvous: rendezvous::client::Behaviour::new(key.clone()), }) .map_err(|_| P2PError::InvalidBehaviourConfig)? From 2ed411e7c759605c362e30f59e5017ada6e12373 Mon Sep 17 00:00:00 2001 From: aidan46 Date: Wed, 15 Jan 2025 16:50:19 +0800 Subject: [PATCH 21/34] fix: Move register and bootstrap fn into their modules --- storage-provider/server/src/p2p.rs | 129 +------------------ storage-provider/server/src/p2p/bootstrap.rs | 50 ++++++- storage-provider/server/src/p2p/register.rs | 81 +++++++++++- 3 files changed, 130 insertions(+), 130 deletions(-) diff --git a/storage-provider/server/src/p2p.rs b/storage-provider/server/src/p2p.rs index 4e5c17d17..14eaeaceb 100644 --- a/storage-provider/server/src/p2p.rs +++ b/storage-provider/server/src/p2p.rs @@ -1,13 +1,10 @@ use std::{fmt::Display, path::PathBuf, str::FromStr}; -use bootstrap::{BootstrapBehaviour, BootstrapBehaviourEvent}; +use bootstrap::bootstrap; use clap::ValueEnum; use ed25519_dalek::{pkcs8::DecodePrivateKey, SigningKey}; -use libp2p::{ - futures::StreamExt, identify, identity::Keypair, rendezvous, rendezvous::Namespace, - swarm::SwarmEvent, Multiaddr, PeerId, Swarm, -}; -use register::{RegisterBehaviour, RegisterBehaviourEvent}; +use libp2p::{identity::Keypair, rendezvous::Namespace, Multiaddr, PeerId}; +use register::register; use serde::{de, Deserialize}; use tokio_util::{sync::CancellationToken, task::TaskTracker}; @@ -160,123 +157,3 @@ pub async fn run_register_node( Ok(()) } - -/// Register the peer with the rendezvous point. -/// The ttl is how long the peer will remain registered in seconds. -async fn register( - mut swarm: Swarm, - rendezvous_point: PeerId, - rendezvous_point_address: Multiaddr, - ttl: Option, - namespace: Namespace, -) -> Result<(), P2PError> { - tracing::info!("Attempting to register with rendezvous point {rendezvous_point} at {rendezvous_point_address}"); - swarm.dial(rendezvous_point_address.clone())?; - - while let Some(event) = swarm.next().await { - match event { - SwarmEvent::NewListenAddr { address, .. } => { - tracing::info!("Listening on {}", address); - } - SwarmEvent::ConnectionClosed { - peer_id, - cause: Some(error), - .. - } if peer_id == rendezvous_point => { - tracing::info!("Lost connection to rendezvous point {}", error); - } - // once `/identify` did its job, we know our external address and can register - SwarmEvent::Behaviour(RegisterBehaviourEvent::Identify( - identify::Event::Received { info, .. }, - )) => { - // Register our external address. - tracing::info!("Registering external address {}", info.observed_addr); - swarm.add_external_address(info.observed_addr); - if let Err(error) = swarm.behaviour_mut().rendezvous.register( - namespace.clone(), - rendezvous_point, - ttl, - ) { - tracing::error!("Failed to register: {error}"); - return Err(P2PError::RegistrationFailed(rendezvous_point)); - } - } - SwarmEvent::Behaviour(RegisterBehaviourEvent::Rendezvous( - rendezvous::client::Event::Registered { - namespace, - ttl, - rendezvous_node, - }, - )) => { - tracing::info!( - "Registered for namespace '{}' at rendezvous point {} for the next {} seconds", - namespace, - rendezvous_node, - ttl - ); - return Ok(()); - } - SwarmEvent::Behaviour(RegisterBehaviourEvent::Rendezvous( - rendezvous::client::Event::RegisterFailed { - rendezvous_node, - namespace, - error, - }, - )) => { - tracing::error!( - "Failed to register: rendezvous_node={}, namespace={}, error_code={:?}", - rendezvous_node, - namespace, - error - ); - return Err(P2PError::RegistrationFailed(rendezvous_node)); - } - _other => {} - } - } - - Ok(()) -} - -/// Run the rendezvous point (bootstrap node). -/// Listens on the given [`Multiaddr`] -async fn bootstrap(mut swarm: Swarm, addr: Multiaddr) -> Result<(), P2PError> { - tracing::info!("Starting P2P bootstrap node at {addr}"); - swarm.listen_on(addr)?; - while let Some(event) = swarm.next().await { - match event { - SwarmEvent::ConnectionEstablished { peer_id, .. } => { - tracing::info!("Connected to {}", peer_id); - } - SwarmEvent::ConnectionClosed { peer_id, .. } => { - tracing::info!("Disconnected from {}", peer_id); - } - SwarmEvent::Behaviour(BootstrapBehaviourEvent::Rendezvous( - rendezvous::server::Event::PeerRegistered { peer, registration }, - )) => { - tracing::info!( - "Peer {} registered for namespace '{}' for {} seconds", - peer, - registration.namespace, - registration.ttl - ); - } - SwarmEvent::Behaviour(BootstrapBehaviourEvent::Rendezvous( - rendezvous::server::Event::DiscoverServed { - enquirer, - registrations, - }, - )) => { - if !registrations.is_empty() { - tracing::info!( - "Served peer {} with {} new registrations", - enquirer, - registrations.len() - ); - } - } - _other => {} - } - } - Ok(()) -} diff --git a/storage-provider/server/src/p2p/bootstrap.rs b/storage-provider/server/src/p2p/bootstrap.rs index d480561e8..bdcd655d7 100644 --- a/storage-provider/server/src/p2p/bootstrap.rs +++ b/storage-provider/server/src/p2p/bootstrap.rs @@ -1,8 +1,8 @@ use std::time::Duration; use libp2p::{ - identify, identity::Keypair, noise, rendezvous, swarm::NetworkBehaviour, tcp, yamux, Multiaddr, - Swarm, SwarmBuilder, + futures::StreamExt, identify, identity::Keypair, noise, rendezvous, swarm::NetworkBehaviour, + swarm::SwarmEvent, tcp, yamux, Multiaddr, Swarm, SwarmBuilder, }; use super::P2PError; @@ -50,3 +50,49 @@ impl BootstrapConfig { Ok((swarm, self.address)) } } + +/// Run the rendezvous point (bootstrap node). +/// Listens on the given [`Multiaddr`] +pub(crate) async fn bootstrap( + mut swarm: Swarm, + addr: Multiaddr, +) -> Result<(), P2PError> { + tracing::info!("Starting P2P bootstrap node at {addr}"); + swarm.listen_on(addr)?; + while let Some(event) = swarm.next().await { + match event { + SwarmEvent::ConnectionEstablished { peer_id, .. } => { + tracing::info!("Connected to {}", peer_id); + } + SwarmEvent::ConnectionClosed { peer_id, .. } => { + tracing::info!("Disconnected from {}", peer_id); + } + SwarmEvent::Behaviour(BootstrapBehaviourEvent::Rendezvous( + rendezvous::server::Event::PeerRegistered { peer, registration }, + )) => { + tracing::info!( + "Peer {} registered for namespace '{}' for {} seconds", + peer, + registration.namespace, + registration.ttl + ); + } + SwarmEvent::Behaviour(BootstrapBehaviourEvent::Rendezvous( + rendezvous::server::Event::DiscoverServed { + enquirer, + registrations, + }, + )) => { + if !registrations.is_empty() { + tracing::info!( + "Served peer {} with {} new registrations", + enquirer, + registrations.len() + ); + } + } + _other => {} + } + } + Ok(()) +} diff --git a/storage-provider/server/src/p2p/register.rs b/storage-provider/server/src/p2p/register.rs index c93441031..070755887 100644 --- a/storage-provider/server/src/p2p/register.rs +++ b/storage-provider/server/src/p2p/register.rs @@ -1,8 +1,8 @@ use std::time::Duration; use libp2p::{ - identify, identity::Keypair, noise, rendezvous, swarm::NetworkBehaviour, tcp, yamux, Multiaddr, - PeerId, Swarm, SwarmBuilder, + futures::StreamExt, identify, identity::Keypair, noise, rendezvous, rendezvous::Namespace, + swarm::NetworkBehaviour, swarm::SwarmEvent, tcp, yamux, Multiaddr, PeerId, Swarm, SwarmBuilder, }; use super::P2PError; @@ -57,3 +57,80 @@ impl RegisterConfig { Ok((swarm, self.rendezvous_point_address, self.rendezvous_point)) } } + +/// Register the peer with the rendezvous point. +/// The ttl is how long the peer will remain registered in seconds. +pub(crate) async fn register( + mut swarm: Swarm, + rendezvous_point: PeerId, + rendezvous_point_address: Multiaddr, + ttl: Option, + namespace: Namespace, +) -> Result<(), P2PError> { + tracing::info!("Attempting to register with rendezvous point {rendezvous_point} at {rendezvous_point_address}"); + swarm.dial(rendezvous_point_address.clone())?; + + while let Some(event) = swarm.next().await { + match event { + SwarmEvent::NewListenAddr { address, .. } => { + tracing::info!("Listening on {}", address); + } + SwarmEvent::ConnectionClosed { + peer_id, + cause: Some(error), + .. + } if peer_id == rendezvous_point => { + tracing::info!("Lost connection to rendezvous point {}", error); + } + // once `/identify` did its job, we know our external address and can register + SwarmEvent::Behaviour(RegisterBehaviourEvent::Identify( + identify::Event::Received { info, .. }, + )) => { + // Register our external address. + tracing::info!("Registering external address {}", info.observed_addr); + swarm.add_external_address(info.observed_addr); + if let Err(error) = swarm.behaviour_mut().rendezvous.register( + namespace.clone(), + rendezvous_point, + ttl, + ) { + tracing::error!("Failed to register: {error}"); + return Err(P2PError::RegistrationFailed(rendezvous_point)); + } + } + SwarmEvent::Behaviour(RegisterBehaviourEvent::Rendezvous( + rendezvous::client::Event::Registered { + namespace, + ttl, + rendezvous_node, + }, + )) => { + tracing::info!( + "Registered for namespace '{}' at rendezvous point {} for the next {} seconds", + namespace, + rendezvous_node, + ttl + ); + return Ok(()); + } + SwarmEvent::Behaviour(RegisterBehaviourEvent::Rendezvous( + rendezvous::client::Event::RegisterFailed { + rendezvous_node, + namespace, + error, + }, + )) => { + tracing::error!( + "Failed to register: rendezvous_node={}, namespace={}, error_code={:?}", + rendezvous_node, + namespace, + error + ); + return Err(P2PError::RegistrationFailed(rendezvous_node)); + } + _other => {} + } + } + + Ok(()) +} From e816c5be327005a0200f28c67f59f0dcb4dd43d8 Mon Sep 17 00:00:00 2001 From: aidan46 Date: Wed, 15 Jan 2025 17:01:07 +0800 Subject: [PATCH 22/34] fix: Move p2p node spawning into separate function --- storage-provider/server/src/main.rs | 49 +++++++++++++++++------------ storage-provider/server/src/p2p.rs | 6 ++++ 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/storage-provider/server/src/main.rs b/storage-provider/server/src/main.rs index 204bc433c..7a365affb 100644 --- a/storage-provider/server/src/main.rs +++ b/storage-provider/server/src/main.rs @@ -36,7 +36,7 @@ use storagext::{ use subxt::{self, tx::Signer}; use tokio::{ sync::{mpsc::UnboundedReceiver, Semaphore}, - task::JoinError, + task::{JoinError, JoinHandle}, }; use tokio_util::sync::CancellationToken; use tracing::level_filters::LevelFilter; @@ -335,7 +335,6 @@ impl TryFrom for Server { impl Server { pub async fn run(self) -> Result<(), ServerError> { - let node_type = self.node_type; let SetupOutput { storage_state, rpc_state, @@ -346,24 +345,7 @@ impl Server { let cancellation_token = CancellationToken::new(); - let p2p_task = match node_type { - NodeType::Bootstrap => { - let config = - BootstrapConfig::new(p2p_state.p2p_key, p2p_state.rendezvous_point_address); - tokio::spawn(run_bootstrap_node(config, cancellation_token.child_token())) - } - NodeType::Register => match p2p_state.rendezvous_point { - Some(rendezvous_point) => { - let config = RegisterConfig::new( - p2p_state.p2p_key, - p2p_state.rendezvous_point_address, - rendezvous_point, - ); - tokio::spawn(run_register_node(config, cancellation_token.child_token())) - } - None => return Err(ServerError::P2P(P2PError::InvalidBehaviourConfig)), - }, - }; + let p2p_task = spawn_p2p_task(p2p_state, cancellation_token.child_token())?; let rpc_task = tokio::spawn(start_rpc_server( rpc_state, cancellation_token.child_token(), @@ -491,6 +473,7 @@ impl Server { }; let p2p_state = P2PState { + node_type: self.node_type, p2p_key: self.p2p_key, rendezvous_point_address: self.rendezvous_point_address, rendezvous_point: self.rendezvous_point, @@ -560,3 +543,29 @@ impl Server { } } } + +/// Spawns a p2p node and returns a `JoinHandle`. +/// The node type is either bootstrap or registration depending on the `p2p_state.node_type` value. +fn spawn_p2p_task( + p2p_state: P2PState, + cancellation_token: CancellationToken, +) -> Result>, ServerError> { + match p2p_state.node_type { + NodeType::Bootstrap => { + let config = + BootstrapConfig::new(p2p_state.p2p_key, p2p_state.rendezvous_point_address); + Ok(tokio::spawn(run_bootstrap_node(config, cancellation_token))) + } + NodeType::Register => match p2p_state.rendezvous_point { + Some(rendezvous_point) => { + let config = RegisterConfig::new( + p2p_state.p2p_key, + p2p_state.rendezvous_point_address, + rendezvous_point, + ); + Ok(tokio::spawn(run_register_node(config, cancellation_token))) + } + None => return Err(ServerError::P2P(P2PError::InvalidBehaviourConfig)), + }, + } +} diff --git a/storage-provider/server/src/p2p.rs b/storage-provider/server/src/p2p.rs index 14eaeaceb..3d750e52c 100644 --- a/storage-provider/server/src/p2p.rs +++ b/storage-provider/server/src/p2p.rs @@ -47,7 +47,13 @@ pub enum P2PError { P2PTransport(#[from] libp2p::TransportError), } +/// State struct for P2P node. +/// Holds all the information needed for spawning a node. +/// Node can be either a bootstrap or a registration node. pub(crate) struct P2PState { + /// P2P Node type, bootstrap or registration + pub(crate) node_type: NodeType, + /// P2P ED25519 private key pub(crate) p2p_key: Keypair, From 3bdf06b9e5e0681a43a9e221da784c4a9a2fa5c4 Mon Sep 17 00:00:00 2001 From: aidan46 Date: Wed, 15 Jan 2025 18:03:13 +0800 Subject: [PATCH 23/34] fix: Change default for NodeType --- storage-provider/server/src/config.rs | 2 +- storage-provider/server/src/p2p.rs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/storage-provider/server/src/config.rs b/storage-provider/server/src/config.rs index 12927355f..9b72ae5c0 100644 --- a/storage-provider/server/src/config.rs +++ b/storage-provider/server/src/config.rs @@ -105,7 +105,7 @@ pub struct ConfigurationArgs { /// P2P Node type, can be either a bootstrap node or a registration node. #[serde(default = "NodeType::default")] - #[arg(long, default_value = "bootstrap")] + #[arg(long, default_value_t = NodeType::Bootstrap)] pub(crate) node_type: NodeType, /// P2P ED25519 private key diff --git a/storage-provider/server/src/p2p.rs b/storage-provider/server/src/p2p.rs index 3d750e52c..7b24c0811 100644 --- a/storage-provider/server/src/p2p.rs +++ b/storage-provider/server/src/p2p.rs @@ -25,7 +25,10 @@ pub enum NodeType { impl Display for NodeType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Debug::fmt(&self, f) + match self { + NodeType::Bootstrap => write!(f, "bootstrap"), + NodeType::Register => write!(f, "register"), + } } } From 4e03a6dedf8859475d15ea8ef218ac100e90666a Mon Sep 17 00:00:00 2001 From: aidan46 Date: Wed, 15 Jan 2025 18:19:48 +0800 Subject: [PATCH 24/34] feat: Add macro for spawning and inspecting errors --- storage-provider/server/src/main.rs | 76 +++++++++++++++-------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/storage-provider/server/src/main.rs b/storage-provider/server/src/main.rs index 7a365affb..576c08346 100644 --- a/storage-provider/server/src/main.rs +++ b/storage-provider/server/src/main.rs @@ -12,6 +12,7 @@ mod storage; use std::{env::temp_dir, net::SocketAddr, path::PathBuf, sync::Arc, time::Duration}; use clap::Parser; +use futures::Future; use libp2p::{identity::Keypair, Multiaddr, PeerId}; use p2p::{ run_bootstrap_node, run_register_node, BootstrapConfig, NodeType, P2PError, P2PState, @@ -178,6 +179,33 @@ pub enum ServerError { P2P(#[from] P2PError), } +/// Takes an expression that returns a nested result and +/// inspected the error while logging it. +macro_rules! inspect_and_log_nested_errors { + ($($task:expr),+ $(,)?) => { + ( + $( + $task + .inspect_err(|err| tracing::error!(%err)) + .inspect(|ok| { + let _ = ok.as_ref().inspect_err(|err| tracing::error!(%err)); + }) + ),+ + ) + }; +} + +/// This macro spawns multiple async tasks and returns the JoinHandle. +macro_rules! spawn_async_tasks { + ($($task:expr),+ $(,)?) => { + ( + $( + tokio::spawn($task) + ),+ + ) + } +} + /// The server arguments, as passed by the user, unvalidated. #[derive(Debug, Parser)] #[command(author, version, about, long_about = None, arg_required_else_help = true)] @@ -346,19 +374,15 @@ impl Server { let cancellation_token = CancellationToken::new(); let p2p_task = spawn_p2p_task(p2p_state, cancellation_token.child_token())?; - let rpc_task = tokio::spawn(start_rpc_server( - rpc_state, - cancellation_token.child_token(), - )); - let storage_task = tokio::spawn(start_upload_server( - Arc::new(storage_state), - cancellation_token.child_token(), - )); - let pipeline_task = tokio::spawn(start_pipeline( - Arc::new(pipeline_state), - pipeline_rx, - cancellation_token.child_token(), - )); + let (rpc_task, storage_task, pipeline_task) = spawn_async_tasks!( + start_rpc_server(rpc_state, cancellation_token.child_token(),), + start_upload_server(Arc::new(storage_state), cancellation_token.child_token(),), + start_pipeline( + Arc::new(pipeline_state), + pipeline_rx, + cancellation_token.child_token(), + ) + ); // Wait for SIGTERM on the main thread and once received "unblock" tokio::signal::ctrl_c() @@ -373,29 +397,9 @@ impl Server { let (upload_result, rpc_task, pipeline_task, p2p_task) = tokio::join!(storage_task, rpc_task, pipeline_task, p2p_task); - // Log errors - let upload_result = upload_result - .inspect_err(|err| tracing::error!(%err)) - .inspect(|ok| { - let _ = ok.as_ref().inspect_err(|err| tracing::error!(%err)); - }); - let rpc_task = rpc_task - .inspect_err(|err| tracing::error!(%err)) - .inspect(|ok| { - let _ = ok.as_ref().inspect_err(|err| tracing::error!(%err)); - }); - - let pipeline_task = pipeline_task - .inspect_err(|err| tracing::error!(%err)) - .inspect(|ok| { - let _ = ok.as_ref().inspect_err(|err| tracing::error!(%err)); - }); - - let p2p_task = p2p_task - .inspect_err(|err| tracing::error!(%err)) - .inspect(|ok| { - let _ = ok.as_ref().inspect_err(|err| tracing::error!(%err)); - }); + // Inspect and log errors + let (upload_result, rpc_task, pipeline_task, p2p_task) = + inspect_and_log_nested_errors!(upload_result, rpc_task, pipeline_task, p2p_task); // Exit with error upload_result??; From c9057a922a23c33cc522984ff9eade8886b4858d Mon Sep 17 00:00:00 2001 From: aidan46 Date: Wed, 15 Jan 2025 18:27:38 +0800 Subject: [PATCH 25/34] fix: Remove task spawning macro --- storage-provider/server/src/main.rs | 33 ++++++++++++----------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/storage-provider/server/src/main.rs b/storage-provider/server/src/main.rs index 576c08346..f9401f8fa 100644 --- a/storage-provider/server/src/main.rs +++ b/storage-provider/server/src/main.rs @@ -195,17 +195,6 @@ macro_rules! inspect_and_log_nested_errors { }; } -/// This macro spawns multiple async tasks and returns the JoinHandle. -macro_rules! spawn_async_tasks { - ($($task:expr),+ $(,)?) => { - ( - $( - tokio::spawn($task) - ),+ - ) - } -} - /// The server arguments, as passed by the user, unvalidated. #[derive(Debug, Parser)] #[command(author, version, about, long_about = None, arg_required_else_help = true)] @@ -374,15 +363,19 @@ impl Server { let cancellation_token = CancellationToken::new(); let p2p_task = spawn_p2p_task(p2p_state, cancellation_token.child_token())?; - let (rpc_task, storage_task, pipeline_task) = spawn_async_tasks!( - start_rpc_server(rpc_state, cancellation_token.child_token(),), - start_upload_server(Arc::new(storage_state), cancellation_token.child_token(),), - start_pipeline( - Arc::new(pipeline_state), - pipeline_rx, - cancellation_token.child_token(), - ) - ); + let rpc_task = tokio::spawn(start_rpc_server( + rpc_state, + cancellation_token.child_token(), + )); + let storage_task = tokio::spawn(start_upload_server( + Arc::new(storage_state), + cancellation_token.child_token(), + )); + let pipeline_task = tokio::spawn(start_pipeline( + Arc::new(pipeline_state), + pipeline_rx, + cancellation_token.child_token(), + )); // Wait for SIGTERM on the main thread and once received "unblock" tokio::signal::ctrl_c() From 2528fcea432c0c179aa13f07e37183264b07adb6 Mon Sep 17 00:00:00 2001 From: aidan46 Date: Wed, 15 Jan 2025 18:36:56 +0800 Subject: [PATCH 26/34] fix: Remove unused import --- storage-provider/server/src/main.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/storage-provider/server/src/main.rs b/storage-provider/server/src/main.rs index f9401f8fa..4274b1c07 100644 --- a/storage-provider/server/src/main.rs +++ b/storage-provider/server/src/main.rs @@ -12,7 +12,6 @@ mod storage; use std::{env::temp_dir, net::SocketAddr, path::PathBuf, sync::Arc, time::Duration}; use clap::Parser; -use futures::Future; use libp2p::{identity::Keypair, Multiaddr, PeerId}; use p2p::{ run_bootstrap_node, run_register_node, BootstrapConfig, NodeType, P2PError, P2PState, From f40c851470304b505423980ac4ef297c55c53d06 Mon Sep 17 00:00:00 2001 From: aidan46 Date: Wed, 15 Jan 2025 18:46:22 +0800 Subject: [PATCH 27/34] fix: Cargo fmt --- storage-provider/server/src/p2p/bootstrap.rs | 8 ++++++-- storage-provider/server/src/p2p/register.rs | 9 +++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/storage-provider/server/src/p2p/bootstrap.rs b/storage-provider/server/src/p2p/bootstrap.rs index bdcd655d7..ebc8cc146 100644 --- a/storage-provider/server/src/p2p/bootstrap.rs +++ b/storage-provider/server/src/p2p/bootstrap.rs @@ -1,8 +1,12 @@ use std::time::Duration; use libp2p::{ - futures::StreamExt, identify, identity::Keypair, noise, rendezvous, swarm::NetworkBehaviour, - swarm::SwarmEvent, tcp, yamux, Multiaddr, Swarm, SwarmBuilder, + futures::StreamExt, + identify, + identity::Keypair, + noise, rendezvous, + swarm::{NetworkBehaviour, SwarmEvent}, + tcp, yamux, Multiaddr, Swarm, SwarmBuilder, }; use super::P2PError; diff --git a/storage-provider/server/src/p2p/register.rs b/storage-provider/server/src/p2p/register.rs index 070755887..c5dc986d3 100644 --- a/storage-provider/server/src/p2p/register.rs +++ b/storage-provider/server/src/p2p/register.rs @@ -1,8 +1,13 @@ use std::time::Duration; use libp2p::{ - futures::StreamExt, identify, identity::Keypair, noise, rendezvous, rendezvous::Namespace, - swarm::NetworkBehaviour, swarm::SwarmEvent, tcp, yamux, Multiaddr, PeerId, Swarm, SwarmBuilder, + futures::StreamExt, + identify, + identity::Keypair, + noise, + rendezvous::{self, Namespace}, + swarm::{NetworkBehaviour, SwarmEvent}, + tcp, yamux, Multiaddr, PeerId, Swarm, SwarmBuilder, }; use super::P2PError; From 2f50126f62fc91036a0066720f5e9953828c1479 Mon Sep 17 00:00:00 2001 From: aidan46 Date: Wed, 15 Jan 2025 19:23:23 +0800 Subject: [PATCH 28/34] fix: Remove unused configs --- examples/p2p-config/bootstrap.toml | 2 -- examples/p2p-config/register.toml | 3 --- examples/p2p-keys/private.pem | 3 --- examples/p2p-keys/public.pem | 3 --- 4 files changed, 11 deletions(-) delete mode 100644 examples/p2p-config/bootstrap.toml delete mode 100644 examples/p2p-config/register.toml delete mode 100644 examples/p2p-keys/private.pem delete mode 100644 examples/p2p-keys/public.pem diff --git a/examples/p2p-config/bootstrap.toml b/examples/p2p-config/bootstrap.toml deleted file mode 100644 index 7ba97d23d..000000000 --- a/examples/p2p-config/bootstrap.toml +++ /dev/null @@ -1,2 +0,0 @@ -address = "/ip4/127.0.0.1/tcp/62649" -keypair = "@examples/p2p-keys/private.pem" diff --git a/examples/p2p-config/register.toml b/examples/p2p-config/register.toml deleted file mode 100644 index 500c3fe5c..000000000 --- a/examples/p2p-config/register.toml +++ /dev/null @@ -1,3 +0,0 @@ -keypair = "@examples/p2p-keys/private.pem" -rendezvous_point = "12D3KooWS8tjUoiMgh2hZ12EyDjnJuEoa1zxq7G7EQtqafRUT8fr" -rendezvous_point_address = "/ip4/127.0.0.1/tcp/62649" diff --git a/examples/p2p-keys/private.pem b/examples/p2p-keys/private.pem deleted file mode 100644 index 6fbe8aad3..000000000 --- a/examples/p2p-keys/private.pem +++ /dev/null @@ -1,3 +0,0 @@ ------BEGIN PRIVATE KEY----- -MC4CAQAwBQYDK2VwBCIEIKo5QJZ40QKfNdrdMyjJeYagahGMgT7ibZsIuUeC3Yrw ------END PRIVATE KEY----- \ No newline at end of file diff --git a/examples/p2p-keys/public.pem b/examples/p2p-keys/public.pem deleted file mode 100644 index 139ecf733..000000000 --- a/examples/p2p-keys/public.pem +++ /dev/null @@ -1,3 +0,0 @@ ------BEGIN PUBLIC KEY----- -MCowBQYDK2VwAyEA8noBMkYU3PmEVHWpu78JrIoeKjZcC8jX1SgRI8ZBPok= ------END PUBLIC KEY----- From dae95d0c71e0f8f5a1ce27521424a4322ff57284 Mon Sep 17 00:00:00 2001 From: aidan46 Date: Wed, 15 Jan 2025 19:24:28 +0800 Subject: [PATCH 29/34] docs: Improve macro comment --- storage-provider/server/src/main.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/storage-provider/server/src/main.rs b/storage-provider/server/src/main.rs index 4274b1c07..315ff75b9 100644 --- a/storage-provider/server/src/main.rs +++ b/storage-provider/server/src/main.rs @@ -178,8 +178,16 @@ pub enum ServerError { P2P(#[from] P2PError), } -/// Takes an expression that returns a nested result and -/// inspected the error while logging it. +/// Takes an expression that returns a `Result, E1>`. +/// It tries to inspect and log the first error (`E1`), otherwise, +/// it inspects the result and tries to inspect the nested error (`E2`). +/// +/// This macro is *roughly* equivalent to calling: +/// ```text +/// res // : Result, E1> +/// .inspect_err(|e| tracing::error!(%e)) +/// .inspect(|r| r.inspect_err(|e| tracing::error!(%e)) +/// ``` macro_rules! inspect_and_log_nested_errors { ($($task:expr),+ $(,)?) => { ( From bf4295c3ef073a4c187dfc7fbfe18b49db79ff9a Mon Sep 17 00:00:00 2001 From: aidan46 Date: Wed, 15 Jan 2025 19:25:23 +0800 Subject: [PATCH 30/34] docs: Fix proof defaults in server docs --- docs/src/storage-provider-cli/server.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/storage-provider-cli/server.md b/docs/src/storage-provider-cli/server.md index df4af597c..e3febfde7 100644 --- a/docs/src/storage-provider-cli/server.md +++ b/docs/src/storage-provider-cli/server.md @@ -109,8 +109,8 @@ The supported configuration parameters are: | `node-url` | `ws://127.0.0.1:42069` | | `database-directory` | `/tmp//deals_database` | | `storage-directory` | `/tmp//deals_storage` | -| `seal-proof` | 2KiB | -| `post-proof` | 2KiB | +| `seal-proof` | `2KiB` | +| `post-proof` | `2KiB` | | `porep_parameters` | NA | | `post_parameters` | NA | | `node_type` | `bootstrap` | From ceb74a6653e495dafc5ff0a48450b6b24245d0d1 Mon Sep 17 00:00:00 2001 From: aidan46 Date: Wed, 15 Jan 2025 19:32:30 +0800 Subject: [PATCH 31/34] fix: Move p2p.rs to mod.rs --- storage-provider/server/src/{p2p.rs => p2p/mod.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename storage-provider/server/src/{p2p.rs => p2p/mod.rs} (100%) diff --git a/storage-provider/server/src/p2p.rs b/storage-provider/server/src/p2p/mod.rs similarity index 100% rename from storage-provider/server/src/p2p.rs rename to storage-provider/server/src/p2p/mod.rs From 55379227fe833748e7a23448e3f039cfa94fb4c3 Mon Sep 17 00:00:00 2001 From: aidan46 Date: Wed, 15 Jan 2025 19:39:02 +0800 Subject: [PATCH 32/34] docs: Add docs to publish script --- examples/rpc_publish.sh | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/examples/rpc_publish.sh b/examples/rpc_publish.sh index 5e2437445..8f9f2992f 100755 --- a/examples/rpc_publish.sh +++ b/examples/rpc_publish.sh @@ -21,19 +21,31 @@ PROVIDER="//Charlie" INPUT_FILE="$1" INPUT_FILE_NAME="$(basename "$INPUT_FILE")" +# CARv2 file location INPUT_TMP_FILE="/tmp/$INPUT_FILE_NAME.car" +# Config file location +CONFIG="/tmp/config.toml" +# P2P Node variables P2P_PUBLIC_KEY="/tmp/public.pem" P2P_PRIVATE_KEY="/tmp/private.pem" P2P_ADDRESS="/ip4/127.0.0.1/tcp/62649" -# Generate ED25519 private key +# Generate ED25519 private key to be replaced with a polka-storage-provider-client command +# TODO(@aidan46, 15/01, #676) openssl genpkey -algorithm ED25519 -out "$P2P_PRIVATE_KEY" -outpubkey "$P2P_PUBLIC_KEY" + +# Convert file to CARv2 format target/release/mater-cli convert -q --overwrite "$INPUT_FILE" "$INPUT_TMP_FILE" && + +# Calculate COMMP and set PIECE_CID and PIECE_SIZE INPUT_COMMP="$(target/release/polka-storage-provider-client proofs commp "$INPUT_TMP_FILE")" PIECE_CID="$(echo "$INPUT_COMMP" | jq -r ".cid")" PIECE_SIZE="$(echo "$INPUT_COMMP" | jq ".size")" + +# Generate Peer ID from public key PEER_ID="$(target/release/polka-storage-provider-client generate-peer-id --pubkey "$P2P_PUBLIC_KEY")" -CONFIG="/tmp/bootstrap.toml" + +# echo config file in the file in the /tmp folder echo "seal_proof = '2KiB' post_proof = '2KiB' porep_parameters = '2KiB.porep.params' From a31984b817e615ae0965d7d1c757107b739f7ffc Mon Sep 17 00:00:00 2001 From: aidan46 Date: Wed, 15 Jan 2025 19:41:37 +0800 Subject: [PATCH 33/34] fix: Use if let over match for early return --- storage-provider/server/src/main.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/storage-provider/server/src/main.rs b/storage-provider/server/src/main.rs index 315ff75b9..388d841af 100644 --- a/storage-provider/server/src/main.rs +++ b/storage-provider/server/src/main.rs @@ -560,16 +560,16 @@ fn spawn_p2p_task( BootstrapConfig::new(p2p_state.p2p_key, p2p_state.rendezvous_point_address); Ok(tokio::spawn(run_bootstrap_node(config, cancellation_token))) } - NodeType::Register => match p2p_state.rendezvous_point { - Some(rendezvous_point) => { - let config = RegisterConfig::new( - p2p_state.p2p_key, - p2p_state.rendezvous_point_address, - rendezvous_point, - ); - Ok(tokio::spawn(run_register_node(config, cancellation_token))) - } - None => return Err(ServerError::P2P(P2PError::InvalidBehaviourConfig)), - }, + NodeType::Register => { + let Some(rendezvous_point) = p2p_state.rendezvous_point else { + return Err(ServerError::P2P(P2PError::InvalidBehaviourConfig)); + }; + let config = RegisterConfig::new( + p2p_state.p2p_key, + p2p_state.rendezvous_point_address, + rendezvous_point, + ); + Ok(tokio::spawn(run_register_node(config, cancellation_token))) + } } } From 89f1fe163024500c6a2edb49f7aac58166406bd6 Mon Sep 17 00:00:00 2001 From: aidan46 Date: Wed, 15 Jan 2025 19:42:02 +0800 Subject: [PATCH 34/34] docs: Update PeerID comment for register command --- storagext/cli/src/cmd/storage_provider.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storagext/cli/src/cmd/storage_provider.rs b/storagext/cli/src/cmd/storage_provider.rs index 2bd139d06..a974dc08d 100644 --- a/storagext/cli/src/cmd/storage_provider.rs +++ b/storagext/cli/src/cmd/storage_provider.rs @@ -37,7 +37,7 @@ pub enum StorageProviderCommand { /// Register account as a Storage Provider, so it can perform duties in Storage Provider Pallet. #[command(name = "register")] RegisterStorageProvider { - /// PeerId in Storage Provider P2P network, can be any String. + /// PeerId in Storage Provider P2P network. peer_id: PeerId, /// Proof of Space Time type. /// Can only be "2KiB" meaning `RegisteredPoStProof::StackedDRGWindow2KiBV1P1`.