1use crate::{
2 crypto::{
3 ecdsa::{ECDSASigner, ECDSAVerifier},
4 KeyType, PrivateKey, PublicKey, Signature,
5 },
6 error::{ChaincraftError, Result},
7 shared::{MessageType, SharedMessage, SharedObjectId},
8 shared_object::ApplicationObject,
9};
10use async_trait::async_trait;
11use chrono::{DateTime, Utc};
12use serde::{Deserialize, Serialize};
13use sha2::{Digest, Sha256};
14use std::collections::{HashMap, HashSet};
15
16#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
18pub enum TendermintMessageType {
19 Proposal {
20 height: u64,
21 round: u32,
22 block_hash: String,
23 proposer: String,
24 timestamp: DateTime<Utc>,
25 signature: String,
26 },
27 Prevote {
28 height: u64,
29 round: u32,
30 block_hash: Option<String>, validator: String,
32 signature: String,
33 },
34 Precommit {
35 height: u64,
36 round: u32,
37 block_hash: Option<String>, validator: String,
39 signature: String,
40 },
41 ValidatorSet {
42 validators: Vec<ValidatorInfo>,
43 height: u64,
44 },
45 BlockCommit {
46 height: u64,
47 block_hash: String,
48 commit_signatures: Vec<String>,
49 timestamp: DateTime<Utc>,
50 },
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
55pub struct ValidatorInfo {
56 pub address: String,
57 pub public_key: String,
58 pub voting_power: u64,
59 pub active: bool,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct Block {
65 pub height: u64,
66 pub hash: String,
67 pub previous_hash: String,
68 pub timestamp: DateTime<Utc>,
69 pub proposer: String,
70 pub transactions: Vec<serde_json::Value>,
71 pub commit_signatures: Vec<String>,
72}
73
74#[derive(Debug, Clone, PartialEq)]
76pub enum ConsensusState {
77 Propose,
78 Prevote,
79 Precommit,
80 Commit,
81}
82
83#[derive(Debug, Clone)]
85pub struct Vote {
86 pub validator: String,
87 pub block_hash: Option<String>,
88 pub signature: String,
89 pub timestamp: DateTime<Utc>,
90}
91
92#[derive(Debug)]
94pub struct TendermintObject {
95 pub id: SharedObjectId,
96 pub validators: HashMap<String, ValidatorInfo>,
97 pub blocks: Vec<Block>,
98 pub current_height: u64,
99 pub current_round: u32,
100 pub state: ConsensusState,
101 pub proposals: HashMap<(u64, u32), TendermintMessageType>,
102 pub prevotes: HashMap<(u64, u32), HashMap<String, Vote>>,
103 pub precommits: HashMap<(u64, u32), HashMap<String, Vote>>,
104 pub locked_block: Option<String>,
105 pub locked_round: Option<u32>,
106 pub my_validator_address: String,
107 pub signer: ECDSASigner,
108 pub verifier: ECDSAVerifier,
109 pub messages: Vec<TendermintMessageType>,
110}
111
112impl TendermintObject {
113 pub fn new() -> Result<Self> {
114 let signer = ECDSASigner::new()?;
115 let my_validator_address = signer.get_public_key_pem()?;
116
117 let genesis_block = Block {
119 height: 0,
120 hash: "genesis_hash".to_string(),
121 previous_hash: "".to_string(),
122 timestamp: Utc::now(),
123 proposer: "genesis".to_string(),
124 transactions: vec![],
125 commit_signatures: vec![],
126 };
127
128 Ok(Self {
129 id: SharedObjectId::new(),
130 validators: HashMap::new(),
131 blocks: vec![genesis_block],
132 current_height: 1,
133 current_round: 0,
134 state: ConsensusState::Propose,
135 proposals: HashMap::new(),
136 prevotes: HashMap::new(),
137 precommits: HashMap::new(),
138 locked_block: None,
139 locked_round: None,
140 my_validator_address,
141 signer,
142 verifier: ECDSAVerifier::new(),
143 messages: Vec::new(),
144 })
145 }
146
147 pub fn add_validator(&mut self, address: String, public_key: String, voting_power: u64) {
149 let validator = ValidatorInfo {
150 address: address.clone(),
151 public_key,
152 voting_power,
153 active: true,
154 };
155 self.validators.insert(address, validator);
156 }
157
158 pub fn total_voting_power(&self) -> u64 {
160 self.validators
161 .values()
162 .filter(|v| v.active)
163 .map(|v| v.voting_power)
164 .sum()
165 }
166
167 pub fn has_majority(&self, voting_power: u64) -> bool {
169 voting_power * 3 > self.total_voting_power() * 2
170 }
171
172 pub fn process_proposal(&mut self, proposal: TendermintMessageType) -> Result<bool> {
174 if let TendermintMessageType::Proposal { height, round, .. } = &proposal {
175 if *height == self.current_height && *round == self.current_round {
176 self.proposals.insert((*height, *round), proposal.clone());
177 self.messages.push(proposal);
178 return Ok(true);
179 }
180 }
181 Ok(false)
182 }
183
184 pub fn process_prevote(&mut self, prevote: TendermintMessageType) -> Result<bool> {
186 if let TendermintMessageType::Prevote {
187 height,
188 round,
189 block_hash,
190 validator,
191 signature,
192 } = &prevote
193 {
194 if *height == self.current_height && *round == self.current_round {
195 let vote = Vote {
196 validator: validator.clone(),
197 block_hash: block_hash.clone(),
198 signature: signature.clone(),
199 timestamp: Utc::now(),
200 };
201
202 self.prevotes
203 .entry((*height, *round))
204 .or_default()
205 .insert(validator.clone(), vote);
206
207 self.messages.push(prevote);
208 return Ok(true);
209 }
210 }
211 Ok(false)
212 }
213
214 pub fn process_precommit(&mut self, precommit: TendermintMessageType) -> Result<bool> {
216 if let TendermintMessageType::Precommit {
217 height,
218 round,
219 block_hash,
220 validator,
221 signature,
222 } = &precommit
223 {
224 if *height == self.current_height && *round == self.current_round {
225 let vote = Vote {
226 validator: validator.clone(),
227 block_hash: block_hash.clone(),
228 signature: signature.clone(),
229 timestamp: Utc::now(),
230 };
231
232 self.precommits
233 .entry((*height, *round))
234 .or_default()
235 .insert(validator.clone(), vote);
236
237 self.messages.push(precommit);
238 return Ok(true);
239 }
240 }
241 Ok(false)
242 }
243
244 pub fn can_commit(&self) -> Option<String> {
246 if let Some(precommits) = self
247 .precommits
248 .get(&(self.current_height, self.current_round))
249 {
250 let mut vote_counts: HashMap<Option<String>, u64> = HashMap::new();
251
252 for vote in precommits.values() {
253 if let Some(validator) = self.validators.get(&vote.validator) {
254 *vote_counts.entry(vote.block_hash.clone()).or_insert(0) +=
255 validator.voting_power;
256 }
257 }
258
259 for (block_hash, voting_power) in vote_counts {
260 if let Some(hash) = block_hash {
261 if self.has_majority(voting_power) {
262 return Some(hash);
263 }
264 }
265 }
266 }
267 None
268 }
269
270 pub fn commit_block(&mut self, block_hash: String) -> Result<()> {
272 let block = Block {
273 height: self.current_height,
274 hash: block_hash.clone(),
275 previous_hash: self.blocks.last().unwrap().hash.clone(),
276 timestamp: Utc::now(),
277 proposer: self.my_validator_address.clone(),
278 transactions: vec![], commit_signatures: self
280 .precommits
281 .get(&(self.current_height, self.current_round))
282 .map(|votes| votes.values().map(|v| v.signature.clone()).collect())
283 .unwrap_or_default(),
284 };
285
286 self.blocks.push(block);
287 self.current_height += 1;
288 self.current_round = 0;
289 self.state = ConsensusState::Propose;
290 self.locked_block = None;
291 self.locked_round = None;
292
293 self.prevotes.retain(|&(h, _), _| h >= self.current_height);
295 self.precommits
296 .retain(|&(h, _), _| h >= self.current_height);
297 self.proposals.retain(|&(h, _), _| h >= self.current_height);
298
299 Ok(())
300 }
301
302 pub fn create_proposal(
304 &self,
305 transactions: Vec<serde_json::Value>,
306 ) -> Result<TendermintMessageType> {
307 let block_data = serde_json::json!({
308 "height": self.current_height,
309 "round": self.current_round,
310 "previous_hash": self.blocks.last().unwrap().hash,
311 "transactions": transactions,
312 "timestamp": Utc::now().to_rfc3339()
313 });
314
315 let block_hash = format!("{:x}", sha2::Sha256::digest(block_data.to_string().as_bytes()));
316 let signature_data =
317 format!("proposal:{}:{}:{}", self.current_height, self.current_round, block_hash);
318 let signature = self.signer.sign(signature_data.as_bytes())?;
319
320 Ok(TendermintMessageType::Proposal {
321 height: self.current_height,
322 round: self.current_round,
323 block_hash,
324 proposer: self.my_validator_address.clone(),
325 timestamp: Utc::now(),
326 signature: hex::encode(signature.to_bytes()),
327 })
328 }
329
330 pub fn get_consensus_info(&self) -> serde_json::Value {
332 serde_json::json!({
333 "height": self.current_height,
334 "round": self.current_round,
335 "state": format!("{:?}", self.state),
336 "validators_count": self.validators.len(),
337 "blocks_count": self.blocks.len(),
338 "locked_block": self.locked_block,
339 "locked_round": self.locked_round,
340 "total_voting_power": self.total_voting_power()
341 })
342 }
343
344 pub fn get_voting_stats(&self) -> serde_json::Value {
346 let prevote_count = self
347 .prevotes
348 .get(&(self.current_height, self.current_round))
349 .map(|votes| votes.len())
350 .unwrap_or(0);
351 let precommit_count = self
352 .precommits
353 .get(&(self.current_height, self.current_round))
354 .map(|votes| votes.len())
355 .unwrap_or(0);
356
357 serde_json::json!({
358 "height": self.current_height,
359 "round": self.current_round,
360 "prevotes": prevote_count,
361 "precommits": precommit_count,
362 "has_proposal": self.proposals.contains_key(&(self.current_height, self.current_round))
363 })
364 }
365}
366
367#[async_trait]
368impl ApplicationObject for TendermintObject {
369 fn id(&self) -> &SharedObjectId {
370 &self.id
371 }
372
373 fn type_name(&self) -> &'static str {
374 "TendermintBFT"
375 }
376
377 async fn is_valid(&self, message: &SharedMessage) -> Result<bool> {
378 let msg_result: std::result::Result<TendermintMessageType, _> =
379 serde_json::from_value(message.data.clone());
380 Ok(msg_result.is_ok())
381 }
382
383 async fn add_message(&mut self, message: SharedMessage) -> Result<()> {
384 let tendermint_msg: TendermintMessageType = serde_json::from_value(message.data.clone())
385 .map_err(|e| {
386 ChaincraftError::Serialization(crate::error::SerializationError::Json(e))
387 })?;
388
389 let processed = match &tendermint_msg {
390 TendermintMessageType::Proposal { .. } => {
391 self.process_proposal(tendermint_msg.clone())?
392 },
393 TendermintMessageType::Prevote { .. } => {
394 self.process_prevote(tendermint_msg.clone())?
395 },
396 TendermintMessageType::Precommit { .. } => {
397 self.process_precommit(tendermint_msg.clone())?
398 },
399 TendermintMessageType::ValidatorSet { validators, .. } => {
400 for validator in validators {
401 self.add_validator(
402 validator.address.clone(),
403 validator.public_key.clone(),
404 validator.voting_power,
405 );
406 }
407 true
408 },
409 TendermintMessageType::BlockCommit { block_hash, .. } => {
410 self.commit_block(block_hash.clone())?;
411 true
412 },
413 };
414
415 if processed {
416 tracing::debug!("Successfully processed Tendermint message: {:?}", tendermint_msg);
417
418 if let Some(commit_hash) = self.can_commit() {
420 self.commit_block(commit_hash)?;
421 }
422 }
423
424 Ok(())
425 }
426
427 fn is_merkleized(&self) -> bool {
428 false
429 }
430
431 async fn get_latest_digest(&self) -> Result<String> {
432 Ok(format!("{}:{}", self.current_height, self.current_round))
433 }
434
435 async fn has_digest(&self, digest: &str) -> Result<bool> {
436 let current_digest = format!("{}:{}", self.current_height, self.current_round);
437 Ok(digest == current_digest)
438 }
439
440 async fn is_valid_digest(&self, _digest: &str) -> Result<bool> {
441 Ok(true)
442 }
443
444 async fn add_digest(&mut self, _digest: String) -> Result<bool> {
445 Ok(true)
446 }
447
448 async fn gossip_messages(&self, _digest: Option<&str>) -> Result<Vec<SharedMessage>> {
449 Ok(Vec::new())
450 }
451
452 async fn get_messages_since_digest(&self, _digest: &str) -> Result<Vec<SharedMessage>> {
453 Ok(Vec::new())
454 }
455
456 async fn get_state(&self) -> Result<serde_json::Value> {
457 Ok(serde_json::json!({
458 "type": "TendermintBFT",
459 "height": self.current_height,
460 "round": self.current_round,
461 "state": format!("{:?}", self.state),
462 "validators": self.validators.len(),
463 "blocks": self.blocks.len(),
464 "messages": self.messages.len(),
465 "consensus_info": self.get_consensus_info(),
466 "voting_stats": self.get_voting_stats()
467 }))
468 }
469
470 async fn reset(&mut self) -> Result<()> {
471 self.current_height = 1;
472 self.current_round = 0;
473 self.state = ConsensusState::Propose;
474 self.proposals.clear();
475 self.prevotes.clear();
476 self.precommits.clear();
477 self.locked_block = None;
478 self.locked_round = None;
479 self.messages.clear();
480 Ok(())
481 }
482
483 fn clone_box(&self) -> Box<dyn ApplicationObject> {
484 let new_obj = TendermintObject::new().unwrap_or_else(|_| {
486 let signer = ECDSASigner::new().unwrap();
488 let my_validator_address = signer.get_public_key_pem().unwrap();
489 TendermintObject {
490 id: SharedObjectId::new(),
491 validators: HashMap::new(),
492 blocks: vec![],
493 current_height: 1,
494 current_round: 0,
495 state: ConsensusState::Propose,
496 proposals: HashMap::new(),
497 prevotes: HashMap::new(),
498 precommits: HashMap::new(),
499 locked_block: None,
500 locked_round: None,
501 my_validator_address,
502 signer,
503 verifier: ECDSAVerifier::new(),
504 messages: Vec::new(),
505 }
506 });
507 Box::new(new_obj)
508 }
509
510 fn as_any(&self) -> &dyn std::any::Any {
511 self
512 }
513
514 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
515 self
516 }
517}
518
519pub mod helpers {
521 use super::*;
522
523 pub fn create_validator_set_message(
524 validators: Vec<ValidatorInfo>,
525 height: u64,
526 ) -> Result<serde_json::Value> {
527 let validator_msg = TendermintMessageType::ValidatorSet { validators, height };
528 serde_json::to_value(validator_msg)
529 .map_err(|e| ChaincraftError::Serialization(crate::error::SerializationError::Json(e)))
530 }
531
532 pub fn create_proposal_message(
533 height: u64,
534 round: u32,
535 block_hash: String,
536 proposer: String,
537 signer: &ECDSASigner,
538 ) -> Result<serde_json::Value> {
539 let signature_data = format!("proposal:{}:{}:{}", height, round, block_hash);
540 let signature = signer.sign(signature_data.as_bytes())?;
541
542 let proposal = TendermintMessageType::Proposal {
543 height,
544 round,
545 block_hash,
546 proposer,
547 timestamp: Utc::now(),
548 signature: hex::encode(signature.to_bytes()),
549 };
550
551 serde_json::to_value(proposal)
552 .map_err(|e| ChaincraftError::Serialization(crate::error::SerializationError::Json(e)))
553 }
554
555 pub fn create_prevote_message(
556 height: u64,
557 round: u32,
558 block_hash: Option<String>,
559 validator: String,
560 signer: &ECDSASigner,
561 ) -> Result<serde_json::Value> {
562 let signature_data = format!("prevote:{}:{}:{:?}", height, round, block_hash);
563 let signature = signer.sign(signature_data.as_bytes())?;
564
565 let prevote = TendermintMessageType::Prevote {
566 height,
567 round,
568 block_hash,
569 validator,
570 signature: hex::encode(signature.to_bytes()),
571 };
572
573 serde_json::to_value(prevote)
574 .map_err(|e| ChaincraftError::Serialization(crate::error::SerializationError::Json(e)))
575 }
576
577 pub fn create_precommit_message(
578 height: u64,
579 round: u32,
580 block_hash: Option<String>,
581 validator: String,
582 signer: &ECDSASigner,
583 ) -> Result<serde_json::Value> {
584 let signature_data = format!("precommit:{}:{}:{:?}", height, round, block_hash);
585 let signature = signer.sign(signature_data.as_bytes())?;
586
587 let precommit = TendermintMessageType::Precommit {
588 height,
589 round,
590 block_hash,
591 validator,
592 signature: hex::encode(signature.to_bytes()),
593 };
594
595 serde_json::to_value(precommit)
596 .map_err(|e| ChaincraftError::Serialization(crate::error::SerializationError::Json(e)))
597 }
598}