Skip to content

Commit

Permalink
feat(rewards): add bls pub key to node
Browse files Browse the repository at this point in the history
- This is the first step to simple dbc tx fees and rewards for Elders.
- It allows Elders to check if dbc transactions
contain any output for them.
  • Loading branch information
oetyng committed Mar 8, 2023
1 parent 350b07d commit a05fa20
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 56 deletions.
40 changes: 24 additions & 16 deletions sn_interface/src/types/keys/public_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,22 +59,7 @@ impl PublicKey {
///
/// It is often useful to parse such raw strings in user-facing apps like CLI.
pub fn bls_from_hex(hex: &str) -> Result<Self> {
let bytes = hex::decode(hex).map_err(|err| {
Error::FailedToParse(format!(
"Couldn't parse BLS public key bytes from hex: {err}",
))
})?;
let bytes_fixed_len: [u8; bls::PK_SIZE] = bytes.as_slice().try_into()
.map_err(|_| Error::FailedToParse(format!(
"Couldn't parse BLS public key bytes from hex. The provided string must represent exactly {} bytes.",
bls::PK_SIZE
)))?;
let pk = bls::PublicKey::from_bytes(bytes_fixed_len).map_err(|err| {
Error::FailedToParse(format!(
"Couldn't parse BLS public key from fixed-length byte array: {err}",
))
})?;
Ok(Self::from(pk))
Ok(Self::from(bls_from_hex(hex)?))
}

/// Returns the bytes of the underlying public key.
Expand Down Expand Up @@ -229,6 +214,29 @@ impl UpperHex for PublicKey {
}
}

/// Construct a BLS public key from a hex-encoded string.
///
/// It is often useful to parse such raw strings in user-facing apps like CLI.
pub fn bls_from_hex<T: AsRef<[u8]>>(hex: T) -> Result<bls::PublicKey> {
let bytes = hex::decode(hex).map_err(|err| {
Error::FailedToParse(format!(
"Couldn't parse BLS public key bytes from hex: {err}",
))
})?;
let bytes_fixed_len: [u8; bls::PK_SIZE] = bytes.as_slice().try_into()
.map_err(|_| Error::FailedToParse(format!(
"Couldn't parse BLS public key bytes from hex. The provided string must represent exactly {} bytes.",
bls::PK_SIZE
)))?;
let pk = bls::PublicKey::from_bytes(bytes_fixed_len).map_err(|err| {
Error::FailedToParse(format!(
"Couldn't parse BLS public key from fixed-length byte array: {err}",
))
})?;

Ok(pk)
}

#[cfg(test)]
mod tests {
use super::utils;
Expand Down
2 changes: 1 addition & 1 deletion sn_interface/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub use errors::{Error, Result};
pub use identities::{ClientId, NodeId, Participant};
pub use keys::{
keypair::{BlsKeypairShare, Encryption, Keypair, OwnerType, Signing},
public_key::PublicKey,
public_key::{bls_from_hex, PublicKey},
secret_key::SecretKey,
signature::{Signature, SignatureShare},
};
Expand Down
2 changes: 2 additions & 0 deletions sn_node/src/node/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ impl MyNode {
pub(crate) fn first_node(
comm: Comm,
keypair: Keypair,
reward_key: bls::PublicKey,
used_space: UsedSpace,
root_storage_dir: PathBuf,
genesis_sk_set: bls::SecretKeySet,
Expand All @@ -47,6 +48,7 @@ impl MyNode {
let node = Self::new(
comm,
Arc::new(keypair),
reward_key,
network_knowledge,
Some(section_key_share),
used_space,
Expand Down
48 changes: 18 additions & 30 deletions sn_node/src/node/cfg/keypair_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
// permissions and limitations relating to use of the SAFE Network Software.

use crate::node::{Error, Result};
use ed25519_dalek::{Keypair, PublicKey, KEYPAIR_LENGTH};

use sn_interface::types::bls_from_hex;

use ed25519_dalek::{Keypair, KEYPAIR_LENGTH};
use hex::{decode, encode};
use std::path::Path;
use tokio::fs;
Expand Down Expand Up @@ -59,38 +62,26 @@ pub(crate) async fn get_network_keypair(root_dir: &Path) -> Result<Option<Keypai
}

/// Writes the public and secret key (hex-encoded) to different locations at disk.
pub(crate) async fn store_new_reward_keypair(root_dir: &Path, keypair: &Keypair) -> Result<()> {
pub(crate) async fn store_new_reward_keypair(
root_dir: &Path,
secret_key: &bls::SecretKey,
) -> Result<()> {
let secret_key_path = root_dir.join(REWARD_SECRET_KEY_FILENAME);
let public_key_path = root_dir.join(REWARD_PUBLIC_KEY_FILENAME);
fs::write(secret_key_path, encode(keypair.secret.to_bytes())).await?;
fs::write(public_key_path, encode(keypair.public.to_bytes())).await?;

fs::write(secret_key_path, encode(secret_key.to_bytes())).await?;
fs::write(public_key_path, encode(secret_key.public_key().to_bytes())).await?;
Ok(())
}

/// Returns Some(PublicKey) or None if file doesn't exist. It assumes it's hex-encoded.
pub(crate) async fn get_reward_pk(root_dir: &Path) -> Result<Option<PublicKey>> {
/// Returns Some(bls::PublicKey) or None if file doesn't exist. It assumes it's hex-encoded.
pub(crate) async fn get_reward_pk(root_dir: &Path) -> Result<Option<bls::PublicKey>> {
let path = root_dir.join(REWARD_PUBLIC_KEY_FILENAME);
if !path.is_file() {
return Ok(None);
}

let pk_hex_bytes = fs::read(&path).await?;
let pk_bytes = decode(pk_hex_bytes).map_err(|err| {
Error::Configuration(format!(
"couldn't hex-decode rewards Ed25519 public key bytes from {}: {}",
path.display(),
err
))
})?;

let pk = PublicKey::from_bytes(&pk_bytes).map_err(|err| {
Error::Configuration(format!(
"invalid rewards Ed25519 public key bytes read from {}: {}",
path.display(),
err
))
})?;
let pk = bls_from_hex(pk_hex_bytes)?;

Ok(Some(pk))
}
Expand All @@ -105,21 +96,18 @@ mod test {
use tempfile::{tempdir, TempDir};

#[tokio::test]
async fn pubkey_to_and_from_file() -> Result<()> {
let mut rng = OsRng;
let keypair = ed25519_dalek::Keypair::generate(&mut rng);

async fn reward_key_to_and_from_file() -> Result<()> {
let secret_key = bls::SecretKey::random();
let root = create_temp_root()?;
let root_dir = root.path();
store_new_reward_keypair(root_dir, &keypair).await?;
store_new_reward_keypair(root_dir, &secret_key).await?;
let pk_result = get_reward_pk(root_dir).await?;

assert_eq!(pk_result, Some(keypair.public));
assert_eq!(pk_result, Some(secret_key.public_key()));
Ok(())
}

#[tokio::test]
async fn keypair_to_and_from_file() -> Result<()> {
async fn network_keypair_to_and_from_file() -> Result<()> {
let mut rng = OsRng;
let keypair = ed25519_dalek::Keypair::generate(&mut rng);

Expand Down
2 changes: 2 additions & 0 deletions sn_node/src/node/flow_ctrl/tests/network_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,7 @@ impl TestNetwork {
MyNode::new(
comm.clone(),
info.keypair.clone(),
bls::SecretKey::random().public_key(),
network_knowledge.clone(),
None,
UsedSpace::new(min_capacity, max_capacity),
Expand Down Expand Up @@ -832,6 +833,7 @@ impl TestNetwork {
let mut my_node = MyNode::new(
comm.clone(),
info.keypair.clone(),
bls::SecretKey::random().public_key(),
network_knowledge.clone(),
sk_share.clone(),
UsedSpace::new(min_capacity, max_capacity),
Expand Down
8 changes: 8 additions & 0 deletions sn_node/src/node/messaging/client_msgs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ impl MyNode {
spent_transactions: &BTreeSet<DbcTransaction>,
context: &NodeContext,
) -> Result<SpentProofShare> {
// verify that fee is paid (we are included as output)
MyNode::verify_fee(context.reward_key, tx)?;

// verify the spent proofs
MyNode::verify_spent_proofs(spent_proofs, &context.network_knowledge)?;

Expand Down Expand Up @@ -277,6 +280,11 @@ impl MyNode {
Ok(spent_proof_share)
}

fn verify_fee(_our_key: PublicKey, _tx: &DbcTransaction) -> Result<()> {
// TODO: check that we have an output to us, and that it is of sufficient value.
Ok(())
}

// Verify spent proof signatures are valid, and each spent proof is signed by a known section key.
fn verify_spent_proofs(
spent_proofs: &BTreeSet<SpentProof>,
Expand Down
6 changes: 6 additions & 0 deletions sn_node/src/node/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ mod core {
root_storage_dir: PathBuf,
pub(crate) data_storage: DataStorage, // Adult only before cache
pub(crate) keypair: Arc<Keypair>,
pub(crate) reward_key: PublicKey,
// Network resources
pub(crate) section_keys_provider: SectionKeysProvider,
pub(crate) network_knowledge: NetworkKnowledge,
Expand Down Expand Up @@ -129,6 +130,7 @@ mod core {
pub(crate) name: XorName,
pub(crate) info: MyNodeInfo,
pub(crate) keypair: Arc<Keypair>,
pub(crate) reward_key: PublicKey,
pub(crate) network_knowledge: NetworkKnowledge,
pub(crate) section_keys_provider: SectionKeysProvider,
#[debug(skip)]
Expand Down Expand Up @@ -185,6 +187,7 @@ mod core {
name: self.name(),
info: self.info(),
keypair: self.keypair.clone(),
reward_key: self.reward_key,
network_knowledge: self.network_knowledge().clone(),
section_keys_provider: self.section_keys_provider.clone(),
comm: self.comm.clone(),
Expand All @@ -196,9 +199,11 @@ mod core {
}
}

#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
comm: Comm,
keypair: Arc<Keypair>, //todo: Keypair, only test design blocks this
reward_key: PublicKey,
network_knowledge: NetworkKnowledge,
section_key_share: Option<SectionKeyShare>,
used_space: UsedSpace,
Expand Down Expand Up @@ -244,6 +249,7 @@ mod core {
comm,
addr,
keypair,
reward_key,
network_knowledge,
section_keys_provider,
root_storage_dir,
Expand Down
24 changes: 15 additions & 9 deletions sn_node/src/node/node_starter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ use crate::UsedSpace;
use sn_comms::Comm;
use sn_interface::{
network_knowledge::{NetworkKnowledge, SectionTree, MIN_ADULT_AGE},
types::{keys::ed25519, log_markers::LogMarker, PublicKey as TypesPublicKey},
types::{keys::ed25519, log_markers::LogMarker},
};

use rand_07::rngs::OsRng;
use std::{path::Path, sync::Arc, time::Duration};
use tokio::{
fs,
Expand Down Expand Up @@ -59,20 +58,20 @@ pub async fn new_node(
let root_dir = root_dir_buf.as_path();
fs::create_dir_all(root_dir).await?;

let _reward_key = match get_reward_pk(root_dir).await? {
Some(public_key) => TypesPublicKey::Ed25519(public_key),
let reward_key = match get_reward_pk(root_dir).await? {
Some(public_key) => public_key,
None => {
let mut rng = OsRng;
let keypair = ed25519_dalek::Keypair::generate(&mut rng);
store_new_reward_keypair(root_dir, &keypair).await?;
TypesPublicKey::Ed25519(keypair.public)
let secret_key = bls::SecretKey::random();
let public_key = secret_key.public_key();
store_new_reward_keypair(root_dir, &secret_key).await?;
public_key
}
};

let used_space = UsedSpace::new(config.min_capacity(), config.max_capacity());

let (cmd_channel, rejoin_network_rx) =
start_node(config, used_space, root_dir, join_retry_timeout).await?;
start_node(config, used_space, root_dir, reward_key, join_retry_timeout).await?;

Ok((cmd_channel, rejoin_network_rx))
}
Expand All @@ -82,6 +81,7 @@ async fn start_node(
config: &Config,
used_space: UsedSpace,
root_storage_dir: &Path,
reward_key: bls::PublicKey,
join_retry_timeout: Duration,
) -> Result<(CmdChannel, mpsc::Receiver<RejoinReason>)> {
let (fault_cmds_sender, fault_cmds_receiver) =
Expand All @@ -94,6 +94,7 @@ async fn start_node(
comm,
used_space,
root_storage_dir,
reward_key,
fault_cmds_sender.clone(),
)
.await?
Expand All @@ -103,6 +104,7 @@ async fn start_node(
comm,
used_space,
root_storage_dir,
reward_key,
fault_cmds_sender.clone(),
)
.await?
Expand Down Expand Up @@ -155,6 +157,7 @@ async fn start_genesis_node(
comm: Comm,
used_space: UsedSpace,
root_storage_dir: &Path,
reward_key: bls::PublicKey,
fault_cmds_sender: mpsc::Sender<FaultsCmd>,
) -> Result<MyNode> {
// Genesis node having a fix age of 255.
Expand All @@ -173,6 +176,7 @@ async fn start_genesis_node(
let (node, genesis_dbc) = MyNode::first_node(
comm,
keypair,
reward_key,
used_space.clone(),
root_storage_dir.to_path_buf(),
genesis_sk_set,
Expand Down Expand Up @@ -202,6 +206,7 @@ async fn start_normal_node(
comm: Comm,
used_space: UsedSpace,
root_storage_dir: &Path,
reward_key: bls::PublicKey,
fault_cmds_sender: mpsc::Sender<FaultsCmd>,
) -> Result<MyNode> {
let keypair = ed25519::gen_keypair(&Prefix::default().range_inclusive(), MIN_ADULT_AGE);
Expand All @@ -225,6 +230,7 @@ async fn start_normal_node(
let node = MyNode::new(
comm,
Arc::new(keypair),
reward_key,
network_knowledge,
None,
used_space.clone(),
Expand Down

0 comments on commit a05fa20

Please sign in to comment.