1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use crate::structs::{Block, Error, ErrorInfo, Hash, StructMetadata};
use crate::{error_message, SafeOption};
use crate::{HashClear, SafeBytesAccess};
use crate::{ProtoHashable, WithMetadataHashableFields};

impl HashClear for Block {
    fn hash_clear(&mut self) {
        self.transactions.clear();
        self.hash = None;
    }
}

impl WithMetadataHashableFields for Block {
    fn struct_metadata_opt(&mut self) -> Option<&mut StructMetadata> {
        self.struct_metadata.as_mut()
    }

    fn struct_metadata_opt_ref(&self) -> Option<&StructMetadata> {
        self.struct_metadata.as_ref()
    }
}

impl Block {
    // Change this to something off of prost message
    // pub fn proto_serialize(&self) -> Vec<u8> {
    //     return self.encode_to_vec();
    // }
    //
    // pub fn proto_deserialize(bytes: Vec<u8>) -> Result<Self, ErrorInfo> {
    //     // TODO: Automap this error with a generic _.to_string() trait implicit?
    //     return Block::decode(&*bytes)
    //         .map_err(|e| error_message(Error::ProtoDecoderFailure, e.to_string()));
    // }
    //
    // pub fn hash_clear(&mut self) {
    //     self.transactions.clear();
    //     self.hash = None;
    // }

    pub fn with_hash(&mut self) -> &mut Block {
        let hash = self.calculate_hash();
        self.hash = Some(hash);
        self
    }
    //
    // pub fn calculate_hash(&self) -> Hash {
    //     let mut clone = self.clone();
    //     clone.hash_clear();
    //     let input = clone.proto_serialize();
    //     let multihash = constants::HASHER.digest(&input);
    //     return multihash.into();
    // }

    // TODO: Move to trait
    pub fn hash_hex(&self) -> String {
        return hex::encode(self.calculate_hash().bytes.expect("Bytes").value);
    }

    pub fn time(&self) -> Result<i64, ErrorInfo> {
        Ok(self
            .struct_metadata
            .safe_get()?
            .time
            .safe_get()?
            .clone()
        )
    }

    pub fn hash_bytes(&self) -> Result<Vec<u8>, ErrorInfo> {
        Ok(self
            .hash
            .as_ref()
            .ok_or(error_message(Error::MissingField, "hash"))?
            .bytes
            .safe_bytes()?)
    }

    // pub fn from(transactions: Vec<Transaction>, last_time: i64, ) {
    //     if let Some(txa) = txs {
    //         // TODO: re-query observation edge here.
    //         let leafs = transactions
    //             .clone()
    //             .iter()
    //             .map(|e| e.hash_vec().clone())
    //             .collect_vec();
    //         let mut block = Block {
    //             // TODO: use a real merkle root here
    //             merkle_root: Some(rg_merkle::build_root_simple(&leafs)),
    //             transactions: vec, // TODO: can leave this blank and enforce it properly
    //             // to remove the clone on hash calculation? That's clever do it as part
    //             // of a constructor function.
    //             struct_metadata: struct_metadata(last_time as i64),
    //             previous_block_hash: self.last_block.hash.clone(),
    //             metadata: None,
    //             hash: None,
    //             height: self.last_block.height + 1,
    //         }
    //             .with_hash();
    // }
}

#[test]
fn verify_hash_equivalent_schema_change() {
    let block = Block {
        merkle_root: None,
        transactions: vec![],
        struct_metadata: None,
        previous_block_hash: None,
        metadata: None,
        hash: None,
        height: 0,
    };
    // before adding 'test_field: Option<String>'
    // 1440a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26
    println!("Hash hex empty block: {}", block.hash_hex());
    // same hash after adding test_field: None to schema, verifying we can evolve schema while
    // keeping hashes consistent so long as all fields are None at first.
}