Skip to content

Commit

Permalink
Merge pull request #2681 from grumbach/registers_on_top_of_graph_entry
Browse files Browse the repository at this point in the history
Registers on top of graph entry
  • Loading branch information
maqi authored Jan 31, 2025
2 parents adb8930 + 74a37fc commit 8c16ea4
Show file tree
Hide file tree
Showing 23 changed files with 847 additions and 148 deletions.
30 changes: 5 additions & 25 deletions ant-networking/src/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,13 @@
// 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::{driver::GetRecordCfg, Network, NetworkError, Result};
use ant_protocol::storage::{DataTypes, GraphEntry, GraphEntryAddress};
use crate::{NetworkError, Result};
use ant_protocol::storage::{DataTypes, GraphEntry};
use ant_protocol::{
storage::{try_deserialize_record, RecordHeader, RecordKind, RetryStrategy},
NetworkAddress, PrettyPrintRecordKey,
storage::{try_deserialize_record, RecordHeader, RecordKind},
PrettyPrintRecordKey,
};
use libp2p::kad::{Quorum, Record};

impl Network {
/// Gets GraphEntry at GraphEntryAddress from the Network.
pub async fn get_graph_entry(&self, address: GraphEntryAddress) -> Result<Vec<GraphEntry>> {
let key = NetworkAddress::from_graph_entry_address(address).to_record_key();
let get_cfg = GetRecordCfg {
get_quorum: Quorum::All,
retry_strategy: Some(RetryStrategy::Quick),
target_record: None,
expected_holders: Default::default(),
};
let record = self.get_record_from_network(key.clone(), &get_cfg).await?;
debug!(
"Got record from the network, {:?}",
PrettyPrintRecordKey::from(&record.key)
);

get_graph_entry_from_record(&record)
}
}
use libp2p::kad::Record;

pub fn get_graph_entry_from_record(record: &Record) -> Result<Vec<GraphEntry>> {
let header = RecordHeader::from_record(record)?;
Expand Down
4 changes: 2 additions & 2 deletions ant-networking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ impl Network {
continue;
};

if !pointer.verify() {
if !pointer.verify_signature() {
warn!("Rejecting Pointer for {pretty_key} PUT with invalid signature");
continue;
}
Expand All @@ -646,7 +646,7 @@ impl Network {
continue;
};

if !scratchpad.verify() {
if !scratchpad.verify_signature() {
warn!(
"Rejecting Scratchpad for {pretty_key} PUT with invalid signature"
);
Expand Down
10 changes: 6 additions & 4 deletions ant-node/src/put_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ impl Node {
}

// ensure data integrity
if !scratchpad.verify() {
if !scratchpad.verify_signature() {
warn!("Rejecting Scratchpad PUT with invalid signature");
return Err(Error::InvalidScratchpadSignature);
}
Expand Down Expand Up @@ -560,8 +560,10 @@ impl Node {
}

// verify the GraphEntries
let mut validated_entries: BTreeSet<GraphEntry> =
entries_for_key.into_iter().filter(|t| t.verify()).collect();
let mut validated_entries: BTreeSet<GraphEntry> = entries_for_key
.into_iter()
.filter(|t| t.verify_signature())
.collect();

// skip if none are valid
let addr = match validated_entries.first() {
Expand Down Expand Up @@ -785,7 +787,7 @@ impl Node {
payment: Option<ProofOfPayment>,
) -> Result<()> {
// Verify the pointer's signature
if !pointer.verify() {
if !pointer.verify_signature() {
warn!("Pointer signature verification failed");
return Err(Error::InvalidSignature);
}
Expand Down
15 changes: 5 additions & 10 deletions ant-node/tests/data_with_churn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,8 @@ fn create_graph_entry_task(
Ok(graph_entry) => {
println!("Fetched graph_entry at {addr:?}");

let Some((old_output, old_content)) = graph_entry.outputs.last() else {
let Some((old_output, old_content)) = graph_entry.descendants.last()
else {
println!("Can't get output from the graph_entry of {addr:?}");
error!("Can't get output from the graph_entry of {addr:?}");
break;
Expand All @@ -465,13 +466,8 @@ fn create_graph_entry_task(
};

let parents = vec![graph_entry.owner];
let graph_entry = GraphEntry::new(
owner.public_key(),
parents,
*old_content,
outputs,
owner,
);
let graph_entry =
GraphEntry::new(owner, parents, *old_content, outputs);

growing_history[index].push(graph_entry.address());

Expand Down Expand Up @@ -503,8 +499,7 @@ fn create_graph_entry_task(
let owner = SecretKey::random();
let content: [u8; 32] = rand::random();
let parents = vec![];
let graph_entry =
GraphEntry::new(owner.public_key(), parents, content, outputs, &owner);
let graph_entry = GraphEntry::new(&owner, parents, content, outputs);

growing_history.push(vec![graph_entry.address()]);
let _ = owners.insert(owner.public_key(), owner);
Expand Down
5 changes: 0 additions & 5 deletions ant-protocol/src/storage/chunks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,6 @@ impl Chunk {
self.address.xorname()
}

/// Returns size of contained value.
pub fn payload_size(&self) -> usize {
self.value.len()
}

/// Returns size of this chunk after serialisation.
pub fn serialised_size(&self) -> usize {
self.value.len()
Expand Down
47 changes: 31 additions & 16 deletions ant-protocol/src/storage/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,49 @@ pub use bls::{PublicKey, Signature};
pub type GraphContent = [u8; 32];

/// A generic GraphEntry on the Network
/// Graph entries are stored at the owner's public key. Note that there can only be one graph entry per owner.
/// Graph entries can be linked to other graph entries as parents or descendants.
/// Applications are free to define the meaning of these links, those are not enforced by the protocol.
/// The protocol only ensures that the graph entry is immutable once uploaded and that the signature is valid and matches the owner.
/// For convenience it is advised to make use of BLS key derivation to create multiple graph entries from a single key.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Ord, PartialOrd)]
pub struct GraphEntry {
/// The owner of the graph. Note that graph entries are stored at the owner's public key
pub owner: PublicKey,
/// Other graph entries that this graph entry refers to as parents
pub parents: Vec<PublicKey>,
/// The content of the graph entry
pub content: GraphContent,
pub outputs: Vec<(PublicKey, GraphContent)>,
/// Other graph entries that this graph entry refers to as descendants/outputs along with some data associated to each one
pub descendants: Vec<(PublicKey, GraphContent)>,
/// signs the above 4 fields with the owners key
pub signature: Signature,
}

impl GraphEntry {
/// Maximum size of a graph entry
pub const MAX_SIZE: usize = 1024;
/// Maximum size of a graph entry: 100KB
pub const MAX_SIZE: usize = 100 * 1024;

/// Create a new graph entry, signing it with the provided secret key.
pub fn new(
owner: PublicKey,
owner: &SecretKey,
parents: Vec<PublicKey>,
content: GraphContent,
outputs: Vec<(PublicKey, GraphContent)>,
signing_key: &SecretKey,
descendants: Vec<(PublicKey, GraphContent)>,
) -> Self {
let signature = signing_key.sign(Self::bytes_to_sign(&owner, &parents, &content, &outputs));
let key = owner;
let owner = key.public_key();
let signature = key.sign(Self::bytes_to_sign(
&owner,
&parents,
&content,
&descendants,
));
Self {
owner,
parents,
content,
outputs,
descendants,
signature,
}
}
Expand All @@ -54,14 +69,14 @@ impl GraphEntry {
owner: PublicKey,
parents: Vec<PublicKey>,
content: GraphContent,
outputs: Vec<(PublicKey, GraphContent)>,
descendants: Vec<(PublicKey, GraphContent)>,
signature: Signature,
) -> Self {
Self {
owner,
parents,
content,
outputs,
descendants,
signature,
}
}
Expand All @@ -71,7 +86,7 @@ impl GraphEntry {
owner: &PublicKey,
parents: &[PublicKey],
content: &[u8],
outputs: &[(PublicKey, GraphContent)],
descendants: &[(PublicKey, GraphContent)],
) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.extend_from_slice(&owner.to_bytes());
Expand All @@ -85,9 +100,9 @@ impl GraphEntry {
);
bytes.extend_from_slice("content".as_bytes());
bytes.extend_from_slice(content);
bytes.extend_from_slice("outputs".as_bytes());
bytes.extend_from_slice("descendants".as_bytes());
bytes.extend_from_slice(
&outputs
&descendants
.iter()
.flat_map(|(p, c)| [&p.to_bytes(), c.as_slice()].concat())
.collect::<Vec<_>>(),
Expand All @@ -101,11 +116,11 @@ impl GraphEntry {

/// Get the bytes that the signature is calculated from.
pub fn bytes_for_signature(&self) -> Vec<u8> {
Self::bytes_to_sign(&self.owner, &self.parents, &self.content, &self.outputs)
Self::bytes_to_sign(&self.owner, &self.parents, &self.content, &self.descendants)
}

/// Verify the signature of the graph entry
pub fn verify(&self) -> bool {
pub fn verify_signature(&self) -> bool {
self.owner
.verify(&self.signature, self.bytes_for_signature())
}
Expand All @@ -114,7 +129,7 @@ impl GraphEntry {
pub fn size(&self) -> usize {
size_of::<GraphEntry>()
+ self
.outputs
.descendants
.iter()
.map(|(p, c)| p.to_bytes().len() + c.len())
.sum::<usize>()
Expand Down
6 changes: 3 additions & 3 deletions ant-protocol/src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ mod address;
mod chunks;
mod graph;
mod header;
pub mod pointer;
pub use pointer::{Pointer, PointerTarget};
mod pointer;
mod scratchpad;

use core::fmt;
Expand All @@ -21,11 +20,12 @@ use std::{num::NonZeroUsize, time::Duration};
pub use self::{
address::{ChunkAddress, GraphEntryAddress, PointerAddress, ScratchpadAddress},
chunks::Chunk,
graph::GraphEntry,
graph::{GraphContent, GraphEntry},
header::{
try_deserialize_record, try_serialize_record, DataTypes, RecordHeader, RecordKind,
ValidationType,
},
pointer::{Pointer, PointerTarget},
scratchpad::Scratchpad,
};

Expand Down
33 changes: 11 additions & 22 deletions ant-protocol/src/storage/pointer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,21 @@
// 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 bls::{PublicKey, SecretKey, Signature};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use xor_name::XorName;

#[derive(Error, Debug)]
pub enum PointerError {
#[error("Failed to decode hex string: {0}")]
HexDecoding(#[from] FromHexError),
#[error("Failed to create public key: {0}")]
BlsError(#[from] BlsError),
#[error("Invalid public key bytes length")]
InvalidPublicKeyLength,
#[error("Invalid signature")]
InvalidSignature,
#[error("Serialization error: {0}")]
SerializationError(String),
}

/// Pointer, a mutable address pointing to other data on the Network
/// It is stored at the owner's public key and can only be updated by the owner
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Ord, PartialOrd)]
pub struct Pointer {
owner: PublicKey,
counter: u32,
target: PointerTarget,
signature: Signature,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Ord, PartialOrd)]
pub enum PointerTarget {
ChunkAddress(ChunkAddress),
GraphEntryAddress(GraphEntryAddress),
Expand Down Expand Up @@ -107,6 +91,11 @@ impl Pointer {
PointerAddress::from_owner(self.owner)
}

/// Get the target of the pointer
pub fn target(&self) -> &PointerTarget {
&self.target
}

/// 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 @@ -128,7 +117,7 @@ impl Pointer {
}

/// Verifies if the pointer has a valid signature
pub fn verify(&self) -> bool {
pub fn verify_signature(&self) -> bool {
let bytes = self.bytes_for_signature();
self.owner.verify(&self.signature, &bytes)
}
Expand All @@ -154,13 +143,13 @@ mod tests {

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

// Create pointer with wrong signature
let wrong_sk = SecretKey::random();
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
assert!(!wrong_pointer.verify_signature()); // Should be invalid with wrong signature
}
}
8 changes: 4 additions & 4 deletions ant-protocol/src/storage/scratchpad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,11 @@ impl Scratchpad {
self.counter,
);
self.signature = sk.sign(&bytes_to_sign);
debug_assert!(self.verify(), "Must be valid after being signed. This is a bug, please report it by opening an issue on our github");
debug_assert!(self.verify_signature(), "Must be valid after being signed. This is a bug, please report it by opening an issue on our github");
}

/// Verifies that the Scratchpad signature is valid
pub fn verify(&self) -> bool {
pub fn verify_signature(&self) -> bool {
let signing_bytes = Self::bytes_for_signature(
self.address,
self.data_encoding,
Expand Down Expand Up @@ -201,13 +201,13 @@ mod tests {
let sk = SecretKey::random();
let raw_data = Bytes::from_static(b"data to be encrypted");
let mut scratchpad = Scratchpad::new(&sk, 42, &raw_data, 0);
assert!(scratchpad.verify());
assert!(scratchpad.verify_signature());
assert_eq!(scratchpad.counter(), 0);
assert_ne!(scratchpad.encrypted_data(), &raw_data);

let raw_data2 = Bytes::from_static(b"data to be encrypted v2");
scratchpad.update(&raw_data2, &sk);
assert!(scratchpad.verify());
assert!(scratchpad.verify_signature());
assert_eq!(scratchpad.counter(), 1);
assert_ne!(scratchpad.encrypted_data(), &raw_data);
assert_ne!(scratchpad.encrypted_data(), &raw_data2);
Expand Down
Loading

0 comments on commit 8c16ea4

Please sign in to comment.