Skip to content

Commit

Permalink
feat: clean datatypes sizes and other improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
grumbach committed Jan 28, 2025
1 parent 946e4e9 commit 317b98b
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 43 deletions.
2 changes: 1 addition & 1 deletion ant-networking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,7 @@ impl Network {
continue;
};

if !scratchpad.is_valid() {
if !scratchpad.verify() {
warn!(
"Rejecting Scratchpad for {pretty_key} PUT with invalid signature"
);
Expand Down
9 changes: 6 additions & 3 deletions ant-node/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use thiserror::Error;

pub(super) type Result<T, E = Error> = std::result::Result<T, E>;

const SCRATCHPAD_MAX_SIZE: usize = ant_protocol::storage::Scratchpad::MAX_SIZE;

/// Internal error.
#[derive(Debug, Error)]
#[allow(missing_docs)]
Expand All @@ -38,12 +40,13 @@ pub enum Error {
#[error("The Record::key does not match with the key derived from Record::value")]
RecordKeyMismatch,

// Scratchpad is old version
// ------------ Scratchpad Errors
#[error("A newer version of this Scratchpad already exists")]
IgnoringOutdatedScratchpadPut,
// Scratchpad is invalid
#[error("Scratchpad signature is invalid over the counter + content hash")]
#[error("Scratchpad signature is invalid")]
InvalidScratchpadSignature,
#[error("Scratchpad too big: {0}, max size is {SCRATCHPAD_MAX_SIZE}")]
ScratchpadTooBig(usize),

#[error("Invalid signature")]
InvalidSignature,
Expand Down
8 changes: 7 additions & 1 deletion ant-node/src/put_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -467,11 +467,17 @@ impl Node {
}

// ensure data integrity
if !scratchpad.is_valid() {
if !scratchpad.verify() {
warn!("Rejecting Scratchpad PUT with invalid signature");
return Err(Error::InvalidScratchpadSignature);
}

// ensure the scratchpad is not too big
if scratchpad.is_too_big() {
warn!("Rejecting Scratchpad PUT with too big size");
return Err(Error::ScratchpadTooBig(scratchpad.size()));
}

info!(
"Storing sratchpad {addr:?} with content of {:?} as Record locally",
scratchpad.encrypted_data_hash()
Expand Down
24 changes: 24 additions & 0 deletions ant-protocol/src/storage/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ pub struct GraphEntry {
}

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

/// Create a new graph entry, signing it with the provided secret key.
pub fn new(
owner: PublicKey,
Expand Down Expand Up @@ -101,8 +104,29 @@ impl GraphEntry {
Self::bytes_to_sign(&self.owner, &self.parents, &self.content, &self.outputs)
}

/// Verify the signature of the graph entry
pub fn verify(&self) -> bool {
self.owner
.verify(&self.signature, self.bytes_for_signature())
}

/// Size of the graph entry
pub fn size(&self) -> usize {
size_of::<GraphEntry>()
+ self
.outputs
.iter()
.map(|(p, c)| p.to_bytes().len() + c.len())
.sum::<usize>()
+ self
.parents
.iter()
.map(|p| p.to_bytes().len())
.sum::<usize>()
}

/// Returns true if the graph entry is too big
pub fn is_too_big(&self) -> bool {
self.size() > Self::MAX_SIZE
}
}
5 changes: 5 additions & 0 deletions ant-protocol/src/storage/pointer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ impl Pointer {
let bytes = self.bytes_for_signature();
self.owner.verify(&self.signature, &bytes)
}

/// Size of the pointer
pub fn size() -> usize {
size_of::<Pointer>()
}
}

#[cfg(test)]
Expand Down
40 changes: 26 additions & 14 deletions ant-protocol/src/storage/scratchpad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,20 @@ pub struct Scratchpad {
address: ScratchpadAddress,
/// Data encoding: custom apps using scratchpad should use this so they can identify the type of data they are storing
data_encoding: u64,
/// Encrypted data stored in the scratchpad, it is encrypted automatically by the [`Scratchpad::new`] and [`Scratchpad::update_and_sign`] methods
/// Encrypted data stored in the scratchpad, it is encrypted automatically by the [`Scratchpad::new`] and [`Scratchpad::update`] methods
#[debug(skip)]
encrypted_data: Bytes,
/// Monotonically increasing counter to track the number of times this has been updated. When pushed to the network, the scratchpad with the highest counter is kept.
/// Monotonically increasing counter to track the number of times this has been updated.
/// When pushed to the network, the scratchpad with the highest counter is kept.
counter: u64,
/// Signature over the above fields
signature: Signature,
}

impl Scratchpad {
/// Max Scratchpad size is 4MB including the metadata
pub const MAX_SIZE: usize = 4 * 1024 * 1024;

/// Creates a new instance of `Scratchpad`. Encrypts the data, and signs all the elements.
pub fn new(
owner: &SecretKey,
Expand All @@ -51,15 +55,13 @@ impl Scratchpad {
&encrypted_data,
counter,
));
let s = Self {
Self {
address: addr,
encrypted_data,
data_encoding,
counter,
signature,
};
debug_assert!(s.is_valid(), "Must be valid after being signed. This is a bug, please report it by opening an issue on our github");
s
}
}

/// Create a new Scratchpad without provding the secret key
Expand Down Expand Up @@ -106,8 +108,8 @@ impl Scratchpad {
self.data_encoding
}

/// Updates the content, re-signs the scratchpad
pub fn update_and_sign(&mut self, unencrypted_data: &Bytes, sk: &SecretKey) {
/// Updates the content and encrypts it, increments the counter, re-signs the scratchpad
pub fn update(&mut self, unencrypted_data: &Bytes, sk: &SecretKey) {
self.counter += 1;
let pk = self.owner();
let address = ScratchpadAddress::new(*pk);
Expand All @@ -120,11 +122,11 @@ impl Scratchpad {
self.counter,
);
self.signature = sk.sign(&bytes_to_sign);
debug_assert!(self.is_valid(), "Must be valid after being signed. This is a bug, please report it by opening an issue on our github");
debug_assert!(self.verify(), "Must be valid after being signed. This is a bug, please report it by opening an issue on our github");
}

/// Verifies that the Scratchpad is valid
pub fn is_valid(&self) -> bool {
/// Verifies that the Scratchpad signature is valid
pub fn verify(&self) -> bool {
let signing_bytes = Self::bytes_for_signature(
self.address,
self.data_encoding,
Expand Down Expand Up @@ -178,6 +180,16 @@ impl Scratchpad {
pub fn payload_size(&self) -> usize {
self.encrypted_data.len()
}

/// Size of the scratchpad
pub fn size(&self) -> usize {
size_of::<Scratchpad>() + self.payload_size()
}

/// Returns true if the scratchpad is too big
pub fn is_too_big(&self) -> bool {
self.size() > Self::MAX_SIZE
}
}

#[cfg(test)]
Expand All @@ -189,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.is_valid());
assert!(scratchpad.verify());
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_and_sign(&raw_data2, &sk);
assert!(scratchpad.is_valid());
scratchpad.update(&raw_data2, &sk);
assert!(scratchpad.verify());
assert_eq!(scratchpad.counter(), 1);
assert_ne!(scratchpad.encrypted_data(), &raw_data);
assert_ne!(scratchpad.encrypted_data(), &raw_data2);
Expand Down
6 changes: 2 additions & 4 deletions autonomi/src/client/data_types/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,11 @@ impl Client {

// pay for the graph entry
let xor_name = address.xorname();
let graph_size_with_forks = size_of::<GraphEntry>() * 10;
debug!("Paying for graph entry at address: {address:?}");
let (payment_proofs, skipped_payments) = self
.pay_for_content_addrs(
DataTypes::GraphEntry,
std::iter::once((*xor_name, graph_size_with_forks)),
std::iter::once((*xor_name, entry.size())),
payment_option,
)
.await
Expand Down Expand Up @@ -150,11 +149,10 @@ impl Client {
trace!("Getting cost for GraphEntry of {key:?}");
let address = GraphEntryAddress::from_owner(key);
let xor = *address.xorname();
let graph_size_with_forks = size_of::<GraphEntry>() * 10;
let store_quote = self
.get_store_quotes(
DataTypes::GraphEntry,
std::iter::once((xor, graph_size_with_forks)),
std::iter::once((xor, GraphEntry::MAX_SIZE)),
)
.await?;
let total_cost = AttoTokens::from_atto(
Expand Down
7 changes: 2 additions & 5 deletions autonomi/src/client/data_types/pointer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ impl Client {
// TODO: define Pointer default size for pricing
.pay_for_content_addrs(
DataTypes::Pointer,
std::iter::once((xor_name, size_of::<Pointer>())),
std::iter::once((xor_name, Pointer::size())),
payment_option,
)
.await
Expand Down Expand Up @@ -275,10 +275,7 @@ impl Client {
let address = PointerAddress::from_owner(key);
let xor = *address.xorname();
let store_quote = self
.get_store_quotes(
DataTypes::Pointer,
std::iter::once((xor, size_of::<Pointer>())),
)
.get_store_quotes(DataTypes::Pointer, std::iter::once((xor, Pointer::size())))
.await?;
let total_cost = AttoTokens::from_atto(
store_quote
Expand Down
33 changes: 22 additions & 11 deletions autonomi/src/client/data_types/scratchpad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub use crate::Bytes;
pub use ant_protocol::storage::{Scratchpad, ScratchpadAddress};
pub use bls::{PublicKey, SecretKey, Signature};

const SCRATCHPAD_MAX_SIZE: usize = Scratchpad::MAX_SIZE;

/// Errors that can occur when dealing with Scratchpads
#[derive(Debug, thiserror::Error)]
pub enum ScratchpadError {
Expand All @@ -39,13 +41,12 @@ pub enum ScratchpadError {
ScratchpadAlreadyExists(ScratchpadAddress),
#[error("Scratchpad cannot be updated as it does not exist, please create it first or wait for it to be created")]
CannotUpdateNewScratchpad,
#[error("Scratchpad size is too big: {0} > {MAX_SCRATCHPAD_SIZE}")]
#[error("Scratchpad size is too big: {0} > {SCRATCHPAD_MAX_SIZE}")]
ScratchpadTooBig(usize),
#[error("Scratchpad signature is not valid")]
BadSignature,
}

/// Max Scratchpad size is 4MB including the metadata
pub const MAX_SCRATCHPAD_SIZE: usize = 4 * 1024 * 1024;

impl Client {
/// Get Scratchpad from the Network
/// It is stored at the owner's public key
Expand Down Expand Up @@ -126,25 +127,33 @@ impl Client {
Ok(pad)
}

/// Verify a scratchpad
pub fn scratchpad_verify(scratchpad: &Scratchpad) -> Result<(), ScratchpadError> {
if !scratchpad.verify() {
return Err(ScratchpadError::BadSignature);
}
if scratchpad.is_too_big() {
return Err(ScratchpadError::ScratchpadTooBig(scratchpad.size()));
}
Ok(())
}

/// Manually store a scratchpad on the network
pub async fn scratchpad_put(
&self,
scratchpad: Scratchpad,
payment_option: PaymentOption,
) -> Result<(AttoTokens, ScratchpadAddress), ScratchpadError> {
let address = scratchpad.address();
Self::scratchpad_verify(&scratchpad)?;

// pay for the scratchpad
let xor_name = address.xorname();
let size = size_of::<Scratchpad>() + scratchpad.payload_size();
if size > MAX_SCRATCHPAD_SIZE {
return Err(ScratchpadError::ScratchpadTooBig(size));
}
debug!("Paying for scratchpad at address: {address:?}");
let (payment_proofs, _skipped_payments) = self
.pay_for_content_addrs(
DataTypes::Scratchpad,
std::iter::once((xor_name, MAX_SCRATCHPAD_SIZE)),
std::iter::once((xor_name, scratchpad.size())),
payment_option,
)
.await
Expand Down Expand Up @@ -254,7 +263,6 @@ impl Client {
/// Update an existing scratchpad to the network
/// This operation is free but requires the scratchpad to be already created on the network
/// Only the latest version of the scratchpad is kept on the Network, previous versions will be overwritten and unrecoverable
/// The method [`Scratchpad::update_and_sign`] should be used before calling this function to send the scratchpad to the network
pub async fn scratchpad_update(
&self,
owner: &SecretKey,
Expand Down Expand Up @@ -286,6 +294,9 @@ impl Client {
return Err(ScratchpadError::CannotUpdateNewScratchpad);
};

// make sure the scratchpad is valid
Self::scratchpad_verify(&scratchpad)?;

// prepare the record to be stored
let record = Record {
key: NetworkAddress::from_scratchpad_address(address).to_record_key(),
Expand Down Expand Up @@ -328,7 +339,7 @@ impl Client {
let store_quote = self
.get_store_quotes(
DataTypes::Scratchpad,
std::iter::once((scratch_xor, size_of::<Scratchpad>())),
std::iter::once((scratch_xor, SCRATCHPAD_MAX_SIZE)),
)
.await?;

Expand Down
8 changes: 4 additions & 4 deletions autonomi/tests/scratchpad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ async fn scratchpad_put() -> Result<()> {
assert_eq!(got.data_encoding(), content_type);
assert_eq!(got.decrypt_data(&key), Ok(content.clone()));
assert_eq!(got.counter(), 0);
assert!(got.is_valid());
assert!(got.verify());
println!("scratchpad got 1");

// check that the content is decrypted correctly
Expand All @@ -132,7 +132,7 @@ async fn scratchpad_put() -> Result<()> {
assert_eq!(got.data_encoding(), content_type);
assert_eq!(got.decrypt_data(&key), Ok(content2.clone()));
assert_eq!(got.counter(), 1);
assert!(got.is_valid());
assert!(got.verify());
println!("scratchpad got 2");

// check that the content is decrypted correctly
Expand Down Expand Up @@ -175,7 +175,7 @@ async fn scratchpad_errors() -> Result<()> {
assert_eq!(got.data_encoding(), content_type);
assert_eq!(got.decrypt_data(&key), Ok(content.clone()));
assert_eq!(got.counter(), 0);
assert!(got.is_valid());
assert!(got.verify());
println!("scratchpad got 1");

// try create scratchpad at the same address
Expand All @@ -199,7 +199,7 @@ async fn scratchpad_errors() -> Result<()> {
assert_eq!(got.data_encoding(), content_type);
assert_eq!(got.decrypt_data(&key), Ok(content.clone()));
assert_eq!(got.counter(), 0);
assert!(got.is_valid());
assert!(got.verify());
println!("scratchpad got 1");

// check that the content is decrypted correctly and matches the original
Expand Down

0 comments on commit 317b98b

Please sign in to comment.