Skip to content

Commit

Permalink
Merge pull request #2658 from grumbach/working_pointers_and_bls_key_d…
Browse files Browse the repository at this point in the history
…erivation_tools

feat: actually working pointers, key derivation, various fixes
  • Loading branch information
grumbach authored Jan 24, 2025
2 parents 8ad5a4f + 1886c9b commit bfd62d2
Show file tree
Hide file tree
Showing 17 changed files with 852 additions and 220 deletions.
2 changes: 1 addition & 1 deletion ant-networking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ impl Network {
}

if let Some(old) = &valid_pointer {
if old.count() >= pointer.count() {
if old.counter() >= pointer.counter() {
info!("Rejecting Pointer for {pretty_key} with lower count than the previous one");
continue;
}
Expand Down
126 changes: 104 additions & 22 deletions ant-node/src/put_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use ant_protocol::storage::GraphEntry;
use ant_protocol::{
storage::{
try_deserialize_record, try_serialize_record, Chunk, DataTypes, GraphEntryAddress, Pointer,
RecordHeader, RecordKind, Scratchpad, ValidationType,
PointerAddress, RecordHeader, RecordKind, Scratchpad, ValidationType,
},
NetworkAddress, PrettyPrintRecordKey,
};
Expand Down Expand Up @@ -241,28 +241,44 @@ impl Node {
res
}
RecordKind::DataOnly(DataTypes::Pointer) => {
// Pointers should always be paid for
error!("Pointer should not be validated at this point");
Err(Error::InvalidPutWithoutPayment(
PrettyPrintRecordKey::from(&record.key).into_owned(),
))
let pointer = try_deserialize_record::<Pointer>(&record)?;
let net_addr = NetworkAddress::from_pointer_address(pointer.network_address());
let pretty_key = PrettyPrintRecordKey::from(&record.key);
let already_exists = self
.validate_key_and_existence(&net_addr, &record.key)
.await?;

if !already_exists {
warn!("Pointer at address: {:?}, key: {:?} does not exist locally, rejecting PUT without payment", pointer.network_address(), pretty_key);
return Err(Error::InvalidPutWithoutPayment(
PrettyPrintRecordKey::from(&record.key).into_owned(),
));
}

let res = self
.validate_and_store_pointer_record(pointer, record.key.clone(), true, None)
.await;
if res.is_ok() {
let content_hash = XorName::from_content(&record.value);
Marker::ValidPointerPutFromClient(&pretty_key).log();

// Notify replication_fetcher to mark the attempt as completed.
self.network().notify_fetch_completed(
record.key.clone(),
ValidationType::NonChunk(content_hash),
);
}
res
}
RecordKind::DataWithPayment(DataTypes::Pointer) => {
let (payment, pointer) =
try_deserialize_record::<(ProofOfPayment, Pointer)>(&record)?;

// check if the deserialized value's PointerAddress matches the record's key
let net_addr = NetworkAddress::from_pointer_address(pointer.network_address());
let key = net_addr.to_record_key();
let pretty_key = PrettyPrintRecordKey::from(&key);
if record.key != key {
warn!(
"Record's key {pretty_key:?} does not match with the value's PointerAddress, ignoring PUT."
);
return Err(Error::RecordKeyMismatch);
}

let already_exists = self.validate_key_and_existence(&net_addr, &key).await?;
let pretty_key = PrettyPrintRecordKey::from(&record.key);
let already_exists = self
.validate_key_and_existence(&net_addr, &record.key)
.await?;

// The pointer may already exist during the replication.
// The payment shall get deposit to self even if the pointer already exists.
Expand All @@ -278,11 +294,17 @@ impl Node {
}
}

let res = self.validate_and_store_pointer_record(pointer, key, true, Some(payment));
let res = self
.validate_and_store_pointer_record(
pointer,
record.key.clone(),
true,
Some(payment),
)
.await;
if res.is_ok() {
let content_hash = XorName::from_content(&record.value);
Marker::ValidPointerPutFromClient(&PrettyPrintRecordKey::from(&record.key))
.log();
Marker::ValidPointerPutFromClient(&pretty_key).log();

// Notify replication_fetcher to mark the attempt as completed.
self.network().notify_fetch_completed(
Expand Down Expand Up @@ -340,6 +362,7 @@ impl Node {
let pointer = try_deserialize_record::<Pointer>(&record)?;
let key = record.key.clone();
self.validate_and_store_pointer_record(pointer, key, false, None)
.await
}
}
}
Expand Down Expand Up @@ -675,8 +698,50 @@ impl Node {
Ok(local_entries)
}

/// Get the local Pointer for the provided `PointerAddress`
/// This only fetches the Pointer from the local store and does not perform any network operations.
/// If the local Pointer is not present or corrupted, returns `None`.
async fn get_local_pointer(&self, addr: PointerAddress) -> Option<Pointer> {
// get the local Pointer
let record_key = NetworkAddress::from_pointer_address(addr).to_record_key();
debug!("Checking for local Pointer with key: {record_key:?}");
let local_record = match self.network().get_local_record(&record_key).await {
Ok(Some(r)) => r,
Ok(None) => {
debug!("Pointer is not present locally: {record_key:?}");
return None;
}
Err(e) => {
error!("Failed to get Pointer record at {addr:?}: {e}");
return None;
}
};

// deserialize the record and get the Pointer
let local_header = match RecordHeader::from_record(&local_record) {
Ok(h) => h,
Err(_) => {
error!("Failed to deserialize Pointer record at {addr:?}");
return None;
}
};
let record_kind = local_header.kind;
if !matches!(record_kind, RecordKind::DataOnly(DataTypes::Pointer)) {
error!("Found a {record_kind} when expecting to find Pointer at {addr:?}");
return None;
}
let local_pointer: Pointer = match try_deserialize_record(&local_record) {
Ok(p) => p,
Err(_) => {
error!("Failed to deserialize Pointer record at {addr:?}");
return None;
}
};
Some(local_pointer)
}

/// Validate and store a pointer record
pub(crate) fn validate_and_store_pointer_record(
pub(crate) async fn validate_and_store_pointer_record(
&self,
pointer: Pointer,
key: RecordKey,
Expand All @@ -696,6 +761,18 @@ impl Node {
return Err(Error::RecordKeyMismatch);
}

// Keep the pointer with the highest counter
if let Some(local_pointer) = self.get_local_pointer(pointer.network_address()).await {
if pointer.counter() <= local_pointer.counter() {
info!(
"Ignoring Pointer PUT at {key:?} with counter less than or equal to the current counter ({} <= {})",
pointer.counter(),
local_pointer.counter()
);
return Ok(());
}
}

// Store the pointer
let record = Record {
key: key.clone(),
Expand All @@ -708,9 +785,14 @@ impl Node {

if is_client_put {
let content_hash = XorName::from_content(&record.value);
self.replicate_valid_fresh_record(key, ValidationType::NonChunk(content_hash), payment);
self.replicate_valid_fresh_record(
key.clone(),
ValidationType::NonChunk(content_hash),
payment,
);
}

info!("Successfully stored Pointer record at {key:?}");
Ok(())
}
}
14 changes: 14 additions & 0 deletions ant-protocol/src/storage/address/pointer_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,17 @@ impl std::fmt::Debug for PointerAddress {
write!(f, "PointerAddress({})", &self.to_hex()[0..6])
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_pointer_serialization() {
let key = bls::SecretKey::random();
let pointer_address = PointerAddress::from_owner(key.public_key());
let serialized = pointer_address.to_bytes();
let deserialized = PointerAddress::from_bytes(&serialized).unwrap();
assert_eq!(pointer_address, deserialized);
}
}
105 changes: 28 additions & 77 deletions ant-protocol/src/storage/pointer.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
// Copyright 2024 MaidSafe.net limited.
//
// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3.
// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. Please review the Licences for the specific language governing
// permissions and limitations relating to use of the SAFE Network Software.

use crate::storage::{ChunkAddress, GraphEntryAddress, PointerAddress, ScratchpadAddress};
use bls::{Error as BlsError, PublicKey, SecretKey, Signature};
use hex::FromHexError;
use rand::thread_rng;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use xor_name::XorName;
Expand Down Expand Up @@ -49,17 +56,15 @@ impl PointerTarget {

impl Pointer {
/// Create a new pointer, signing it with the provided secret key.
pub fn new(
owner: PublicKey,
counter: u32,
target: PointerTarget,
signing_key: &SecretKey,
) -> Self {
let bytes_to_sign = Self::bytes_to_sign(&owner, counter, &target);
let signature = signing_key.sign(&bytes_to_sign);
/// This pointer would be stored on the network at the provided key's public key
/// There can only be one pointer at a time at the same address (one per key)
pub fn new(owner: &SecretKey, counter: u32, target: PointerTarget) -> Self {
let pubkey = owner.public_key();
let bytes_to_sign = Self::bytes_to_sign(&pubkey, counter, &target);
let signature = owner.sign(&bytes_to_sign);

Self {
owner,
owner: pubkey,
counter,
target,
signature,
Expand Down Expand Up @@ -95,6 +100,11 @@ impl Pointer {
bytes
}

/// Get the address of the pointer
pub fn address(&self) -> PointerAddress {
PointerAddress::from_owner(self.owner)
}

/// Get the bytes that were signed for this pointer
pub fn bytes_for_signature(&self) -> Vec<u8> {
Self::bytes_to_sign(&self.owner, self.counter, &self.target)
Expand All @@ -104,7 +114,9 @@ impl Pointer {
self.target.xorname()
}

pub fn count(&self) -> u32 {
/// Get the counter of the pointer, the higher the counter, the more recent the pointer is
/// Similarly to counter CRDTs only the latest version (highest counter) of the pointer is kept on the network
pub fn counter(&self) -> u32 {
self.counter
}

Expand All @@ -118,91 +130,30 @@ impl Pointer {
let bytes = self.bytes_for_signature();
self.owner.verify(&self.signature, &bytes)
}

pub fn encode_hex(&self) -> String {
hex::encode(self.owner.to_bytes())
}

pub fn decode_hex(hex_str: &str) -> Result<Self, PointerError> {
let bytes = hex::decode(hex_str)?;
if bytes.len() != 48 {
return Err(PointerError::InvalidPublicKeyLength);
}
let mut bytes_array = [0u8; 48];
bytes_array.copy_from_slice(&bytes);

let owner = PublicKey::from_bytes(bytes_array).map_err(PointerError::BlsError)?;

let mut rng = thread_rng();
let target = PointerTarget::ChunkAddress(ChunkAddress::new(XorName::random(&mut rng)));

// Create a temporary secret key just for hex decoding test purposes
let sk = SecretKey::random();
Ok(Self::new(owner, 0, target, &sk))
}
}

#[cfg(test)]
mod tests {
use super::*;
use rand::thread_rng;

#[test]
fn test_pointer_creation_and_validation() {
let owner_sk = SecretKey::random();
let owner_pk = owner_sk.public_key();
let counter = 1;
let mut rng = thread_rng();
let target =
PointerTarget::GraphEntryAddress(GraphEntryAddress::new(XorName::random(&mut rng)));

// Create and sign pointer
let pointer = Pointer::new(owner_pk, counter, target.clone(), &owner_sk);
let pointer = Pointer::new(&owner_sk, counter, target.clone());
assert!(pointer.verify()); // Should be valid with correct signature

// Create pointer with wrong signature
let wrong_sk = SecretKey::random();
let wrong_pointer = Pointer::new(owner_pk, counter, target.clone(), &wrong_sk);
let sig = wrong_sk.sign(pointer.bytes_for_signature());
let wrong_pointer =
Pointer::new_with_signature(owner_sk.public_key(), counter, target.clone(), sig);
assert!(!wrong_pointer.verify()); // Should be invalid with wrong signature
}

#[test]
fn test_pointer_xorname() {
let owner_sk = SecretKey::random();
let owner_pk = owner_sk.public_key();
let counter = 1;
let mut rng = thread_rng();
let target =
PointerTarget::GraphEntryAddress(GraphEntryAddress::new(XorName::random(&mut rng)));

let pointer = Pointer::new(owner_pk, counter, target.clone(), &owner_sk);
let xorname = pointer.xorname();
assert_eq!(xorname, target.xorname());
}

#[test]
fn test_pointer_hex_encoding() {
let owner_sk = SecretKey::random();
let owner_pk = owner_sk.public_key();
let counter = 1;
let mut rng = thread_rng();
let target =
PointerTarget::GraphEntryAddress(GraphEntryAddress::new(XorName::random(&mut rng)));

let pointer = Pointer::new(owner_pk, counter, target, &owner_sk);
let hex = pointer.encode_hex();
let expected_hex = hex::encode(owner_pk.to_bytes());
assert_eq!(hex, expected_hex);
}

#[test]
fn test_pointer_hex_decoding() {
let owner_sk = SecretKey::random();
let owner_pk = owner_sk.public_key();
let hex = hex::encode(owner_pk.to_bytes());

let result = Pointer::decode_hex(&hex);
assert!(result.is_ok());
let pointer = result.unwrap();
assert_eq!(pointer.owner, owner_pk);
}
}
Loading

0 comments on commit bfd62d2

Please sign in to comment.