1use cylinder::Signer;
21use protobuf::Message;
22
23use crate::protos::{
24 block::{Block as BlockProto, BlockHeader as BlockHeaderProto},
25 FromBytes, FromNative, FromProto, IntoBytes, IntoNative, IntoProto, ProtoConversionError,
26};
27use crate::transact::protocol::batch::Batch;
28
29use super::ProtocolBuildError;
30
31#[derive(Clone, Debug, PartialEq, Eq)]
33pub struct Block {
34 header: Vec<u8>,
35 header_signature: String,
36 batches: Vec<Batch>,
37}
38
39impl Block {
40 pub fn header(&self) -> &[u8] {
41 &self.header
42 }
43
44 pub fn header_signature(&self) -> &str {
45 &self.header_signature
46 }
47
48 pub fn batches(&self) -> &[Batch] {
49 &self.batches
50 }
51
52 pub fn into_pair(self) -> Result<BlockPair, ProtocolBuildError> {
53 let header = BlockHeader::from_bytes(&self.header)?;
54
55 Ok(BlockPair {
56 block: self,
57 header,
58 })
59 }
60}
61
62impl std::fmt::Display for Block {
63 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
64 write!(
65 f,
66 "Block(header_signature: {}, {} batches)",
67 self.header_signature,
68 self.batches.len(),
69 )
70 }
71}
72
73impl FromBytes<Block> for Block {
74 fn from_bytes(bytes: &[u8]) -> Result<Self, ProtoConversionError> {
75 Message::parse_from_bytes(bytes)
76 .map_err(|_| {
77 ProtoConversionError::SerializationError(
78 "Unable to get Block from bytes".to_string(),
79 )
80 })
81 .and_then(Self::from_proto)
82 }
83}
84
85impl FromNative<Block> for BlockProto {
86 fn from_native(block: Block) -> Result<Self, ProtoConversionError> {
87 let mut block_proto = BlockProto::new();
88
89 block_proto.set_header(block.header);
90 block_proto.set_header_signature(block.header_signature);
91 block_proto.set_batches(
92 block
93 .batches
94 .into_iter()
95 .map(IntoProto::into_proto)
96 .collect::<Result<_, _>>()?,
97 );
98
99 Ok(block_proto)
100 }
101}
102
103impl FromProto<BlockProto> for Block {
104 fn from_proto(block: BlockProto) -> Result<Self, ProtoConversionError> {
105 Ok(Block {
106 header: block.header,
107 header_signature: block.header_signature,
108 batches: block
109 .batches
110 .into_iter()
111 .map(Batch::from_proto)
112 .collect::<Result<_, _>>()?,
113 })
114 }
115}
116
117impl IntoBytes for Block {
118 fn into_bytes(self) -> Result<Vec<u8>, ProtoConversionError> {
119 self.into_proto()?.write_to_bytes().map_err(|_| {
120 ProtoConversionError::SerializationError("Unable to get bytes from Block".to_string())
121 })
122 }
123}
124
125impl IntoNative<Block> for BlockProto {}
126impl IntoProto<BlockProto> for Block {}
127
128#[derive(Clone, Debug, PartialEq, Eq)]
130pub struct BlockHeader {
131 block_num: u64,
132 previous_block_id: String,
133 signer_public_key: Vec<u8>,
134 batch_ids: Vec<String>,
135 consensus: Vec<u8>,
136 state_root_hash: Vec<u8>,
137}
138
139impl BlockHeader {
140 pub fn block_num(&self) -> u64 {
141 self.block_num
142 }
143
144 pub fn previous_block_id(&self) -> &str {
145 &self.previous_block_id
146 }
147
148 pub fn signer_public_key(&self) -> &[u8] {
149 &self.signer_public_key
150 }
151
152 pub fn batch_ids(&self) -> &[String] {
153 &self.batch_ids
154 }
155
156 pub fn consensus(&self) -> &[u8] {
157 &self.consensus
158 }
159
160 pub fn state_root_hash(&self) -> &[u8] {
161 &self.state_root_hash
162 }
163}
164
165impl std::fmt::Display for BlockHeader {
166 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
167 write!(
168 f,
169 "BlockHeader(block_num: {}, previous_block_id: {}, signer_public_key: {}, \
170 state_root_hash: {}, {} batches)",
171 self.block_num,
172 self.previous_block_id,
173 hex::encode(&self.signer_public_key),
174 hex::encode(&self.state_root_hash),
175 self.batch_ids.len(),
176 )
177 }
178}
179
180impl FromBytes<BlockHeader> for BlockHeader {
181 fn from_bytes(bytes: &[u8]) -> Result<Self, ProtoConversionError> {
182 Message::parse_from_bytes(bytes)
183 .map_err(|_| {
184 ProtoConversionError::SerializationError(
185 "Unable to get BlockHeader from bytes".to_string(),
186 )
187 })
188 .and_then(Self::from_proto)
189 }
190}
191
192impl FromNative<BlockHeader> for BlockHeaderProto {
193 fn from_native(block_header: BlockHeader) -> Result<Self, ProtoConversionError> {
194 let mut block_header_proto = BlockHeaderProto::new();
195
196 block_header_proto.set_block_num(block_header.block_num);
197 block_header_proto.set_previous_block_id(block_header.previous_block_id);
198 block_header_proto.set_signer_public_key(hex::encode(block_header.signer_public_key));
199 block_header_proto.set_batch_ids(block_header.batch_ids.into());
200 block_header_proto.set_consensus(block_header.consensus);
201 block_header_proto.set_state_root_hash(hex::encode(block_header.state_root_hash));
202
203 Ok(block_header_proto)
204 }
205}
206
207impl FromProto<BlockHeaderProto> for BlockHeader {
208 fn from_proto(block_header: BlockHeaderProto) -> Result<Self, ProtoConversionError> {
209 Ok(BlockHeader {
210 block_num: block_header.block_num,
211 previous_block_id: block_header.previous_block_id,
212 signer_public_key: hex::decode(block_header.signer_public_key)?,
213 batch_ids: block_header.batch_ids.into(),
214 consensus: block_header.consensus,
215 state_root_hash: hex::decode(block_header.state_root_hash)?,
216 })
217 }
218}
219
220impl IntoBytes for BlockHeader {
221 fn into_bytes(self) -> Result<Vec<u8>, ProtoConversionError> {
222 self.into_proto()?.write_to_bytes().map_err(|_| {
223 ProtoConversionError::SerializationError(
224 "Unable to get bytes from BlockHeader".to_string(),
225 )
226 })
227 }
228}
229
230impl IntoNative<BlockHeader> for BlockHeaderProto {}
231impl IntoProto<BlockHeaderProto> for BlockHeader {}
232
233#[derive(Clone, Debug, PartialEq, Eq)]
235pub struct BlockPair {
236 block: Block,
237 header: BlockHeader,
238}
239
240impl BlockPair {
241 pub fn block(&self) -> &Block {
242 &self.block
243 }
244
245 pub fn header(&self) -> &BlockHeader {
246 &self.header
247 }
248
249 pub fn take(self) -> (Block, BlockHeader) {
250 (self.block, self.header)
251 }
252}
253
254impl FromBytes<BlockPair> for BlockPair {
255 fn from_bytes(bytes: &[u8]) -> Result<Self, ProtoConversionError> {
256 Block::from_bytes(bytes)?
257 .into_pair()
258 .map_err(|err| ProtoConversionError::DeserializationError(err.to_string()))
259 }
260}
261
262impl FromNative<BlockPair> for BlockProto {
263 fn from_native(block_pair: BlockPair) -> Result<Self, ProtoConversionError> {
264 block_pair.block.into_proto()
265 }
266}
267
268impl FromProto<BlockProto> for BlockPair {
269 fn from_proto(block: BlockProto) -> Result<Self, ProtoConversionError> {
270 Block::from_proto(block)?
271 .into_pair()
272 .map_err(|err| ProtoConversionError::DeserializationError(err.to_string()))
273 }
274}
275
276impl IntoBytes for BlockPair {
277 fn into_bytes(self) -> Result<Vec<u8>, ProtoConversionError> {
278 self.block.into_bytes()
279 }
280}
281
282impl IntoNative<BlockPair> for BlockProto {}
283impl IntoProto<BlockProto> for BlockPair {}
284
285#[derive(Default, Clone)]
287pub struct BlockBuilder {
288 block_num: Option<u64>,
289 previous_block_id: Option<String>,
290 consensus: Vec<u8>,
291 state_root_hash: Option<Vec<u8>>,
292 batches: Option<Vec<Batch>>,
293}
294
295impl BlockBuilder {
296 pub fn new() -> Self {
298 Self::default()
299 }
300
301 pub fn with_block_num(mut self, block_num: u64) -> Self {
303 self.block_num = Some(block_num);
304 self
305 }
306
307 pub fn with_previous_block_id(mut self, previous_block_id: String) -> Self {
309 self.previous_block_id = Some(previous_block_id);
310 self
311 }
312
313 pub fn with_consensus(mut self, consensus: Vec<u8>) -> Self {
315 self.consensus = consensus;
316 self
317 }
318
319 pub fn with_state_root_hash(mut self, state_root_hash: Vec<u8>) -> Self {
321 self.state_root_hash = Some(state_root_hash);
322 self
323 }
324
325 pub fn with_batches(mut self, batches: Vec<Batch>) -> Self {
327 self.batches = Some(batches);
328 self
329 }
330
331 pub fn build_pair(self, signer: &dyn Signer) -> Result<BlockPair, ProtocolBuildError> {
342 let block_num = self.block_num.ok_or_else(|| {
343 ProtocolBuildError::MissingField("'block_num' field is required".to_string())
344 })?;
345 let previous_block_id = self.previous_block_id.ok_or_else(|| {
346 ProtocolBuildError::MissingField("'previous_block_id' field is required".to_string())
347 })?;
348 let state_root_hash = self.state_root_hash.ok_or_else(|| {
349 ProtocolBuildError::MissingField("'state_root_hash' field is required".to_string())
350 })?;
351 let batches = self.batches.ok_or_else(|| {
352 ProtocolBuildError::MissingField("'batches' field is required".to_string())
353 })?;
354
355 let signer_public_key = signer.public_key()?.as_slice().to_vec();
356
357 let header = BlockHeader {
358 block_num,
359 previous_block_id,
360 signer_public_key,
361 batch_ids: batches
362 .iter()
363 .map(|batch| batch.header_signature().to_string())
364 .collect(),
365 consensus: self.consensus,
366 state_root_hash,
367 };
368
369 let header_bytes = header.clone().into_bytes()?;
370 let header_signature = signer.sign(&header_bytes)?.as_hex();
371
372 let block = Block {
373 header: header_bytes,
374 header_signature,
375 batches,
376 };
377
378 Ok(BlockPair { block, header })
379 }
380
381 pub fn build(self, signer: &dyn Signer) -> Result<Block, ProtocolBuildError> {
384 Ok(self.build_pair(signer)?.block)
385 }
386}
387
388#[cfg(test)]
389mod tests {
390 use super::*;
391
392 use cylinder::{secp256k1::Secp256k1Context, Context, Signer};
393
394 use crate::transact::protocol::{
395 batch::BatchBuilder,
396 transaction::{HashMethod, TransactionBuilder},
397 };
398
399 const BLOCK_NUM: u64 = 0;
400 const PREVIOUS_BLOCK_ID: &str = "0123";
401 const CONSENSUS: [u8; 4] = [0x01, 0x02, 0x03, 0x04];
402 const STATE_ROOT_HASH: [u8; 4] = [0x05, 0x06, 0x07, 0x08];
403
404 #[test]
410 fn builder_chain() {
411 let signer = new_signer();
412
413 let pair = BlockBuilder::new()
414 .with_block_num(BLOCK_NUM)
415 .with_previous_block_id(PREVIOUS_BLOCK_ID.into())
416 .with_consensus(CONSENSUS.into())
417 .with_state_root_hash(STATE_ROOT_HASH.into())
418 .with_batches(vec![batch_1(&*signer), batch_2(&*signer)])
419 .build_pair(&*signer)
420 .expect("Failed to build block pair");
421
422 check_pair(&*signer, &pair);
423 }
424
425 #[test]
431 fn builder_separate() {
432 let signer = new_signer();
433
434 let mut builder = BlockBuilder::new();
435 builder = builder.with_block_num(BLOCK_NUM);
436 builder = builder.with_previous_block_id(PREVIOUS_BLOCK_ID.into());
437 builder = builder.with_consensus(CONSENSUS.into());
438 builder = builder.with_state_root_hash(STATE_ROOT_HASH.into());
439 builder = builder.with_batches(vec![batch_1(&*signer), batch_2(&*signer)]);
440
441 let pair = builder
442 .build_pair(&*signer)
443 .expect("Failed to build block pair");
444
445 check_pair(&*signer, &pair);
446 }
447
448 #[test]
453 fn builder_defaults() {
454 let signer = new_signer();
455
456 BlockBuilder::new()
457 .with_block_num(BLOCK_NUM)
458 .with_previous_block_id(PREVIOUS_BLOCK_ID.into())
459 .with_state_root_hash(STATE_ROOT_HASH.into())
460 .with_batches(vec![batch_1(&*signer), batch_2(&*signer)])
461 .build_pair(&*new_signer())
462 .expect("Failed to build block pair");
463 }
464
465 #[test]
473 fn builder_missing_fields() {
474 let signer = new_signer();
475
476 match BlockBuilder::new()
477 .with_previous_block_id(PREVIOUS_BLOCK_ID.into())
478 .with_state_root_hash(STATE_ROOT_HASH.into())
479 .with_batches(vec![batch_1(&*signer), batch_2(&*signer)])
480 .build_pair(&*signer)
481 {
482 Err(ProtocolBuildError::MissingField(_)) => {}
483 res => panic!(
484 "Expected Err(ProtocolBuildError::MissingField), got {:?}",
485 res
486 ),
487 }
488
489 match BlockBuilder::new()
490 .with_block_num(BLOCK_NUM)
491 .with_state_root_hash(STATE_ROOT_HASH.into())
492 .with_batches(vec![batch_1(&*signer), batch_2(&*signer)])
493 .build_pair(&*signer)
494 {
495 Err(ProtocolBuildError::MissingField(_)) => {}
496 res => panic!(
497 "Expected Err(ProtocolBuildError::MissingField), got {:?}",
498 res
499 ),
500 }
501
502 match BlockBuilder::new()
503 .with_block_num(BLOCK_NUM)
504 .with_previous_block_id(PREVIOUS_BLOCK_ID.into())
505 .with_batches(vec![batch_1(&*signer), batch_2(&*signer)])
506 .build_pair(&*signer)
507 {
508 Err(ProtocolBuildError::MissingField(_)) => {}
509 res => panic!(
510 "Expected Err(ProtocolBuildError::MissingField), got {:?}",
511 res
512 ),
513 }
514
515 match BlockBuilder::new()
516 .with_block_num(BLOCK_NUM)
517 .with_previous_block_id(PREVIOUS_BLOCK_ID.into())
518 .with_state_root_hash(STATE_ROOT_HASH.into())
519 .build_pair(&*signer)
520 {
521 Err(ProtocolBuildError::MissingField(_)) => {}
522 res => panic!(
523 "Expected Err(ProtocolBuildError::MissingField), got {:?}",
524 res
525 ),
526 }
527 }
528
529 fn check_pair(signer: &dyn Signer, pair: &BlockPair) {
530 let signer_pub_key = signer
531 .public_key()
532 .expect("Failed to get signer public key");
533
534 assert_eq!(pair.header().block_num(), BLOCK_NUM);
535 assert_eq!(pair.header().previous_block_id(), PREVIOUS_BLOCK_ID);
536 assert_eq!(pair.header().signer_public_key(), signer_pub_key.as_slice());
537 assert_eq!(
538 pair.header().batch_ids(),
539 &[
540 batch_1(signer).header_signature().to_string(),
541 batch_2(signer).header_signature().to_string()
542 ]
543 );
544 assert_eq!(pair.header().consensus(), CONSENSUS);
545 assert_eq!(pair.header().state_root_hash(), STATE_ROOT_HASH);
546 assert_eq!(
547 pair.block().header(),
548 pair.header()
549 .clone()
550 .into_bytes()
551 .expect("Failed to get header bytes")
552 .as_slice()
553 );
554 assert_eq!(pair.block().batches(), &[batch_1(signer), batch_2(signer)]);
555 }
556
557 fn batch_1(signer: &dyn Signer) -> Batch {
558 let txn = TransactionBuilder::new()
559 .with_family_name("test".into())
560 .with_family_version("1.0".into())
561 .with_inputs(vec![])
562 .with_outputs(vec![])
563 .with_payload_hash_method(HashMethod::Sha512)
564 .with_payload(vec![])
565 .with_nonce(vec![1])
566 .build(signer)
567 .expect("Failed to build txn");
568
569 BatchBuilder::new()
570 .with_transactions(vec![txn])
571 .build(signer)
572 .expect("Failed to build batch1")
573 }
574
575 fn batch_2(signer: &dyn Signer) -> Batch {
576 let txn = TransactionBuilder::new()
577 .with_family_name("test".into())
578 .with_family_version("1.0".into())
579 .with_inputs(vec![])
580 .with_outputs(vec![])
581 .with_payload_hash_method(HashMethod::Sha512)
582 .with_payload(vec![])
583 .with_nonce(vec![2])
584 .build(signer)
585 .expect("Failed to build txn");
586
587 BatchBuilder::new()
588 .with_transactions(vec![txn])
589 .build(signer)
590 .expect("Failed to build batch1")
591 }
592
593 fn new_signer() -> Box<dyn Signer> {
594 let context = Secp256k1Context::new();
595 let key = context.new_random_private_key();
596 context.new_signer(key)
597 }
598}