block.rs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. use std::io;
  2. use log::debug;
  3. use super::{Metadata, StreamletMetadata, OuroborosMetadata, BLOCK_VERSION};
  4. use crate::{
  5. crypto::{address::Address, keypair::PublicKey, schnorr::Signature},
  6. impl_vec, net,
  7. tx::Transaction,
  8. util::{
  9. serial::{serialize, Decodable, Encodable, SerialDecodable, SerialEncodable, VarInt},
  10. time::Timestamp,
  11. },
  12. Result,
  13. };
  14. /// This struct represents a tuple of the form (`v`, `st`, `e`, `sl`, `txs`, `metadata`).
  15. /// The transactions here are stored as hashes, which serve as pointers to
  16. /// the actual transaction data in the blockchain database.
  17. #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
  18. pub struct Block {
  19. /// Block version
  20. pub v: u8,
  21. /// Previous block hash
  22. pub st: blake3::Hash,
  23. /// Epoch
  24. pub e: u64,
  25. /// Slot uid
  26. pub sl: u64,
  27. /// Transaction hashes
  28. pub txs: Vec<blake3::Hash>,
  29. /// Additional block information
  30. pub metadata: Metadata,
  31. }
  32. impl Block {
  33. pub fn new(
  34. st: blake3::Hash,
  35. e: u64,
  36. sl: u64,
  37. txs: Vec<blake3::Hash>,
  38. metadata: Metadata,
  39. ) -> Self {
  40. let v = *BLOCK_VERSION;
  41. Self { v, st, e, sl, txs, metadata }
  42. }
  43. /// Generate the genesis block.
  44. pub fn genesis_block(genesis_ts: Timestamp, genesis_data: blake3::Hash, eta: [u8;32]) -> Self {
  45. let metadata =
  46. Metadata::new(genesis_ts, eta);
  47. Self::new(genesis_data, 0, 0, vec![], metadata)
  48. }
  49. /// Calculate the block hash
  50. pub fn blockhash(&self) -> blake3::Hash {
  51. blake3::hash(&serialize(self))
  52. }
  53. }
  54. /// Auxiliary structure used for blockchain syncing.
  55. #[derive(Debug, SerialEncodable, SerialDecodable)]
  56. pub struct BlockOrder {
  57. /// Slot UID
  58. pub sl: u64,
  59. /// Blockhash of that slot
  60. pub block: blake3::Hash,
  61. }
  62. impl net::Message for BlockOrder {
  63. fn name() -> &'static str {
  64. "blockorder"
  65. }
  66. }
  67. /// Structure representing full block data.
  68. #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
  69. pub struct BlockInfo {
  70. /// Block version
  71. pub v: u8,
  72. /// Previous block hash
  73. pub st: blake3::Hash,
  74. /// Epoch
  75. pub e: u64,
  76. /// Slot uid
  77. pub sl: u64,
  78. /// Transactions payload
  79. pub txs: Vec<Transaction>,
  80. /// Additional proposal information
  81. pub metadata: Metadata,
  82. /// Proposal information used by Streamlet consensus
  83. pub sm: StreamletMetadata,
  84. }
  85. impl BlockInfo {
  86. pub fn new(
  87. st: blake3::Hash,
  88. e: u64,
  89. sl: u64,
  90. txs: Vec<Transaction>,
  91. metadata: Metadata,
  92. sm: StreamletMetadata,
  93. ) -> Self {
  94. let v = *BLOCK_VERSION;
  95. Self { v, st, e, sl, txs, metadata, sm }
  96. }
  97. /// Calculate the block hash
  98. pub fn blockhash(&self) -> blake3::Hash {
  99. let block: Block = self.clone().into();
  100. block.blockhash()
  101. }
  102. }
  103. impl From<BlockInfo> for Block {
  104. fn from(b: BlockInfo) -> Self {
  105. let txids = b.txs.iter().map(|x| blake3::hash(&serialize(x))).collect();
  106. Self { v: b.v, st: b.st, e: b.e, sl: b.sl, txs: txids, metadata: b.metadata }
  107. }
  108. }
  109. impl net::Message for BlockInfo {
  110. fn name() -> &'static str {
  111. "blockinfo"
  112. }
  113. }
  114. impl_vec!(BlockInfo);
  115. /// Auxiliary structure used for blockchain syncing
  116. #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
  117. pub struct BlockResponse {
  118. /// Response blocks.
  119. pub blocks: Vec<BlockInfo>,
  120. }
  121. impl net::Message for BlockResponse {
  122. fn name() -> &'static str {
  123. "blockresponse"
  124. }
  125. }
  126. /// This struct represents a block proposal, used for consensus.
  127. #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
  128. pub struct BlockProposal {
  129. /// Leader public key
  130. pub public_key: PublicKey,
  131. /// Block signature
  132. pub signature: Signature,
  133. /// Leader address
  134. pub address: Address,
  135. /// Block data
  136. pub block: BlockInfo,
  137. }
  138. impl BlockProposal {
  139. #[allow(clippy::too_many_arguments)]
  140. pub fn new(
  141. public_key: PublicKey,
  142. signature: Signature,
  143. address: Address,
  144. st: blake3::Hash,
  145. e: u64,
  146. sl: u64,
  147. txs: Vec<Transaction>,
  148. metadata: Metadata,
  149. sm: StreamletMetadata,
  150. ) -> Self {
  151. let block = BlockInfo::new(st, e, sl, txs, metadata, sm);
  152. Self { public_key, signature, address, block }
  153. }
  154. /// Produce proposal hash using `st`, `e`, `sl`, `txs`, and `metadata`.
  155. pub fn hash(&self) -> blake3::Hash {
  156. Self::to_proposal_hash(
  157. self.block.st,
  158. self.block.e,
  159. self.block.sl,
  160. &self.block.txs,
  161. &self.block.metadata,
  162. )
  163. }
  164. /// Generate a proposal hash using provided `st`, `e`, `sl`, `txs`, and `metadata`.
  165. pub fn to_proposal_hash(
  166. st: blake3::Hash,
  167. e: u64,
  168. sl: u64,
  169. transactions: &[Transaction],
  170. metadata: &Metadata,
  171. ) -> blake3::Hash {
  172. let mut txs = Vec::with_capacity(transactions.len());
  173. for tx in transactions {
  174. txs.push(blake3::hash(&serialize(tx)));
  175. }
  176. blake3::hash(&serialize(&Block::new(st, e, sl, txs, metadata.clone())))
  177. }
  178. }
  179. impl PartialEq for BlockProposal {
  180. fn eq(&self, other: &Self) -> bool {
  181. self.public_key == other.public_key &&
  182. self.signature == other.signature &&
  183. self.address == other.address &&
  184. self.block.st == other.block.st &&
  185. self.block.e == other.block.e &&
  186. self.block.sl == other.block.sl &&
  187. self.block.txs == other.block.txs &&
  188. self.block.metadata == other.block.metadata
  189. }
  190. }
  191. impl net::Message for BlockProposal {
  192. fn name() -> &'static str {
  193. "proposal"
  194. }
  195. }
  196. impl_vec!(BlockProposal);
  197. impl From<BlockProposal> for BlockInfo {
  198. fn from(block: BlockProposal) -> BlockInfo {
  199. block.block
  200. }
  201. }
  202. /// This struct represents a sequence of block proposals.
  203. #[derive(Debug, Clone, PartialEq, SerialEncodable, SerialDecodable)]
  204. pub struct ProposalChain {
  205. pub genesis_block: blake3::Hash,
  206. pub proposals: Vec<BlockProposal>,
  207. }
  208. impl ProposalChain {
  209. pub fn new(genesis_block: blake3::Hash, initial_proposal: BlockProposal) -> Self {
  210. Self { genesis_block, proposals: vec![initial_proposal] }
  211. }
  212. /// A proposal is considered valid when its parent hash is equal to the
  213. /// hash of the previous proposal and their slots are incremental,
  214. /// excluding the genesis block proposal.
  215. /// Additional validity rules can be applied.
  216. pub fn check_proposal(&self, proposal: &BlockProposal, previous: &BlockProposal) -> bool {
  217. if proposal.block.st == self.genesis_block {
  218. debug!("check_proposal(): Genesis block proposal provided.");
  219. return false
  220. }
  221. let prev_hash = previous.hash();
  222. if proposal.block.st != prev_hash || proposal.block.sl <= previous.block.sl {
  223. debug!("check_proposal(): Provided proposal is invalid.");
  224. return false
  225. }
  226. true
  227. }
  228. /// A proposals chain is considered valid when every proposal is valid,
  229. /// based on the `check_proposal` function.
  230. pub fn check_chain(&self) -> bool {
  231. for (index, proposal) in self.proposals[1..].iter().enumerate() {
  232. if !self.check_proposal(proposal, &self.proposals[index]) {
  233. return false
  234. }
  235. }
  236. true
  237. }
  238. /// Insertion of a valid proposal.
  239. pub fn add(&mut self, proposal: &BlockProposal) {
  240. if self.check_proposal(proposal, self.proposals.last().unwrap()) {
  241. self.proposals.push(proposal.clone());
  242. }
  243. }
  244. /// Proposals chain notarization check.
  245. pub fn notarized(&self) -> bool {
  246. for proposal in &self.proposals {
  247. if !proposal.block.sm.notarized {
  248. return false
  249. }
  250. }
  251. true
  252. }
  253. }
  254. impl_vec!(ProposalChain);