Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const_format = "0.2"
criterion = { version = "0.8", features = ["html_reports"] }
eyre = "0.6"
hex = "0.4"
once_cell = { version = "1.21", default-features = false }
paste = "1.0"
regex = "1.12"
serde = "1.0"
Expand Down
2 changes: 1 addition & 1 deletion crates/calendar/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ tokio = { workspace = true, features = ["full"] }
tower-http = { workspace = true, features = ["limit"] }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
uts-core = { workspace = true }
uts-core = { workspace = true, features = ["bytes"] }

[lints]
workspace = true
26 changes: 12 additions & 14 deletions crates/calendar/src/routes/ots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ use alloy_primitives::{Keccak256, b256};
use alloy_signer::SignerSync;
use alloy_signer_local::LocalSigner;
use axum::body::Bytes;
use bytes::{BufMut, BytesMut};
use bytes::BytesMut;
use smallvec::SmallVec;
use std::time::SystemTime;
use tracing::Level;
use uts_core::{
codec::{
Encode, Encoder,
v1::{Attestation, opcode::OpCode},
Encoder,
v1::{Attestation, PendingAttestation, opcode::OpCode},
},
utils::Hexed,
};
Expand Down Expand Up @@ -53,9 +53,9 @@ pub async fn submit_digest(digest: Bytes) -> Bytes {
+ 8 // Pending tag
+ 1 // length of packed ATTESTATION data length in leb128
+ (1 + uri.len()); // length of uri in leb128 + uri bytes
let attestation = Attestation::Pending { uri };
let attestation = PendingAttestation { uri: uri.into() };

let mut timestamp = BytesMut::with_capacity(buf_size).writer(); // TODO: replace this with a builder
let mut timestamp = BytesMut::with_capacity(buf_size);

let mut pending_attestation = SmallVec::<[u8; MAX_MESSAGE_SIZE]>::new();

Expand All @@ -66,8 +66,8 @@ pub async fn submit_digest(digest: Bytes) -> Bytes {
.as_secs();
trace!(recv_timestamp);
let recv_timestamp = recv_timestamp.to_le_bytes();
OpCode::PREPEND.encode(&mut timestamp).unwrap();
timestamp.encode_bytes(&recv_timestamp).unwrap();
timestamp.encode(OpCode::PREPEND).unwrap();
timestamp.encode_bytes(recv_timestamp).unwrap();
pending_attestation.extend(recv_timestamp);

trace!(digest = ?Hexed(&digest));
Expand All @@ -80,8 +80,8 @@ pub async fn submit_digest(digest: Bytes) -> Bytes {
let undeniable_sig = signer.sign_message_sync(&digest).unwrap();
let undeniable_sig = undeniable_sig.as_erc2098();
trace!(undeniable_sig = ?Hexed(&undeniable_sig));
OpCode::APPEND.encode(&mut timestamp).unwrap();
timestamp.encode_bytes(&undeniable_sig).unwrap();
timestamp.encode(OpCode::APPEND).unwrap();
timestamp.encode_bytes(undeniable_sig).unwrap();
pending_attestation.extend(undeniable_sig);

trace!(pending_attestation = ?Hexed(&pending_attestation));
Expand All @@ -96,14 +96,12 @@ pub async fn submit_digest(digest: Bytes) -> Bytes {
let mut hasher = Keccak256::new();
hasher.update(&pending_attestation);
hasher.finalize_into(&mut pending_attestation[0..32]);
OpCode::KECCAK256.encode(&mut timestamp).unwrap();
timestamp.encode(OpCode::KECCAK256).unwrap();

OpCode::ATTESTATION.encode(&mut timestamp).unwrap();
attestation.encode(&mut timestamp).unwrap();
timestamp.encode(OpCode::ATTESTATION).unwrap();
timestamp.encode(attestation.to_raw().unwrap()).unwrap();

// TODO: store the pending_attestation into journal

let timestamp = timestamp.into_inner();
debug_assert_eq!(timestamp.len(), buf_size, "buffer size mismatch");
timestamp.freeze()
}
Expand Down
11 changes: 6 additions & 5 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,26 @@ version.workspace = true
[[bin]]
name = "uts-info"
path = "src/bin/uts_info.rs"
required-features = ["std"]

[dependencies]
auto_impl.workspace = true
bytes = { workspace = true, optional = true }
digest.workspace = true
hex.workspace = true
once_cell = { workspace = true, features = ["alloc"] }
paste.workspace = true
ripemd.workspace = true
sha1.workspace = true
sha2.workspace = true
sha3.workspace = true
smallvec = { workspace = true, features = ["write"] }
thiserror.workspace = true
tracing = { workspace = true, optional = true }

[features]
nightly = [
"smallvec/specialization",
"smallvec/may_dangle",
]
bytes = ["dep:bytes"]
default = ["std"]
std = []
tracing = ["dep:tracing"]

[dev-dependencies]
Expand Down
7 changes: 4 additions & 3 deletions crates/core/src/bin/uts_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::{
process,
};
use uts_core::codec::{
Decode, VersionedProof,
Decode, Reader, VersionedProof,
v1::{DetachedTimestamp, Timestamp},
};

Expand All @@ -32,10 +32,11 @@ fn main() {
}
};

match VersionedProof::<DetachedTimestamp>::decode(&mut fh) {
match VersionedProof::<DetachedTimestamp>::decode(&mut Reader(&mut fh)) {
Ok(ots) => {
println!("OTS Detached Timestamp found:");
println!("{ots}");
return;
}
Err(e) => {
println!(
Expand All @@ -47,7 +48,7 @@ fn main() {

fh.seek(io::SeekFrom::Start(0)).unwrap();

match Timestamp::decode(fh) {
match Timestamp::decode(&mut Reader(&mut fh)) {
Ok(ots) => {
println!("Raw Timestamp found:");
println!("{ots}");
Expand Down
66 changes: 35 additions & 31 deletions crates/core/src/codec.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use crate::error::{DecodeError, EncodeError};
use alloc::alloc::Global;
use auto_impl::auto_impl;
use std::{
io::{BufRead, Write},
ops::RangeBounds,
};
use core::{alloc::Allocator, ops::RangeBounds};

mod proof;
pub use proof::{Proof, Version, VersionedProof};

mod primitives;
mod imp;
#[cfg(feature = "std")]
pub use imp::{Reader, Writer};

/// Types and helpers for the version 1 serialization format.
pub mod v1;
Expand All @@ -17,43 +17,36 @@ pub mod v1;
pub const MAGIC: &[u8; 31] = b"\x00OpenTimestamps\x00\x00Proof\x00\xbf\x89\xe2\xe8\x84\xe8\x92\x94";

/// Helper trait for writing OpenTimestamps primitives to a byte stream.
pub trait Encoder: Write {
pub trait Encoder: Sized {
/// Encodes a single byte to the writer.
fn encode_byte(&mut self, byte: u8) -> Result<(), EncodeError> {
self.write_all(&[byte])?;
Ok(())
}
fn encode_byte(&mut self, byte: u8) -> Result<(), EncodeError>;

/// Encodes a byte slice prefixed with its length.
fn encode_bytes(&mut self, bytes: &[u8]) -> Result<(), EncodeError> {
fn encode_bytes(&mut self, bytes: impl AsRef<[u8]>) -> Result<(), EncodeError> {
let bytes = bytes.as_ref();
self.encode(bytes.len())?;
self.write_all(bytes)?;
Ok(())
}

/// Writes the OpenTimestamps magic sequence to the stream.
fn encode_magic(&mut self) -> Result<(), EncodeError> {
self.write_all(MAGIC)?;
Ok(())
self.write_all(&MAGIC[..])
}

/// Encodes a value implementing the [`Encode`] trait.
#[inline]
fn encode(&mut self, value: impl Encode) -> Result<(), EncodeError> {
value.encode(self)
}
}

impl<W: Write> Encoder for W {}
// --- no_std feature compatibility ---
fn write_all(&mut self, buf: impl AsRef<[u8]>) -> Result<(), EncodeError>;
}

/// Helper trait for reading OpenTimestamps primitives from a byte stream.
pub trait Decoder: BufRead {
pub trait Decoder: Sized {
/// Decodes a single byte from the reader.
fn decode_byte(&mut self) -> Result<u8, DecodeError> {
let mut byte = [0];
self.read_exact(&mut byte)?;
Ok(byte[0])
}
fn decode_byte(&mut self) -> Result<u8, DecodeError>;

/// Decodes a value and ensures it falls within the supplied range.
fn decode_ranged<T: Decode + PartialOrd>(
Expand All @@ -80,26 +73,37 @@ pub trait Decoder: BufRead {
}

/// Decodes a value implementing the [`Decode`] trait.
#[inline]
fn decode<T: Decode>(&mut self) -> Result<T, DecodeError> {
T::decode(self)
}
}

impl<R: BufRead> Decoder for R {}

/// Marker trait for types supporting both [`Encode`] and [`Decode`].
pub trait Codec: Encode + Decode {}
/// Decodes a value implementing the [`Decode`] trait.
fn decode_in<T: DecodeIn<A>, A: Allocator>(&mut self, alloc: A) -> Result<T, DecodeError> {
T::decode_in(self, alloc)
}

impl<T: Encode + Decode> Codec for T {}
// --- no_std feature compatibility ---
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), DecodeError>;
}

/// Serializes a value into an OpenTimestamps-compatible byte stream.
#[auto_impl(&, &mut, Box, Rc, Arc)]
pub trait Encode {
fn encode(&self, writer: impl Encoder) -> Result<(), EncodeError>;
fn encode(&self, writer: &mut impl Encoder) -> Result<(), EncodeError>;
}

/// Deserializes a value from an OpenTimestamps-compatible byte stream.
pub trait Decode: Sized {
fn decode(reader: impl Decoder) -> Result<Self, DecodeError>;
fn decode(decoder: &mut impl Decoder) -> Result<Self, DecodeError>;
}

/// Deserializes a value from an OpenTimestamps-compatible byte stream.
pub trait DecodeIn<A: Allocator>: Sized {
fn decode_in(decoder: &mut impl Decoder, alloc: A) -> Result<Self, DecodeError>;
}

impl<T: DecodeIn<Global>> Decode for T {
fn decode(decoder: &mut impl Decoder) -> Result<Self, DecodeError> {
T::decode_in(decoder, Global)
}
}
43 changes: 43 additions & 0 deletions crates/core/src/codec/imp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use crate::codec::*;
use alloc::vec::Vec;

#[cfg(feature = "bytes")]
mod bytes;
mod primitives;
#[cfg(feature = "std")]
mod std_io;

#[cfg(feature = "std")]
pub use std_io::{Reader, Writer};

impl<A: Allocator> Encoder for Vec<u8, A> {
fn encode_byte(&mut self, byte: u8) -> Result<(), EncodeError> {
self.push(byte);
Ok(())
}

fn write_all(&mut self, buf: impl AsRef<[u8]>) -> Result<(), EncodeError> {
self.extend_from_slice(buf.as_ref());
Ok(())
}
}

impl Decoder for &[u8] {
fn decode_byte(&mut self) -> Result<u8, DecodeError> {
let Some((a, b)) = self.split_at_checked(1) else {
return Err(DecodeError::UnexpectedEof);
};
*self = b;
Ok(a[0])
}

fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), DecodeError> {
let len = buf.len();
let Some((a, b)) = self.split_at_checked(len) else {
return Err(DecodeError::UnexpectedEof);
};
buf.copy_from_slice(a);
*self = b;
Ok(())
}
}
14 changes: 14 additions & 0 deletions crates/core/src/codec/imp/bytes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use crate::codec::*;
use bytes::{BufMut, BytesMut};

impl Encoder for BytesMut {
fn encode_byte(&mut self, byte: u8) -> Result<(), EncodeError> {
self.put_u8(byte);
Ok(())
}

fn write_all(&mut self, buf: impl AsRef<[u8]>) -> Result<(), EncodeError> {
self.put_slice(buf.as_ref());
Ok(())
}
}
Loading