Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restructure #19

Merged
merged 6 commits into from
May 14, 2024
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
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Sia Core RS

This is a Rust implementation of core Sia types. It is intended to be used as a
library for projects that need to interact with Sia at a low level. It is not
intended to be a full Sia node implementation.

This project is currently in the early stages of development and is not yet ready
for production use. The API will have breaking changes.

## Features
- [x] BIP-39 Seeds
- [x] Addresses
- [x] Sia binary encoding
- [ ] Sector roots
- [ ] Merkle proofs

### v1
- [x] Unlock conditions
- [x] Transaction signing

### v2
- [x] Spend policies
- [ ] Transaction signing
- [ ] RHP4
- [ ] State tree verification
213 changes: 213 additions & 0 deletions src/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
use blake2b_simd::{Hash, Params};
use core::fmt;
use serde::Serialize;

pub struct ChainIndex {
pub height: u64,
pub id: [u8; 32],
}

impl fmt::Display for ChainIndex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}:{}", self.height, hex::encode(self.id))
}
}

/// encapsulates the various errors that can occur when parsing a Sia object
/// from a string
#[derive(Debug, PartialEq)]
pub enum HexParseError {
MissingPrefix,
InvalidLength,
InvalidPrefix,
InvalidChecksum, // not every object has a checksum
HexError(hex::FromHexError),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Hash256([u8; 32]);

impl Serialize for Hash256 {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
if serializer.is_human_readable() {
serializer.serialize_str(&self.to_string())
} else {
self.0.serialize(serializer)
}
}
}

impl Hash256 {
pub fn new(data: [u8; 32]) -> Self {
Hash256(data)
}

pub fn as_bytes(&self) -> &[u8] {
self.0.as_ref()
}

pub fn as_array(&self) -> &[u8; 32] {
&self.0
}

pub fn parse_string(s: &str) -> Result<Self, HexParseError> {
let s = match s.split_once(':') {
Some((_prefix, suffix)) => suffix,
None => s,
};

if s.len() != 64 {
return Err(HexParseError::InvalidLength);
}

let mut data = [0u8; 32];
hex::decode_to_slice(s, &mut data).map_err(HexParseError::HexError)?;
Ok(Hash256(data))
}
}

impl fmt::Display for Hash256 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "h:{}", hex::encode(self.0))
}
}

impl From<Hash> for Hash256 {
fn from(hash: Hash) -> Self {
let mut h = [0; 32];
h.copy_from_slice(&hash.as_bytes()[..32]);
Self(h)
}
}

impl AsRef<[u8]> for Hash256 {
fn as_ref(&self) -> &[u8] {
&self.0
}
}

/// An address that can be used to receive UTXOs
#[derive(Debug, PartialEq, Clone)]
pub struct Address([u8; 32]);

impl Serialize for Address {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
self.to_string().serialize(serializer)
} else {
self.0.serialize(serializer)
}
}
}

impl Address {
pub fn new(addr: [u8; 32]) -> Address {
Address(addr)
}

pub fn parse_string(s: &str) -> Result<Self, HexParseError> {
let s = match s.split_once(':') {
Some((_prefix, suffix)) => suffix,
None => s,
};

if s.len() != 76 {
return Err(HexParseError::InvalidLength);
}

let mut data = [0u8; 38];
hex::decode_to_slice(s, &mut data).map_err(HexParseError::HexError)?;

let h = Params::new()
.hash_length(32)
.to_state()
.update(&data[..32])
.finalize();
let checksum = h.as_bytes();

if checksum[..6] != data[32..] {
return Err(HexParseError::InvalidChecksum);
}

Ok(data[..32].into())
}
}

impl AsRef<[u8]> for Address {
fn as_ref(&self) -> &[u8] {
&self.0
}
}

impl From<&[u8]> for Address {
fn from(val: &[u8]) -> Self {
let mut data = [0u8; 32];
data.copy_from_slice(val);
Address(data)
}
}

impl fmt::Display for Address {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut buf = [0u8; 32 + 6];
buf[..32].copy_from_slice(&self.0);

let h = Params::new()
.hash_length(32)
.to_state()
.update(&self.0)
.finalize();

buf[32..].copy_from_slice(&h.as_bytes()[..6]);
write!(f, "addr:{}", hex::encode(buf))
}
}

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

#[test]
fn test_json_serialize_hash256() {
let hash = Hash256::parse_string(
"h:9aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b6",
)
.unwrap();
assert_eq!(
serde_json::to_string(&hash).unwrap(),
"\"h:9aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b6\""
);
}

#[test]
fn test_sia_serialize_address() {
let address = Address::parse_string(
"addr:8fb49ccf17dfdcc9526dec6ee8a5cca20ff8247302053d3777410b9b0494ba8cdf32abee86f0",
)
.unwrap();

// note: the expected value is the same as the input value, but without the checksum
assert_eq!(
to_bytes(&address).unwrap(),
hex::decode("8fb49ccf17dfdcc9526dec6ee8a5cca20ff8247302053d3777410b9b0494ba8c")
.unwrap()
)
}

#[test]
fn test_json_serialize_address() {
let address = Address::parse_string(
"addr:8fb49ccf17dfdcc9526dec6ee8a5cca20ff8247302053d3777410b9b0494ba8cdf32abee86f0",
)
.unwrap();

assert_eq!(
serde_json::to_string(&address).unwrap(),
"\"addr:8fb49ccf17dfdcc9526dec6ee8a5cca20ff8247302053d3777410b9b0494ba8cdf32abee86f0\""
)
}
}
12 changes: 0 additions & 12 deletions src/consensus.rs

This file was deleted.

101 changes: 4 additions & 97 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,108 +1,15 @@
use core::fmt;
mod common;

pub mod address;
pub mod consensus;
pub mod currency;
pub mod encoding;
pub mod seed;
pub mod signing;
pub mod spendpolicy;
pub mod transactions;
pub mod unlock_conditions;

pub(crate) mod blake2b;
pub(crate) mod merkle;
pub(crate) mod specifier;

pub use address::*;
pub use consensus::*;
pub use common::*;
pub use currency::*;
pub use seed::*;
use serde::Serialize;
pub use signing::*;
pub use spendpolicy::*;
pub use transactions::*;

/// encapsulates the various errors that can occur when parsing a Sia object
/// from a string
#[derive(Debug, PartialEq)]
pub enum HexParseError {
MissingPrefix,
InvalidLength,
InvalidPrefix,
InvalidChecksum, // not every object has a checksum
HexError(hex::FromHexError),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Hash256([u8; 32]);

impl Serialize for Hash256 {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
if serializer.is_human_readable() {
serializer.serialize_str(&self.to_string())
} else {
self.0.serialize(serializer)
}
}
}

impl Hash256 {
pub fn new(data: [u8; 32]) -> Self {
Hash256(data)
}

pub fn as_bytes(&self) -> [u8; 32] {
self.0
}

pub fn parse_string(s: &str) -> Result<Self, HexParseError> {
let s = match s.split_once(':') {
Some((_prefix, suffix)) => suffix,
None => s,
};

if s.len() != 64 {
return Err(HexParseError::InvalidLength);
}

let mut data = [0u8; 32];
hex::decode_to_slice(s, &mut data).map_err(HexParseError::HexError)?;
Ok(Hash256(data))
}
}

impl fmt::Display for Hash256 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "h:{}", hex::encode(self.0))
}
}

impl AsRef<[u8]> for Hash256 {
fn as_ref(&self) -> &[u8] {
&self.0
}
}

impl From<&[u8]> for Hash256 {
fn from(value: &[u8]) -> Self {
let mut h = [0; 32];
h.copy_from_slice(value);
Self(h)
}
}

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

#[test]
fn test_json_serialize_hash256() {
let hash = Hash256::parse_string(
"h:9aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b6",
)
.unwrap();
assert_eq!(
serde_json::to_string(&hash).unwrap(),
"\"h:9aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b6\""
);
}
}
File renamed without changes.
Loading