Skip to main content

sawtooth/protocol/
block.rs

1/*
2 * Copyright 2018-2020 Cargill Incorporated
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 * ------------------------------------------------------------------------------
16 */
17
18//! Sawtooth block protocol
19
20use 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/// A Sawtooth block
32#[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/// A Sawtooth block header
129#[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/// A Sawtooth (block, block header) pair
234#[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/// Builder for [`Block`](struct.Block.html) and [`BlockPair`](struct.BlockPair.html)
286#[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    /// Creates a new `BlockBuilder`
297    pub fn new() -> Self {
298        Self::default()
299    }
300
301    /// Sets the block number for the block to be built
302    pub fn with_block_num(mut self, block_num: u64) -> Self {
303        self.block_num = Some(block_num);
304        self
305    }
306
307    /// Sets the ID of the block previous to the one being built
308    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    /// Sets the consensus bytes for of block to be built
314    pub fn with_consensus(mut self, consensus: Vec<u8>) -> Self {
315        self.consensus = consensus;
316        self
317    }
318
319    /// Sets the state root hash of the block to be built
320    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    /// Sets the batches that will be in the block to be built
326    pub fn with_batches(mut self, batches: Vec<Batch>) -> Self {
327        self.batches = Some(batches);
328        self
329    }
330
331    /// Builds the `BlockPair`
332    ///
333    /// # Errors
334    ///
335    /// * Returns an error if any of the following are not set:
336    ///   - `block_num`
337    ///   - `previous_block_id`
338    ///   - `state_root_hash`
339    ///   - `batches`
340    /// * Propogates any signing error that occurs
341    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    /// Builds the `BlockPair`. This is a wrapper of the
382    /// [`build_pair`](struct.BlockBuilder.html#method.build_pair) method.
383    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    /// Verify that the `BlockBuilder` can be successfully used in a chain.
405    ///
406    /// 1. Initialize a signer
407    /// 2. Construct the block using a builder chain
408    /// 3. Verify that the resulting block pair is correct
409    #[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    /// Verify that the `BlockBuilder` can be successfully used with separate calls to its methods.
426    ///
427    /// 1. Initialize a signer
428    /// 2. Construct the block using separate method calls and assignments of the builder
429    /// 3. Verify that the resulting block pair is correct
430    #[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    /// Verify that the consensus field can be excluded from the `BlockBuilder`.
449    ///
450    /// 1. Initialize a signer
451    /// 2. Verify that a block without a `consensus` value set builds succesfully
452    #[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    /// Verify that the `BlockBuilder` fails when any of the required fields are missing.
466    ///
467    /// 1. Initialize a signer
468    /// 2. Attempt to build a block without setting `block_num` and verify that it fails.
469    /// 3. Attempt to build a block without setting `previous_block_id` and verify that it fails.
470    /// 4. Attempt to build a block without setting `state_root_hash` and verify that it fails.
471    /// 5. Attempt to build a block without setting `batches` and verify that it fails.
472    #[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}