Skip to content

Commit

Permalink
feat(storage-provider-server): add p2p bootstrap and registration (#666)
Browse files Browse the repository at this point in the history
  • Loading branch information
aidan46 authored Jan 16, 2025
1 parent 498840d commit cba074e
Show file tree
Hide file tree
Showing 22 changed files with 1,261 additions and 126 deletions.
655 changes: 590 additions & 65 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 }
Expand Down
44 changes: 33 additions & 11 deletions docs/src/storage-provider-cli/server.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,28 +81,50 @@ 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/<random>/deals_database` |
| `storage-directory` | `/tmp/<random>/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/<random>/deals_database` |
| `storage-directory` | `/tmp/<random>/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

```json
{
"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",
}
```
31 changes: 28 additions & 3 deletions examples/rpc_publish.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,38 @@ 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 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")"

# echo config file in the file in the /tmp folder
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
target/release/storagext-cli --sr25519-key "$CLIENT" market add-balance 250000000000 &
Expand All @@ -39,7 +64,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 &

Expand All @@ -64,7 +89,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")"
Expand Down
15 changes: 13 additions & 2 deletions examples/start_sp.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 &
Expand All @@ -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
Expand All @@ -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" \
Expand Down
4 changes: 2 additions & 2 deletions pallets/market/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -119,7 +119,7 @@ impl pallet_storage_provider::Config for Test {
type RuntimeEvent = RuntimeEvent;
type Randomness = DummyRandomnessGenerator<Self>;
type AuthorVrfHistory = DummyRandomnessGenerator<Self>;
type PeerId = BoundedVec<u8, ConstU32<32>>; // Max length of SHA256 hash
type PeerId = BoundedVec<u8, ConstU32<PEER_ID_MAX_BYTES>>; // https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#peer-ids
type Currency = Balances;
type Market = Market;
type ProofVerification = Proofs;
Expand Down
4 changes: 2 additions & 2 deletions pallets/storage-provider/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -198,7 +198,7 @@ impl pallet_storage_provider::Config for Test {
type RuntimeEvent = RuntimeEvent;
type Randomness = DummyRandomnessGenerator<Self>;
type AuthorVrfHistory = DummyRandomnessGenerator<Self>;
type PeerId = BoundedVec<u8, ConstU32<32>>; // Max length of SHA256 hash
type PeerId = BoundedVec<u8, ConstU32<PEER_ID_MAX_BYTES>>; // https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#peer-ids
type Currency = Balances;
type Market = Market;
type ProofVerification = DummyProofsVerification;
Expand Down
6 changes: 6 additions & 0 deletions primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,9 @@ pub const MAX_SEAL_PROOF_BYTES: u32 = 1_920;
/// References:
/// * <https://github.com/filecoin-project/ref-fvm/blob/32583cc05aa422c8e1e7ba81d56a888ac9d90e61/shared/src/sector/registered_proof.rs#L159>
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;
3 changes: 2 additions & 1 deletion runtime/src/configs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<u8, ConstU32<32>>; // Max length of SHA256 hash
type PeerId = BoundedVec<u8, ConstU32<PEER_ID_MAX_BYTES>>; // 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;
Expand Down
2 changes: 2 additions & 0 deletions storage-provider/client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
28 changes: 28 additions & 0 deletions storage-provider/client/src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
mod proofs;
mod wallet;

use std::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,
Expand Down Expand Up @@ -47,6 +51,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
Expand Down Expand Up @@ -98,6 +108,13 @@ 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,
},
}

impl Cli {
Expand Down Expand Up @@ -131,6 +148,7 @@ impl Cli {
deal_proposal,
signer_key,
} => Self::sign_deal(deal_proposal, signer_key),
Self::GeneratePeerID { pubkey } => Self::generate_peer_id(pubkey),
}
}

Expand Down Expand Up @@ -182,4 +200,14 @@ impl Cli {
);
Ok(())
}

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);

println!("{peer_id}");
Ok(())
}
}
2 changes: 2 additions & 0 deletions storage-provider/server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
31 changes: 28 additions & 3 deletions storage-provider/server/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -98,4 +102,25 @@ 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.
#[serde(default = "NodeType::default")]
#[arg(long, default_value_t = NodeType::Bootstrap)]
pub(crate) node_type: NodeType,

/// P2P ED25519 private key
#[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
/// 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.
#[serde(default, deserialize_with = "string_to_peer_id_option")]
#[arg(long)]
pub(crate) rendezvous_point: Option<PeerId>,
}
Loading

0 comments on commit cba074e

Please sign in to comment.