monero_serai/
block.rs

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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use std_shims::{
  vec,
  vec::Vec,
  io::{self, Read, Write},
};

use crate::{
  io::*,
  primitives::keccak256,
  merkle::merkle_root,
  transaction::{Input, Transaction},
};

const CORRECT_BLOCK_HASH_202612: [u8; 32] =
  hex_literal::hex!("426d16cff04c71f8b16340b722dc4010a2dd3831c22041431f772547ba6e331a");
const EXISTING_BLOCK_HASH_202612: [u8; 32] =
  hex_literal::hex!("bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698");

/// A Monero block's header.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct BlockHeader {
  /// The hard fork of the protocol this block follows.
  ///
  /// Per the C++ codebase, this is the `major_version`.
  pub hardfork_version: u8,
  /// A signal for a proposed hard fork.
  ///
  /// Per the C++ codebase, this is the `minor_version`.
  pub hardfork_signal: u8,
  /// Seconds since the epoch.
  pub timestamp: u64,
  /// The previous block's hash.
  pub previous: [u8; 32],
  /// The nonce used to mine the block.
  ///
  /// Miners should increment this while attempting to find a block with a hash satisfying the PoW
  /// rules.
  pub nonce: u32,
}

impl BlockHeader {
  /// Write the BlockHeader.
  pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
    write_varint(&self.hardfork_version, w)?;
    write_varint(&self.hardfork_signal, w)?;
    write_varint(&self.timestamp, w)?;
    w.write_all(&self.previous)?;
    w.write_all(&self.nonce.to_le_bytes())
  }

  /// Serialize the BlockHeader to a `Vec<u8>`.
  pub fn serialize(&self) -> Vec<u8> {
    let mut serialized = vec![];
    self.write(&mut serialized).unwrap();
    serialized
  }

  /// Read a BlockHeader.
  pub fn read<R: Read>(r: &mut R) -> io::Result<BlockHeader> {
    Ok(BlockHeader {
      hardfork_version: read_varint(r)?,
      hardfork_signal: read_varint(r)?,
      timestamp: read_varint(r)?,
      previous: read_bytes(r)?,
      nonce: read_bytes(r).map(u32::from_le_bytes)?,
    })
  }
}

/// A Monero block.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Block {
  /// The block's header.
  pub header: BlockHeader,
  /// The miner's transaction.
  pub miner_transaction: Transaction,
  /// The transactions within this block.
  pub transactions: Vec<[u8; 32]>,
}

impl Block {
  /// The zero-index position of this block within the blockchain.
  ///
  /// This information comes from the Block's miner transaction. If the miner transaction isn't
  /// structed as expected, this will return None.
  pub fn number(&self) -> Option<usize> {
    match &self.miner_transaction {
      Transaction::V1 { prefix, .. } | Transaction::V2 { prefix, .. } => {
        match prefix.inputs.first() {
          Some(Input::Gen(number)) => Some(*number),
          _ => None,
        }
      }
    }
  }

  /// Write the Block.
  pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
    self.header.write(w)?;
    self.miner_transaction.write(w)?;
    write_varint(&self.transactions.len(), w)?;
    for tx in &self.transactions {
      w.write_all(tx)?;
    }
    Ok(())
  }

  /// Serialize the Block to a `Vec<u8>`.
  pub fn serialize(&self) -> Vec<u8> {
    let mut serialized = vec![];
    self.write(&mut serialized).unwrap();
    serialized
  }

  /// Serialize the block as required for the proof of work hash.
  ///
  /// This is distinct from the serialization required for the block hash. To get the block hash,
  /// use the [`Block::hash`] function.
  pub fn serialize_pow_hash(&self) -> Vec<u8> {
    let mut blob = self.header.serialize();
    blob.extend_from_slice(&merkle_root(self.miner_transaction.hash(), &self.transactions));
    write_varint(&(1 + u64::try_from(self.transactions.len()).unwrap()), &mut blob).unwrap();
    blob
  }

  /// Get the hash of this block.
  pub fn hash(&self) -> [u8; 32] {
    let mut hashable = self.serialize_pow_hash();
    // Monero pre-appends a VarInt of the block-to-hash'ss length before getting the block hash,
    // but doesn't do this when getting the proof of work hash :)
    let mut hashing_blob = Vec::with_capacity(9 + hashable.len());
    write_varint(&u64::try_from(hashable.len()).unwrap(), &mut hashing_blob).unwrap();
    hashing_blob.append(&mut hashable);

    let hash = keccak256(hashing_blob);
    if hash == CORRECT_BLOCK_HASH_202612 {
      return EXISTING_BLOCK_HASH_202612;
    };
    hash
  }

  /// Read a Block.
  pub fn read<R: Read>(r: &mut R) -> io::Result<Block> {
    Ok(Block {
      header: BlockHeader::read(r)?,
      miner_transaction: Transaction::read(r)?,
      transactions: (0_usize .. read_varint(r)?)
        .map(|_| read_bytes(r))
        .collect::<Result<_, _>>()?,
    })
  }
}