1#![warn(rust_2018_idioms, missing_debug_implementations, missing_docs)]
2mod sloth;
5
6use crate::sloth::Sloth;
7
8#[derive(Debug, Clone)]
10pub struct Spartan<const PRIME_SIZE_BYTES: usize, const PIECE_SIZE_BYTES: usize> {
11 genesis_piece: [u8; PIECE_SIZE_BYTES],
12 sloth: Sloth<PRIME_SIZE_BYTES, PIECE_SIZE_BYTES>,
13}
14
15pub type Spartan64bit4096 = Spartan<8, 4096>;
17pub type Spartan128bit4096 = Spartan<16, 4096>;
19pub type Spartan256bit4096 = Spartan<32, 4096>;
21pub type Spartan512bit4096 = Spartan<64, 4096>;
23pub type Spartan1024bit4096 = Spartan<128, 4096>;
25pub type Spartan2048bit4096 = Spartan<256, 4096>;
27pub type Spartan4096bit4096 = Spartan<512, 4096>;
29
30impl<const PRIME_SIZE_BYTES: usize, const PIECE_SIZE_BYTES: usize>
31 Spartan<PRIME_SIZE_BYTES, PIECE_SIZE_BYTES>
32{
33 fn new_internal(genesis_piece: [u8; PIECE_SIZE_BYTES]) -> Self {
34 let sloth = Sloth::new();
35 Self {
36 genesis_piece,
37 sloth,
38 }
39 }
40}
41
42impl Spartan<8, 4096> {
43 pub fn new(genesis_piece: [u8; 4096]) -> Self {
45 Self::new_internal(genesis_piece)
46 }
47}
48
49impl Spartan<16, 4096> {
50 pub fn new(genesis_piece: [u8; 4096]) -> Self {
52 Self::new_internal(genesis_piece)
53 }
54}
55
56impl Spartan<32, 4096> {
57 pub fn new(genesis_piece: [u8; 4096]) -> Self {
59 Self::new_internal(genesis_piece)
60 }
61}
62
63impl Spartan<64, 4096> {
64 pub fn new(genesis_piece: [u8; 4096]) -> Self {
66 Self::new_internal(genesis_piece)
67 }
68}
69
70impl Spartan<128, 4096> {
71 pub fn new(genesis_piece: [u8; 4096]) -> Self {
73 Self::new_internal(genesis_piece)
74 }
75}
76
77impl Spartan<256, 4096> {
78 pub fn new(genesis_piece: [u8; 4096]) -> Self {
80 Self::new_internal(genesis_piece)
81 }
82}
83
84impl Spartan<512, 4096> {
85 pub fn new(genesis_piece: [u8; 4096]) -> Self {
87 Self::new_internal(genesis_piece)
88 }
89}
90
91impl<const PRIME_SIZE_BYTES: usize, const PIECE_SIZE_BYTES: usize>
92 Spartan<PRIME_SIZE_BYTES, PIECE_SIZE_BYTES>
93{
94 pub fn encode(
97 &self,
98 encoding_key_hash: [u8; PRIME_SIZE_BYTES],
99 nonce: u64,
100 rounds: usize,
101 ) -> [u8; PIECE_SIZE_BYTES] {
102 let mut expanded_iv = encoding_key_hash;
103 for (i, &byte) in nonce.to_le_bytes().iter().rev().enumerate() {
104 expanded_iv[PRIME_SIZE_BYTES - i - 1] ^= byte;
105 }
106
107 let mut encoding = self.genesis_piece;
108 self.sloth
110 .encode(&mut encoding, expanded_iv, rounds)
111 .unwrap();
112
113 encoding
114 }
115
116 pub fn is_valid(
118 &self,
119 mut encoding: [u8; PIECE_SIZE_BYTES],
120 encoding_key_hash: [u8; PRIME_SIZE_BYTES],
121 nonce: u64,
122 rounds: usize,
123 ) -> bool {
124 let mut expanded_iv = encoding_key_hash;
125 for (i, &byte) in nonce.to_le_bytes().iter().rev().enumerate() {
126 expanded_iv[PRIME_SIZE_BYTES - i - 1] ^= byte;
127 }
128
129 self.sloth.decode(&mut encoding, expanded_iv, rounds);
130
131 encoding == self.genesis_piece
132 }
133
134 #[cfg(feature = "parallel")]
136 pub fn is_valid_parallel(
137 &self,
138 piece: [u8; PIECE_SIZE_BYTES],
139 encoding_key_hash: [u8; PRIME_SIZE_BYTES],
140 nonce: u64,
141 rounds: usize,
142 ) -> bool {
143 let mut piece = piece;
144 let mut expanded_iv = encoding_key_hash;
145 for (i, &byte) in nonce.to_le_bytes().iter().rev().enumerate() {
146 expanded_iv[PRIME_SIZE_BYTES - i - 1] ^= byte;
147 }
148
149 self.sloth.decode_parallel(&mut piece, expanded_iv, rounds);
150
151 piece == self.genesis_piece
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158 use rand::prelude::*;
159
160 fn random_bytes<const BYTES: usize>() -> [u8; BYTES] {
161 let mut bytes = [0u8; BYTES];
162 rand::thread_rng().fill(&mut bytes[..]);
163 bytes
164 }
165
166 #[test]
167 fn test_random_piece() {
168 let genesis_piece = random_bytes();
169 let encoding_key = random_bytes();
170 let nonce = rand::random();
171
172 let spartan = Spartan256bit4096::new(genesis_piece);
173 let encoding = spartan.encode(encoding_key, nonce, 1);
174
175 assert!(spartan.is_valid(encoding, encoding_key, nonce, 1));
176
177 assert!(spartan.is_valid_parallel(encoding, encoding_key, nonce, 1));
178 }
179}