1use crate::reserved::ReservedState;
2use crate::*;
3use std::collections::HashMap;
4use std::collections::HashSet;
5use thiserror::Error;
6
7#[derive(Error, Debug, Clone)]
8pub enum Error {
9 #[error("invalid argument: {0}")]
10 InvalidArgument(String),
11 #[error("invalid proof: {0}")]
12 InvalidProof(String),
13 #[error("crypto error: {0}")]
14 CryptoError(String, CryptoError),
15 #[error("invalid commit: applied {0} commit cannot be applied at {1} phase")]
16 PhaseMismatch(String, String),
17}
18
19pub fn verify_header_to_header(h1: &BlockHeader, h2: &BlockHeader) -> Result<(), Error> {
26 if h2.height != h1.height + 1 {
27 return Err(Error::InvalidArgument(format!(
28 "invalid height: expected {}, got {}",
29 h1.height + 1,
30 h2.height
31 )));
32 }
33 if h2.previous_hash != h1.to_hash256() {
34 return Err(Error::InvalidArgument(format!(
35 "invalid previous hash: expected {}, got {}",
36 h1.to_hash256(),
37 h2.previous_hash
38 )));
39 }
40 if !h1
41 .validator_set
42 .iter()
43 .any(|(public_key, _)| public_key == &h2.author)
44 {
45 return Err(Error::InvalidArgument(format!(
46 "invalid author: {} is not in the validator set",
47 h2.author
48 )));
49 }
50 if h2.timestamp < h1.timestamp {
51 return Err(Error::InvalidArgument(format!(
52 "invalid timestamp: expected larger than or equal to {}, got {}",
53 h1.timestamp, h2.timestamp
54 )));
55 }
56 verify_finalization_proof(h1, &h2.prev_block_finalization_proof)?;
57 Ok(())
58}
59
60pub fn verify_finalization_proof(
62 header: &BlockHeader,
63 block_finalization_proof: &FinalizationProof,
64) -> Result<(), Error> {
65 let total_voting_power: VotingPower = header.validator_set.iter().map(|(_, v)| v).sum();
66 let mut voted_validators = HashSet::new();
67 for signature in &block_finalization_proof.signatures {
68 signature
69 .verify(&FinalizationSignTarget {
70 block_hash: header.to_hash256(),
71 round: block_finalization_proof.round,
72 })
73 .map_err(|e| Error::CryptoError("invalid finalization proof".to_string(), e))?;
74 voted_validators.insert(signature.signer());
75 }
76 let voted_voting_power: VotingPower = header
77 .validator_set
78 .iter()
79 .filter(|(v, _)| voted_validators.contains(v))
80 .map(|(_, power)| power)
81 .sum();
82 if voted_voting_power * 3 <= total_voting_power * 2 {
83 return Err(Error::InvalidProof(format!(
84 "invalid finalization proof - voted voting power is too low: {voted_voting_power} / {total_voting_power}"
85 )));
86 }
87 Ok(())
88}
89
90#[derive(Debug, Clone, PartialEq, Eq)]
94enum Phase {
95 Transaction {
98 last_transaction: Transaction,
99 preceding_transactions: Vec<Transaction>,
100 },
101 Agenda {
103 agenda: Agenda,
104 },
105 AgendaProof {
107 agenda_proof: AgendaProof,
108 },
109 ExtraAgendaTransaction {
112 last_extra_agenda_timestamp: Timestamp,
113 },
115 Block,
117}
118
119#[derive(Debug, Clone)]
123pub struct CommitSequenceVerifier {
124 header: BlockHeader,
125 phase: Phase,
126 reserved_state: ReservedState,
127 commits_for_next_block: Vec<Commit>,
128 total_commits: Vec<Commit>,
129}
130
131impl CommitSequenceVerifier {
132 pub fn new(start_header: BlockHeader, reserved_state: ReservedState) -> Result<Self, Error> {
134 Ok(Self {
135 header: start_header.clone(),
136 phase: Phase::Block,
137 reserved_state,
138 commits_for_next_block: vec![],
139 total_commits: vec![Commit::Block(start_header)],
140 })
141 }
142
143 pub fn get_header(&self) -> &BlockHeader {
144 &self.header
145 }
146
147 pub fn get_total_commits(&self) -> &[Commit] {
149 &self.total_commits
150 }
151
152 pub fn get_reserved_state(&self) -> &ReservedState {
153 &self.reserved_state
154 }
155
156 pub fn get_block_headers(&self) -> Vec<(BlockHeader, usize)> {
160 self.total_commits
161 .iter()
162 .enumerate()
163 .filter_map(|(i, commit)| match commit {
164 Commit::Block(header) => Some((header.clone(), i)),
165 _ => None,
166 })
167 .collect()
168 }
169
170 pub fn verify_last_header_finalization(&self, proof: &FinalizationProof) -> Result<(), Error> {
175 verify_finalization_proof(&self.header, proof)
176 }
177
178 pub fn verify_reserved_state(&self, rs: &ReservedState) -> Result<(), Error> {
180 if rs.members.len() < 4 {
182 return Err(Error::InvalidArgument(
183 "the number of members is less than 4".to_string(),
184 ));
185 }
186 let valid_leader_candidates: HashSet<&MemberName> = rs
191 .members
192 .iter()
193 .filter(|m| !m.expelled)
194 .map(|m| &m.name)
195 .collect();
196 if !rs
197 .consensus_leader_order
198 .iter()
199 .all(|m| valid_leader_candidates.contains(m))
200 {
201 return Err(Error::InvalidArgument(
202 "Some consensus leaders are not valid candidates".to_string(),
203 ));
204 }
205 if rs
206 .consensus_leader_order
207 .iter()
208 .collect::<HashSet<&MemberName>>()
209 .len()
210 <= 1
211 {
212 return Err(Error::InvalidArgument(
213 "consensus_leader_order should consist of more than 1 unique members".to_string(),
214 ));
215 }
216 if rs.genesis_info != self.reserved_state.genesis_info {
218 return Err(Error::InvalidArgument("genesis_info changes".to_string()));
219 }
220 let mut member_names = HashSet::new();
222 let mut public_keys = HashSet::new();
223 for member in &rs.members {
224 if !member_names.insert(&member.name) {
225 return Err(Error::InvalidArgument(format!(
226 "member name '{}' already exists",
227 member.name
228 )));
229 }
230 if !public_keys.insert(&member.public_key) {
231 return Err(Error::InvalidArgument(format!(
232 "the public key of '{}' already exists",
233 member.name
234 )));
235 }
236 }
237 let member_names: HashSet<String> = rs.members.iter().map(|m| m.name.clone()).collect();
240 for existing_member in &self.reserved_state.members {
241 if !member_names.contains(&existing_member.name) {
242 return Err(Error::InvalidArgument(format!(
243 "{} doesn't not exist in members",
244 &existing_member.name
245 )));
246 }
247 }
248 Ok(())
249 }
250
251 pub fn apply_commit(&mut self, commit: &Commit) -> Result<(), Error> {
253 match (commit, &self.phase) {
254 (Commit::Block(block_header), Phase::AgendaProof { agenda_proof: _ }) => {
255 verify_header_to_header(&self.header, block_header)?;
256 let commit_merkle_root =
258 BlockHeader::calculate_commit_merkle_root(&self.commits_for_next_block);
259 if commit_merkle_root != block_header.commit_merkle_root {
260 return Err(Error::InvalidArgument(format!(
261 "invalid commit merkle root: expected {}, got {}",
262 commit_merkle_root, block_header.commit_merkle_root
263 )));
264 };
265 self.header = block_header.clone();
266 self.phase = Phase::Block;
267 self.commits_for_next_block = vec![];
268 }
269 (
270 Commit::Block(block_header),
271 Phase::ExtraAgendaTransaction {
272 last_extra_agenda_timestamp,
273 },
274 ) => {
275 verify_header_to_header(&self.header, block_header)?;
276 if block_header.timestamp < *last_extra_agenda_timestamp {
278 return Err(Error::InvalidArgument(format!(
279 "invalid block timestamp: expected larger than or equal to the last extra-agenda transaction timestamp {}, got {}",
280 last_extra_agenda_timestamp, block_header.timestamp
281 )));
282 }
283 let commit_merkle_root =
285 BlockHeader::calculate_commit_merkle_root(&self.commits_for_next_block);
286 if commit_merkle_root != block_header.commit_merkle_root {
287 return Err(Error::InvalidArgument(format!(
288 "invalid commit merkle root: expected {}, got {}",
289 commit_merkle_root, block_header.commit_merkle_root
290 )));
291 };
292 self.header = block_header.clone();
293 self.phase = Phase::Block;
294 self.commits_for_next_block = vec![];
295 }
296 (Commit::Transaction(tx), Phase::Block) => {
297 if let Diff::Reserved(rs) = &tx.diff {
299 self.verify_reserved_state(rs)?;
300 self.reserved_state = *rs.clone();
301 } else if let Diff::General(rs, _) = &tx.diff {
302 self.verify_reserved_state(rs)?;
303 self.reserved_state = *rs.clone();
304 }
305 self.phase = Phase::Transaction {
306 last_transaction: tx.clone(),
307 preceding_transactions: vec![],
308 };
309 }
310 (
311 Commit::Transaction(tx),
312 Phase::Transaction {
313 last_transaction,
314 preceding_transactions,
315 },
316 ) => {
317 if tx.timestamp < last_transaction.timestamp {
319 return Err(Error::InvalidArgument(format!(
320 "invalid transaction timestamp: expected larger than or equal to the last transaction timestamp {}, got {}",
321 last_transaction.timestamp, tx.timestamp
322 )));
323 }
324 if let Diff::Reserved(rs) = &tx.diff {
326 self.verify_reserved_state(rs)?;
327 self.reserved_state = *rs.clone();
328 } else if let Diff::General(rs, _) = &tx.diff {
329 self.verify_reserved_state(rs)?;
330 self.reserved_state = *rs.clone();
331 }
332 let mut preceding_transactions = preceding_transactions.clone();
333 preceding_transactions.push(last_transaction.clone());
334 self.phase = Phase::Transaction {
335 last_transaction: tx.clone(),
336 preceding_transactions,
337 };
338 }
339 (Commit::Agenda(agenda), Phase::Block) => {
340 if agenda.height != self.header.height + 1 {
342 return Err(Error::InvalidArgument(format!(
343 "invalid agenda block height: expected {}, got {}",
344 self.header.height + 1,
345 agenda.height
346 )));
347 }
348 if agenda.transactions_hash != Agenda::calculate_transactions_hash(&[]) {
350 return Err(Error::InvalidArgument(format!(
351 "invalid agenda transactions_hash: expected {}, got {}",
352 Agenda::calculate_transactions_hash(&[]),
353 agenda.transactions_hash
354 )));
355 }
356 if agenda.previous_block_hash != self.header.to_hash256() {
358 return Err(Error::InvalidArgument(format!(
359 "invalid agenda previous_block_hash: expected {}, got {}",
360 self.header.to_hash256(),
361 agenda.previous_block_hash
362 )));
363 }
364 self.phase = Phase::Agenda {
365 agenda: agenda.clone(),
366 };
367 }
368 (
369 Commit::Agenda(agenda),
370 Phase::Transaction {
371 last_transaction,
372 preceding_transactions,
373 },
374 ) => {
375 if agenda.height != self.header.height + 1 {
377 return Err(Error::InvalidArgument(format!(
378 "invalid agenda block height: expected {}, got {}",
379 self.header.height + 1,
380 agenda.height
381 )));
382 }
383 if agenda.timestamp < last_transaction.timestamp {
385 return Err(Error::InvalidArgument(
386 format!("invalid agenda timestamp: expected larger than or equal to the last transaction timestamp {}, got {}", last_transaction.timestamp, agenda.timestamp)
387 ));
388 }
389 let transactions = [
391 preceding_transactions.clone(),
392 vec![last_transaction.clone()],
393 ]
394 .concat();
395 if agenda.transactions_hash != Agenda::calculate_transactions_hash(&transactions) {
396 return Err(Error::InvalidArgument(format!(
397 "invalid agenda transactions_hash: expected {}, got {}",
398 Agenda::calculate_transactions_hash(&transactions),
399 agenda.transactions_hash
400 )));
401 }
402 if agenda.previous_block_hash != self.header.to_hash256() {
404 return Err(Error::InvalidArgument(format!(
405 "invalid agenda previous_block_hash: expected {}, got {}",
406 self.header.to_hash256(),
407 agenda.previous_block_hash
408 )));
409 }
410 self.phase = Phase::Agenda {
411 agenda: agenda.clone(),
412 };
413 }
414 (Commit::AgendaProof(agenda_proof), Phase::Agenda { agenda }) => {
415 if agenda_proof.height != self.header.height + 1 {
417 return Err(Error::InvalidArgument(format!(
418 "invalid agenda proof block height: expected {}, got {}",
419 self.header.height + 1,
420 agenda_proof.height
421 )));
422 }
423 if agenda_proof.agenda_hash != agenda.to_hash256() {
425 return Err(Error::InvalidArgument(format!(
426 "invalid agenda proof: invalid agenda hash expected {}, got {}",
427 agenda.to_hash256(),
428 agenda_proof.agenda_hash
429 )));
430 }
431 for signature in agenda_proof.proof.iter() {
433 signature.verify(agenda).map_err(|e| {
434 Error::CryptoError("invalid agenda proof: invalid signature".to_string(), e)
435 })?;
436 }
437 let governance_set = self
439 .reserved_state
440 .get_governance_set()
441 .unwrap()
442 .into_iter()
443 .collect::<HashMap<_, _>>();
444 let total_weight = governance_set.values().sum::<u64>();
445 let signed_weight = agenda_proof
446 .proof
447 .iter()
448 .map(|s| {
449 if let Some(weight) = governance_set.get(s.signer()) {
450 Ok(*weight)
451 } else {
452 Err(Error::InvalidArgument(format!(
453 "invalid agenda proof: invalid signer {}",
454 s.signer()
455 )))
456 }
457 })
458 .collect::<Result<Vec<_>, Error>>()?
459 .iter()
460 .sum::<u64>();
461 if signed_weight * 2 <= total_weight {
462 return Err(Error::InvalidArgument(
463 "invalid agenda proof: insufficient signed weight".to_string(),
464 ));
465 }
466 self.phase = Phase::AgendaProof {
467 agenda_proof: agenda_proof.clone(),
468 };
469 }
470 (Commit::ExtraAgendaTransaction(tx), Phase::AgendaProof { agenda_proof: _ }) => {
471 match tx {
472 ExtraAgendaTransaction::Delegate(tx) => {
473 self.reserved_state.apply_delegate(tx).map_err(|e| {
475 Error::InvalidArgument(format!("invalid delegation: {e}"))
476 })?;
477 self.phase = Phase::ExtraAgendaTransaction {
478 last_extra_agenda_timestamp: tx.data.timestamp,
479 };
480 }
481 ExtraAgendaTransaction::Undelegate(tx) => {
482 self.reserved_state.apply_undelegate(tx).map_err(|e| {
484 Error::InvalidArgument(format!("invalid undelegation: {e}"))
485 })?;
486 self.phase = Phase::ExtraAgendaTransaction {
487 last_extra_agenda_timestamp: tx.data.timestamp,
488 };
489 }
490 ExtraAgendaTransaction::Report(_tx) => unimplemented!(),
491 }
492 }
493 (
494 Commit::ExtraAgendaTransaction(tx),
495 Phase::ExtraAgendaTransaction {
496 last_extra_agenda_timestamp,
497 },
498 ) => {
499 match tx {
500 ExtraAgendaTransaction::Delegate(tx) => {
501 self.reserved_state.apply_delegate(tx).map_err(|e| {
503 Error::InvalidArgument(format!("invalid delegation: {e}"))
504 })?;
505 if tx.data.timestamp < *last_extra_agenda_timestamp {
507 return Err(Error::InvalidArgument(
508 format!("invalid extra-agenda transaction timestamp: expected larger than or equal to the last transaction timestamp {}, got {}", last_extra_agenda_timestamp, tx.data.timestamp)
509 ));
510 }
511 self.phase = Phase::ExtraAgendaTransaction {
512 last_extra_agenda_timestamp: tx.data.timestamp,
513 };
514 }
515 ExtraAgendaTransaction::Undelegate(tx) => {
516 self.reserved_state.apply_undelegate(tx).map_err(|e| {
518 Error::InvalidArgument(format!("invalid undelegation: {e}"))
519 })?;
520 if tx.data.timestamp < *last_extra_agenda_timestamp {
522 return Err(Error::InvalidArgument(
523 format!("invalid extra-agenda transaction timestamp: expected larger than or equal to the last transaction timestamp {}, got {}", last_extra_agenda_timestamp, tx.data.timestamp)
524 ));
525 }
526 self.phase = Phase::ExtraAgendaTransaction {
527 last_extra_agenda_timestamp: tx.data.timestamp,
528 };
529 }
530 ExtraAgendaTransaction::Report(_tx) => unimplemented!(),
531 }
532 }
533 (Commit::ChatLog(_chat_log), _) => unimplemented!(),
534 (commit, phase) => {
535 return Err(Error::PhaseMismatch(
536 format!("{commit:?}"),
537 format!("{phase:?}"),
538 ));
539 }
540 }
541 self.commits_for_next_block.push(commit.clone());
542 self.total_commits.push(commit.clone());
543 Ok(())
544 }
545}
546
547#[cfg(test)]
548mod test {
549 use super::*;
550 use crate::merkle_tree::OneshotMerkleTree;
551 use serde_json::json;
552
553 fn generate_validator_keypair(size: u8) -> Vec<(PublicKey, PrivateKey)> {
554 let mut validator_keypair: Vec<(PublicKey, PrivateKey)> = vec![];
555 for i in 0..size {
556 validator_keypair.push(generate_keypair([i]))
557 }
558 validator_keypair
559 }
560
561 fn generate_block_header(
562 validator_keypair: &[(PublicKey, PrivateKey)],
563 author_index: usize,
564 finalization_proof: FinalizationProof,
565 previous_hash_value: Hash256,
566 block_height: BlockHeight,
567 time: Timestamp,
568 commit_merkle_root_value: Hash256,
569 ) -> BlockHeader {
570 let validator_set: Vec<(PublicKey, u64)> = validator_keypair
571 .iter()
572 .map(|(public_key, _)| (public_key.clone(), 1))
573 .collect();
574 BlockHeader {
575 author: validator_set[author_index].0.clone(),
576 prev_block_finalization_proof: finalization_proof,
577 previous_hash: previous_hash_value,
578 height: block_height,
579 timestamp: time,
580 commit_merkle_root: commit_merkle_root_value,
581 repository_merkle_root: Hash256::zero(),
582 validator_set: validator_set.to_vec(),
583 version: SIMPERBY_CORE_PROTOCOL_VERSION.to_string(),
584 }
585 }
586
587 fn get_members(validator_set: &[(PublicKey, VotingPower)]) -> Vec<Member> {
588 let mut members = vec![];
589 for (i, (public_key, voting_power)) in validator_set.iter().enumerate() {
590 members.push(Member {
591 public_key: public_key.clone(),
592 name: format!("member{i}").to_string(),
593 governance_voting_power: *voting_power,
594 consensus_voting_power: *voting_power,
595 governance_delegatee: None,
596 consensus_delegatee: None,
597 expelled: false,
598 });
599 }
600 members
601 }
602
603 fn generate_reserved_state(
604 validator_keypair: &[(PublicKey, PrivateKey)],
605 author_index: usize,
606 time: Timestamp,
607 ) -> ReservedState {
608 let genesis_header: BlockHeader = BlockHeader {
609 author: validator_keypair[author_index].0.clone(),
610 prev_block_finalization_proof: FinalizationProof::genesis(),
611 previous_hash: Hash256::zero(),
612 height: 0,
613 timestamp: time,
614 commit_merkle_root: OneshotMerkleTree::create(vec![]).root(),
615 repository_merkle_root: Hash256::zero(),
616 validator_set: validator_keypair
617 .iter()
618 .map(|(public_key, _)| (public_key.clone(), 1))
619 .collect(),
620 version: SIMPERBY_CORE_PROTOCOL_VERSION.to_string(),
621 };
622 let members = get_members(&genesis_header.validator_set);
623 let mut consensus_leader_order: Vec<MemberName> =
624 members.iter().map(|member| member.name.clone()).collect();
625 consensus_leader_order.sort();
626 ReservedState {
627 genesis_info: GenesisInfo {
628 header: genesis_header.clone(),
629 genesis_proof: generate_unanimous_finalization_proof(
630 validator_keypair,
631 &genesis_header,
632 0,
633 ),
634 chain_name: "PDAO Chain".to_string(),
635 },
636 members, consensus_leader_order,
638 version: SIMPERBY_CORE_PROTOCOL_VERSION.to_string(),
639 }
640 }
641
642 fn generate_empty_transaction_commit(time: Timestamp) -> Commit {
643 Commit::Transaction(Transaction {
644 author: "doesn't matter".to_owned(),
645 timestamp: time,
646 head: "Test empty commit".to_string(),
647 body: "This is important!".to_string(),
648 diff: Diff::None,
649 })
650 }
651
652 fn generate_non_reserved_diff_transaction_commit(time: Timestamp) -> Commit {
653 Commit::Transaction(Transaction {
654 author: "doesn't matter".to_owned(),
655 timestamp: time,
656 head: "Test non-reserved-diff commit".to_string(),
657 body: serde_spb::to_string(&json!({
658 "type": "transfer-ft",
659 "asset": "ETH",
660 "amount": "0.1",
661 "recipient": "<key:some-addr-in-ethereum>",
662 }))
663 .unwrap(),
664 diff: Diff::NonReserved(Hash256::hash("The actual content of the diff".as_bytes())),
665 })
666 }
667
668 fn generate_reserved_diff_transaction_commit(
669 validator_keypair: &mut Vec<(PublicKey, PrivateKey)>,
670 reserved_state: &mut ReservedState,
671 seed: u8,
672 time: Timestamp,
673 ) -> Commit {
674 validator_keypair.push(generate_keypair([seed]));
676 let new_member_name = format!("member{}", validator_keypair.len() - 1);
677 reserved_state.members.push(Member {
678 public_key: validator_keypair.last().unwrap().0.clone(),
679 name: new_member_name.clone(),
680 governance_voting_power: 1,
681 consensus_voting_power: 1,
682 governance_delegatee: None,
683 consensus_delegatee: None,
684 expelled: false,
685 });
686 reserved_state.consensus_leader_order.push(new_member_name);
687 reserved_state.consensus_leader_order.sort();
688 Commit::Transaction(Transaction {
689 author: "doesn't matter".to_owned(),
690 timestamp: time,
691 head: "Test reserved-diff commit".to_string(),
692 body: String::new(),
693 diff: Diff::Reserved(Box::new(reserved_state.clone())),
694 })
695 }
696
697 fn genearte_general_diff_transaction_commit(
698 validator_keypair: &mut Vec<(PublicKey, PrivateKey)>,
699 reserved_state: &mut ReservedState,
700 seed: u8,
701 time: Timestamp,
702 ) -> Commit {
703 validator_keypair.push(generate_keypair([seed]));
705 let new_member_name = format!("member{}", validator_keypair.len() - 1);
706 reserved_state.members.push(Member {
707 public_key: validator_keypair.last().unwrap().0.clone(),
708 name: new_member_name.clone(),
709 governance_voting_power: 1,
710 consensus_voting_power: 1,
711 governance_delegatee: None,
712 consensus_delegatee: None,
713 expelled: false,
714 });
715 reserved_state.consensus_leader_order.push(new_member_name);
716 reserved_state.consensus_leader_order.sort();
717 Commit::Transaction(Transaction {
718 author: "doesn't matter".to_owned(),
719 timestamp: time,
720 head: "Test non-reserved-diff commit".to_string(),
721 body: serde_spb::to_string(&json!({
722 "type": "transfer-ft",
723 "asset": "ETH",
724 "amount": "0.1",
725 "recipient": "<key:some-addr-in-ethereum>",
726 }))
727 .unwrap(),
728 diff: Diff::General(
729 Box::new(reserved_state.clone()),
730 Hash256::hash("The actual content of the diff".as_bytes()),
731 ),
732 })
733 }
734
735 fn generate_agenda_commit(agenda: &Agenda) -> Commit {
736 Commit::Agenda(agenda.clone())
737 }
738
739 fn generate_agenda_proof_commit(
740 validator_keypair: &[(PublicKey, PrivateKey)],
741 agenda: &Agenda,
742 agenda_hash_value: Hash256,
743 ) -> Commit {
744 let mut agenda_proof: Vec<TypedSignature<Agenda>> = vec![];
745 for (_, private_key) in validator_keypair {
746 agenda_proof.push(TypedSignature::sign(agenda, private_key).unwrap())
747 }
748 Commit::AgendaProof(AgendaProof {
749 agenda_hash: agenda_hash_value,
750 proof: agenda_proof,
751 height: agenda.height,
752 timestamp: 0,
753 })
754 }
755
756 fn generate_delegation_transaction_commit(
757 data: &DelegationTransactionData,
758 proof: TypedSignature<DelegationTransactionData>,
759 ) -> Commit {
760 Commit::ExtraAgendaTransaction(ExtraAgendaTransaction::Delegate(TxDelegate {
761 data: data.clone(),
762 proof,
763 }))
764 }
765
766 fn generate_undelegation_transaction_commit(
767 data: &UndelegationTransactionData,
768 proof: TypedSignature<UndelegationTransactionData>,
769 ) -> Commit {
770 Commit::ExtraAgendaTransaction(ExtraAgendaTransaction::Undelegate(TxUndelegate {
771 data: data.clone(),
772 proof,
773 }))
774 }
775
776 fn generate_unanimous_finalization_proof(
777 validator_keypair: &[(PublicKey, PrivateKey)],
778 header: &BlockHeader,
779 round: ConsensusRound,
780 ) -> FinalizationProof {
781 let mut signatures: Vec<TypedSignature<FinalizationSignTarget>> = vec![];
782 for (_, private_key) in validator_keypair {
783 signatures.push(
784 TypedSignature::sign(
785 &FinalizationSignTarget {
786 round,
787 block_hash: header.to_hash256(),
788 },
789 private_key,
790 )
791 .unwrap(),
792 );
793 }
794 FinalizationProof { round, signatures }
795 }
796
797 fn generate_block_commit(
798 validator_keypair: &[(PublicKey, PrivateKey)],
799 author_index: usize,
800 previous_header: BlockHeader,
801 time: Timestamp,
802 commit_merkle_root_value: Hash256,
803 repository_merkle_root_value: Hash256,
804 ) -> Commit {
805 Commit::Block(BlockHeader {
806 author: validator_keypair[author_index].0.clone(),
807 prev_block_finalization_proof: generate_unanimous_finalization_proof(
808 validator_keypair,
809 &previous_header,
810 0,
811 ),
812 previous_hash: Commit::Block(previous_header.clone()).to_hash256(),
813 height: previous_header.height + 1,
814 timestamp: time,
815 commit_merkle_root: commit_merkle_root_value,
816 repository_merkle_root: repository_merkle_root_value,
817 validator_set: validator_keypair
818 .iter()
819 .map(|(public_key, _)| (public_key.clone(), 1))
820 .collect(),
821 version: SIMPERBY_CORE_PROTOCOL_VERSION.to_string(),
822 })
823 }
824
825 fn setup_test(
826 validator_set_size: u8,
827 ) -> (
828 Vec<(PublicKey, PrivateKey)>,
829 ReservedState,
830 CommitSequenceVerifier,
831 ) {
832 let validator_keypair: Vec<(PublicKey, PrivateKey)> =
833 generate_validator_keypair(validator_set_size);
834 let start_header: BlockHeader = generate_block_header(
835 &validator_keypair,
836 0,
837 FinalizationProof::genesis(),
838 Hash256::zero(),
839 0,
840 0,
841 OneshotMerkleTree::create(vec![]).root(),
842 );
843 let reserved_state: ReservedState = generate_reserved_state(&validator_keypair, 0, 0);
844 let csv: CommitSequenceVerifier =
845 CommitSequenceVerifier::new(start_header, reserved_state.clone()).unwrap();
846 (validator_keypair, reserved_state, csv)
847 }
848
849 fn calculate_agenda_transactions_hash(phase: Phase) -> Hash256 {
850 if let Phase::Transaction {
851 ref last_transaction,
852 ref preceding_transactions,
853 } = phase
854 {
855 Agenda::calculate_transactions_hash(
856 &[
857 preceding_transactions.clone(),
858 vec![last_transaction.clone()],
859 ]
860 .concat(),
861 )
862 } else {
863 Agenda::calculate_transactions_hash(&[])
864 }
865 }
866
867 #[test]
868 fn correct_commit_sequence1() {
870 let (mut validator_keypair, mut reserved_state, mut csv) = setup_test(4);
871 csv.apply_commit(&generate_empty_transaction_commit(1))
873 .unwrap();
874 csv.apply_commit(&generate_non_reserved_diff_transaction_commit(2))
876 .unwrap();
877 csv.apply_commit(&generate_reserved_diff_transaction_commit(
879 &mut validator_keypair,
880 &mut reserved_state,
881 4,
882 3,
883 ))
884 .unwrap();
885 csv.apply_commit(&genearte_general_diff_transaction_commit(
887 &mut validator_keypair,
888 &mut reserved_state,
889 5,
890 4,
891 ))
892 .unwrap();
893 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
895 let agenda: Agenda = Agenda {
896 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
897 timestamp: 5,
898 transactions_hash: agenda_transactions_hash,
899 height: csv.header.height + 1,
900 previous_block_hash: csv.header.to_hash256(),
901 };
902 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
903 csv.apply_commit(&generate_agenda_proof_commit(
905 &validator_keypair,
906 &agenda,
907 agenda.to_hash256(),
908 ))
909 .unwrap();
910 }
911
912 #[test]
913 fn correct_commit_sequence2() {
915 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
916 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
918 let agenda: Agenda = Agenda {
919 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
920 timestamp: 1,
921 transactions_hash: agenda_transactions_hash,
922 height: csv.header.height + 1,
923 previous_block_hash: csv.header.to_hash256(),
924 };
925 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
926 csv.apply_commit(&generate_agenda_proof_commit(
928 &validator_keypair,
929 &agenda,
930 agenda.to_hash256(),
931 ))
932 .unwrap();
933 }
934
935 #[test]
936 fn invalid_block_commit_with_invalid_height() {
938 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
939 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
941 let agenda: Agenda = Agenda {
942 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
943 timestamp: 1,
944 transactions_hash: agenda_transactions_hash,
945 height: csv.header.height + 1,
946 previous_block_hash: csv.header.to_hash256(),
947 };
948 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
949 csv.apply_commit(&generate_agenda_proof_commit(
951 &validator_keypair,
952 &agenda,
953 agenda.to_hash256(),
954 ))
955 .unwrap();
956 csv.apply_commit(&Commit::Block(BlockHeader {
958 author: validator_keypair[0].0.clone(),
959 prev_block_finalization_proof: generate_unanimous_finalization_proof(
960 &validator_keypair,
961 &csv.header,
962 0,
963 ),
964 previous_hash: Commit::Block(csv.header.clone()).to_hash256(),
965 height: csv.header.height + 2,
966 timestamp: 2,
967 commit_merkle_root: BlockHeader::calculate_commit_merkle_root(
968 &csv.commits_for_next_block,
969 ),
970 repository_merkle_root: Hash256::zero(),
971 validator_set: validator_keypair
972 .iter()
973 .map(|(public_key, _)| (public_key.clone(), 1))
974 .collect(),
975 version: SIMPERBY_CORE_PROTOCOL_VERSION.to_string(),
976 }))
977 .unwrap_err();
978 }
979
980 #[test]
981 fn invalid_block_commit_with_invalid_previous_hash() {
983 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
984 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
986 let agenda: Agenda = Agenda {
987 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
988 timestamp: 1,
989 transactions_hash: agenda_transactions_hash,
990 height: csv.header.height + 1,
991 previous_block_hash: csv.header.to_hash256(),
992 };
993 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
994 csv.apply_commit(&generate_agenda_proof_commit(
996 &validator_keypair,
997 &agenda,
998 agenda.to_hash256(),
999 ))
1000 .unwrap();
1001 csv.apply_commit(&Commit::Block(BlockHeader {
1003 author: validator_keypair[0].0.clone(),
1004 prev_block_finalization_proof: generate_unanimous_finalization_proof(
1005 &validator_keypair,
1006 &csv.header,
1007 0,
1008 ),
1009 previous_hash: Hash256::zero(),
1010 height: csv.header.height + 1,
1011 timestamp: 2,
1012 commit_merkle_root: BlockHeader::calculate_commit_merkle_root(
1013 &csv.commits_for_next_block,
1014 ),
1015 repository_merkle_root: Hash256::zero(),
1016 validator_set: validator_keypair
1017 .iter()
1018 .map(|(public_key, _)| (public_key.clone(), 1))
1019 .collect(),
1020 version: SIMPERBY_CORE_PROTOCOL_VERSION.to_string(),
1021 }))
1022 .unwrap_err();
1023 }
1024
1025 #[test]
1026 fn invalid_block_commit_with_invalid_author() {
1028 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1029 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
1031 let agenda: Agenda = Agenda {
1032 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1033 timestamp: 1,
1034 transactions_hash: agenda_transactions_hash,
1035 height: csv.header.height + 1,
1036 previous_block_hash: csv.header.to_hash256(),
1037 };
1038 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
1039 csv.apply_commit(&generate_agenda_proof_commit(
1041 &validator_keypair,
1042 &agenda,
1043 agenda.to_hash256(),
1044 ))
1045 .unwrap();
1046 csv.apply_commit(&Commit::Block(BlockHeader {
1048 author: generate_keypair([42]).0,
1049 prev_block_finalization_proof: generate_unanimous_finalization_proof(
1050 &validator_keypair,
1051 &csv.header,
1052 0,
1053 ),
1054 previous_hash: Commit::Block(csv.header.clone()).to_hash256(),
1055 height: csv.header.height + 1,
1056 timestamp: 2,
1057 commit_merkle_root: BlockHeader::calculate_commit_merkle_root(
1058 &csv.commits_for_next_block,
1059 ),
1060 repository_merkle_root: Hash256::zero(),
1061 validator_set: validator_keypair
1062 .iter()
1063 .map(|(public_key, _)| (public_key.clone(), 1))
1064 .collect(),
1065 version: SIMPERBY_CORE_PROTOCOL_VERSION.to_string(),
1066 }))
1067 .unwrap_err();
1068 }
1069
1070 #[test]
1071 fn invalid_block_commit_with_invalid_timestamp() {
1073 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1074 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
1076 let agenda: Agenda = Agenda {
1077 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1078 timestamp: 1,
1079 transactions_hash: agenda_transactions_hash,
1080 height: csv.header.height + 1,
1081 previous_block_hash: csv.header.to_hash256(),
1082 };
1083 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
1084 csv.apply_commit(&generate_agenda_proof_commit(
1086 &validator_keypair,
1087 &agenda,
1088 agenda.to_hash256(),
1089 ))
1090 .unwrap();
1091 csv.apply_commit(&Commit::Block(BlockHeader {
1093 author: validator_keypair[0].0.clone(),
1094 prev_block_finalization_proof: generate_unanimous_finalization_proof(
1095 &validator_keypair,
1096 &csv.header,
1097 0,
1098 ),
1099 previous_hash: Commit::Block(csv.header.clone()).to_hash256(),
1100 height: csv.header.height + 1,
1101 timestamp: -1,
1102 commit_merkle_root: BlockHeader::calculate_commit_merkle_root(
1103 &csv.commits_for_next_block,
1104 ),
1105 repository_merkle_root: Hash256::zero(),
1106 validator_set: validator_keypair
1107 .iter()
1108 .map(|(public_key, _)| (public_key.clone(), 1))
1109 .collect(),
1110 version: SIMPERBY_CORE_PROTOCOL_VERSION.to_string(),
1111 }))
1112 .unwrap_err();
1113 }
1114
1115 #[test]
1116 fn invalid_block_commit_with_invalid_finalization_proof_for_invalid_signature() {
1118 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1119 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
1121 let agenda: Agenda = Agenda {
1122 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1123 timestamp: 1,
1124 transactions_hash: agenda_transactions_hash,
1125 height: csv.header.height + 1,
1126 previous_block_hash: csv.header.to_hash256(),
1127 };
1128 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
1129 csv.apply_commit(&generate_agenda_proof_commit(
1131 &validator_keypair,
1132 &agenda,
1133 agenda.to_hash256(),
1134 ))
1135 .unwrap();
1136 csv.apply_commit(&Commit::Block(generate_block_header(
1138 &validator_keypair,
1139 0,
1140 generate_unanimous_finalization_proof(
1141 &validator_keypair,
1142 &generate_block_header(
1143 &validator_keypair[1..],
1144 0,
1145 FinalizationProof::genesis(),
1146 csv.header.to_hash256(),
1147 csv.header.height + 1,
1148 2,
1149 OneshotMerkleTree::create(vec![]).root(),
1150 ),
1151 0,
1152 ),
1153 csv.header.to_hash256(),
1154 csv.header.height + 1,
1155 2,
1156 OneshotMerkleTree::create(vec![]).root(),
1157 )))
1158 .unwrap_err();
1159 }
1160
1161 #[test]
1162 fn invalid_block_commit_with_invalid_finalization_proof_for_low_voting_power() {
1164 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1165 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
1167 let agenda: Agenda = Agenda {
1168 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1169 timestamp: 1,
1170 transactions_hash: agenda_transactions_hash,
1171 height: csv.header.height + 1,
1172 previous_block_hash: csv.header.to_hash256(),
1173 };
1174 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
1175 csv.apply_commit(&generate_agenda_proof_commit(
1177 &validator_keypair,
1178 &agenda,
1179 agenda.to_hash256(),
1180 ))
1181 .unwrap();
1182 csv.apply_commit(&Commit::Block(BlockHeader {
1184 author: validator_keypair[0].0.clone(),
1185 prev_block_finalization_proof: {
1186 let mut proof =
1187 generate_unanimous_finalization_proof(&validator_keypair, &csv.header, 0);
1188 proof.signatures = vec![proof.signatures[0].clone()];
1189 proof
1190 },
1191 previous_hash: Commit::Block(csv.header.clone()).to_hash256(),
1192 height: csv.header.height + 1,
1193 timestamp: 2,
1194 commit_merkle_root: BlockHeader::calculate_commit_merkle_root(
1195 &csv.commits_for_next_block,
1196 ),
1197 repository_merkle_root: Hash256::zero(),
1198 validator_set: validator_keypair
1199 .iter()
1200 .map(|(public_key, _)| (public_key.clone(), 1))
1201 .collect(),
1202 version: SIMPERBY_CORE_PROTOCOL_VERSION.to_string(),
1203 }))
1204 .unwrap_err();
1205 }
1206
1207 #[test]
1208 fn invalid_block_commit_with_invalid_commit_merkle_root() {
1210 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1211 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
1213 let agenda: Agenda = Agenda {
1214 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1215 timestamp: 1,
1216 transactions_hash: agenda_transactions_hash,
1217 height: csv.header.height + 1,
1218 previous_block_hash: csv.header.to_hash256(),
1219 };
1220 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
1221 csv.apply_commit(&generate_agenda_proof_commit(
1223 &validator_keypair,
1224 &agenda,
1225 agenda.to_hash256(),
1226 ))
1227 .unwrap();
1228 csv.apply_commit(&Commit::Block(BlockHeader {
1230 author: validator_keypair[0].0.clone(),
1231 prev_block_finalization_proof: generate_unanimous_finalization_proof(
1232 &validator_keypair,
1233 &csv.header,
1234 0,
1235 ),
1236 previous_hash: Commit::Block(csv.header.clone()).to_hash256(),
1237 height: csv.header.height + 1,
1238 timestamp: 2,
1239 commit_merkle_root: OneshotMerkleTree::create(vec![]).root(),
1240 repository_merkle_root: Hash256::zero(),
1241 validator_set: validator_keypair
1242 .iter()
1243 .map(|(public_key, _)| (public_key.clone(), 1))
1244 .collect(),
1245 version: SIMPERBY_CORE_PROTOCOL_VERSION.to_string(),
1246 }))
1247 .unwrap_err();
1248 }
1249
1250 #[test]
1251 fn phase_mismatch_for_block_commit1() {
1253 let (validator_keypair, _, mut csv) = setup_test(4);
1254 csv.apply_commit(&generate_block_commit(
1256 &validator_keypair,
1257 0,
1258 csv.header.clone(),
1259 1,
1260 OneshotMerkleTree::create(vec![]).root(),
1261 Hash256::zero(),
1262 ))
1263 .unwrap_err();
1264 }
1265
1266 #[test]
1267 fn phase_mismatch_for_block_commit2() {
1269 let (validator_keypair, _, mut csv) = setup_test(4);
1270 csv.apply_commit(&generate_empty_transaction_commit(1))
1272 .unwrap();
1273 csv.apply_commit(&generate_block_commit(
1275 &validator_keypair,
1276 0,
1277 csv.header.clone(),
1278 2,
1279 OneshotMerkleTree::create(vec![]).root(),
1280 Hash256::zero(),
1281 ))
1282 .unwrap_err();
1283 }
1284
1285 #[test]
1286 fn phase_mismatch_for_block_commit3() {
1288 let (mut validator_keypair, mut reserved_state, mut csv) = setup_test(4);
1289 csv.apply_commit(&generate_empty_transaction_commit(1))
1291 .unwrap();
1292 csv.apply_commit(&generate_non_reserved_diff_transaction_commit(2))
1294 .unwrap();
1295 csv.apply_commit(&generate_reserved_diff_transaction_commit(
1297 &mut validator_keypair,
1298 &mut reserved_state,
1299 4,
1300 3,
1301 ))
1302 .unwrap();
1303 csv.apply_commit(&genearte_general_diff_transaction_commit(
1305 &mut validator_keypair,
1306 &mut reserved_state,
1307 5,
1308 4,
1309 ))
1310 .unwrap();
1311 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
1313 let agenda: Agenda = Agenda {
1314 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1315 timestamp: 5,
1316 transactions_hash: agenda_transactions_hash,
1317 height: csv.header.height + 1,
1318 previous_block_hash: csv.header.to_hash256(),
1319 };
1320 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
1321 csv.apply_commit(&generate_block_commit(
1323 &validator_keypair,
1324 0,
1325 csv.header.clone(),
1326 5,
1327 OneshotMerkleTree::create(vec![]).root(),
1328 Hash256::zero(),
1329 ))
1330 .unwrap_err();
1331 }
1332
1333 #[test]
1334 fn phase_mismatch_for_transaction_commit1() {
1336 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1337 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
1339 let agenda: Agenda = Agenda {
1340 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1341 timestamp: 1,
1342 transactions_hash: agenda_transactions_hash,
1343 height: csv.header.height + 1,
1344 previous_block_hash: csv.header.to_hash256(),
1345 };
1346 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
1347 csv.apply_commit(&generate_empty_transaction_commit(2))
1349 .unwrap_err();
1350 }
1351
1352 #[test]
1353 fn phase_mismatch_for_transaction_commit2() {
1355 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1356 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
1358 let agenda: Agenda = Agenda {
1359 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1360 timestamp: 1,
1361 transactions_hash: agenda_transactions_hash,
1362 height: csv.header.height + 1,
1363 previous_block_hash: csv.header.to_hash256(),
1364 };
1365 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
1366 csv.apply_commit(&generate_agenda_proof_commit(
1368 &validator_keypair,
1369 &agenda,
1370 agenda.to_hash256(),
1371 ))
1372 .unwrap();
1373 csv.apply_commit(&generate_empty_transaction_commit(2))
1375 .unwrap_err();
1376 }
1377
1378 #[ignore]
1379 #[test]
1380 fn phase_mismatch_for_transaction_commit3() {
1383 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1384 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
1386 let agenda: Agenda = Agenda {
1387 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1388 timestamp: 1,
1389 transactions_hash: agenda_transactions_hash,
1390 height: csv.header.height + 1,
1391 previous_block_hash: csv.header.to_hash256(),
1392 };
1393 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
1394 csv.apply_commit(&generate_agenda_proof_commit(
1396 &validator_keypair,
1397 &agenda,
1398 agenda.to_hash256(),
1399 ))
1400 .unwrap();
1401 let delegator = reserved_state.members[0].clone();
1404 let delegator_private_key = validator_keypair[0].1.clone();
1405 let delegatee = reserved_state.members[1].clone();
1406 let delegation_transaction_data: DelegationTransactionData = DelegationTransactionData {
1407 delegator: delegator.name,
1408 delegatee: delegatee.name,
1409 governance: true,
1410 block_height: csv.header.height + 1,
1411 timestamp: 2,
1412 chain_name: reserved_state.genesis_info.chain_name,
1413 };
1414 let proof =
1415 TypedSignature::sign(&delegation_transaction_data, &delegator_private_key).unwrap();
1416 csv.apply_commit(&generate_delegation_transaction_commit(
1417 &delegation_transaction_data,
1418 proof,
1419 ))
1420 .unwrap();
1421 csv.apply_commit(&generate_empty_transaction_commit(3))
1423 .unwrap_err();
1424 }
1425
1426 #[test]
1427 fn invalid_agenda_commit_with_invalid_height() {
1430 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1431 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
1433 let agenda: Agenda = Agenda {
1434 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1435 timestamp: 1,
1436 transactions_hash: agenda_transactions_hash,
1437 height: 0,
1438 previous_block_hash: csv.header.to_hash256(),
1439 };
1440 csv.apply_commit(&generate_agenda_commit(&agenda))
1441 .unwrap_err();
1442 }
1443
1444 #[test]
1445 fn invalid_agenda_commit_with_invalid_agenda_hash1() {
1447 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1448 let agenda_transactions_hash = if let Commit::Transaction(transaction) =
1450 generate_empty_transaction_commit(1)
1451 {
1452 Agenda::calculate_transactions_hash(&[transaction])
1453 } else {
1454 panic!("generate_empty_transaction_commit should return Commit::Transaction type value")
1455 };
1456 let agenda: Agenda = Agenda {
1457 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1458 timestamp: 2,
1459 transactions_hash: agenda_transactions_hash,
1460 height: csv.header.height + 1,
1461 previous_block_hash: csv.header.to_hash256(),
1462 };
1463 csv.apply_commit(&generate_agenda_commit(&agenda))
1464 .unwrap_err();
1465 }
1466
1467 #[test]
1468 fn invalid_agenda_commit_with_invalid_agenda_hash2() {
1470 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1471 csv.apply_commit(&generate_empty_transaction_commit(1))
1473 .unwrap();
1474 let agenda_transactions_hash = Agenda::calculate_transactions_hash(&[]);
1476 let agenda: Agenda = Agenda {
1477 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1478 timestamp: 2,
1479 transactions_hash: agenda_transactions_hash,
1480 height: csv.header.height + 1,
1481 previous_block_hash: csv.header.to_hash256(),
1482 };
1483 csv.apply_commit(&generate_agenda_commit(&agenda))
1484 .unwrap_err();
1485 }
1486
1487 #[test]
1488 fn invalid_agenda_commit_with_invalid_timestamp() {
1490 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1491 csv.apply_commit(&generate_empty_transaction_commit(1))
1493 .unwrap();
1494 let agenda: Agenda = Agenda {
1496 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1497 timestamp: 0,
1498 transactions_hash: Agenda::calculate_transactions_hash(&[]),
1499 height: csv.header.height + 1,
1500 previous_block_hash: csv.header.to_hash256(),
1501 };
1502 csv.apply_commit(&generate_agenda_commit(&agenda))
1503 .unwrap_err();
1504 }
1505
1506 #[test]
1507 fn phase_mismatch_for_agenda_commit1() {
1509 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1510 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
1512 let agenda: Agenda = Agenda {
1513 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1514 timestamp: 1,
1515 transactions_hash: agenda_transactions_hash,
1516 height: csv.header.height + 1,
1517 previous_block_hash: csv.header.to_hash256(),
1518 };
1519 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
1520 csv.apply_commit(&generate_agenda_commit(&agenda))
1522 .unwrap_err();
1523 }
1524
1525 #[test]
1526 fn phase_mismatch_for_agenda_commit2() {
1528 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1529 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
1531 let agenda: Agenda = Agenda {
1532 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1533 timestamp: 1,
1534 transactions_hash: agenda_transactions_hash,
1535 height: csv.header.height + 1,
1536 previous_block_hash: csv.header.to_hash256(),
1537 };
1538 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
1539 csv.apply_commit(&generate_agenda_proof_commit(
1541 &validator_keypair,
1542 &agenda,
1543 agenda.to_hash256(),
1544 ))
1545 .unwrap();
1546 csv.apply_commit(&generate_agenda_commit(&agenda))
1548 .unwrap_err();
1549 }
1550
1551 #[ignore]
1552 #[test]
1553 fn phase_mismatch_for_agenda_commit3() {
1555 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1556 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
1558 let agenda: Agenda = Agenda {
1559 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1560 timestamp: 1,
1561 transactions_hash: agenda_transactions_hash,
1562 height: csv.header.height + 1,
1563 previous_block_hash: csv.header.to_hash256(),
1564 };
1565 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
1566 csv.apply_commit(&generate_agenda_proof_commit(
1568 &validator_keypair,
1569 &agenda,
1570 agenda.to_hash256(),
1571 ))
1572 .unwrap();
1573 let delegator = reserved_state.members[0].clone();
1576 let delegator_private_key = validator_keypair[0].1.clone();
1577 let delegatee = reserved_state.members[1].clone();
1578 let delegation_transaction_data: DelegationTransactionData = DelegationTransactionData {
1579 delegator: delegator.name,
1580 delegatee: delegatee.name,
1581 governance: true,
1582 block_height: csv.header.height + 1,
1583 timestamp: 2,
1584 chain_name: reserved_state.genesis_info.chain_name.clone(),
1585 };
1586 let proof =
1587 TypedSignature::sign(&delegation_transaction_data, &delegator_private_key).unwrap();
1588 csv.apply_commit(&generate_delegation_transaction_commit(
1589 &delegation_transaction_data,
1590 proof,
1591 ))
1592 .unwrap();
1593 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
1595 let agenda: Agenda = Agenda {
1596 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1597 timestamp: 3,
1598 transactions_hash: agenda_transactions_hash,
1599 height: csv.header.height + 1,
1600 previous_block_hash: csv.header.to_hash256(),
1601 };
1602 csv.apply_commit(&generate_agenda_commit(&agenda))
1603 .unwrap_err();
1604 }
1605
1606 #[test]
1607 fn invalid_agenda_proof_commit_with_invalid_height() {
1610 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1611 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
1613 let agenda: Agenda = Agenda {
1614 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1615 timestamp: 1,
1616 transactions_hash: agenda_transactions_hash,
1617 height: csv.header.height + 1,
1618 previous_block_hash: csv.header.to_hash256(),
1619 };
1620 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
1621 csv.apply_commit(&generate_agenda_proof_commit(
1623 &validator_keypair,
1624 &Agenda {
1625 author: reserved_state.query_name(&validator_keypair[1].0).unwrap(),
1626 timestamp: 1,
1627 transactions_hash: agenda_transactions_hash,
1628 height: 0,
1629 previous_block_hash: csv.header.to_hash256(),
1630 },
1631 agenda.to_hash256(),
1632 ))
1633 .unwrap_err();
1634 }
1635
1636 #[test]
1637 fn invalid_agenda_proof_with_invalid_agenda_hash() {
1639 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1640 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
1642 let agenda: Agenda = Agenda {
1643 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1644 timestamp: 1,
1645 transactions_hash: agenda_transactions_hash,
1646 height: csv.header.height + 1,
1647 previous_block_hash: csv.header.to_hash256(),
1648 };
1649 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
1650 csv.apply_commit(&generate_agenda_proof_commit(
1652 &validator_keypair,
1653 &agenda,
1654 Hash256::zero(),
1655 ))
1656 .unwrap_err();
1657 }
1658
1659 #[test]
1660 fn invalid_agenda_proof_with_invalid_signature() {
1662 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1663 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
1665 let agenda: Agenda = Agenda {
1666 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1667 timestamp: 1,
1668 transactions_hash: agenda_transactions_hash,
1669 height: csv.header.height + 1,
1670 previous_block_hash: csv.header.to_hash256(),
1671 };
1672 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
1673 csv.apply_commit(&generate_agenda_proof_commit(
1675 &validator_keypair,
1676 &Agenda {
1677 author: reserved_state.query_name(&validator_keypair[1].0).unwrap(),
1678 timestamp: 0,
1679 transactions_hash: Hash256::zero(),
1680 height: csv.header.height + 1,
1681 previous_block_hash: csv.header.to_hash256(),
1682 },
1683 agenda.to_hash256(),
1684 ))
1685 .unwrap_err();
1686 }
1687
1688 #[test]
1689 fn phase_mismatch_for_agenda_proof_commit1() {
1691 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1692 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
1694 let agenda: Agenda = Agenda {
1695 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1696 timestamp: 1,
1697 transactions_hash: agenda_transactions_hash,
1698 height: csv.header.height + 1,
1699 previous_block_hash: csv.header.to_hash256(),
1700 };
1701 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
1702 csv.apply_commit(&generate_agenda_proof_commit(
1704 &validator_keypair,
1705 &agenda,
1706 agenda.to_hash256(),
1707 ))
1708 .unwrap();
1709 csv.apply_commit(&generate_agenda_proof_commit(
1711 &validator_keypair,
1712 &agenda,
1713 agenda.to_hash256(),
1714 ))
1715 .unwrap_err();
1716 }
1717
1718 #[test]
1719 fn phase_mismatch_for_agenda_proof_commit2() {
1721 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1722 csv.apply_commit(&generate_empty_transaction_commit(1))
1724 .unwrap();
1725 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
1727 let agenda: Agenda = Agenda {
1728 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1729 timestamp: 2,
1730 transactions_hash: agenda_transactions_hash,
1731 height: csv.header.height + 1,
1732 previous_block_hash: csv.header.to_hash256(),
1733 };
1734 csv.apply_commit(&generate_agenda_proof_commit(
1735 &validator_keypair,
1736 &agenda,
1737 agenda.to_hash256(),
1738 ))
1739 .unwrap_err();
1740 }
1741
1742 #[test]
1743 fn invalid_reserved_state_with_too_few_members() {
1745 let (_, reserved_state, mut csv) = setup_test(3);
1747 csv.apply_commit(&Commit::Transaction(Transaction {
1749 author: "doesn't matter".to_owned(),
1750 timestamp: 3,
1751 head: "Test reserved-diff commit".to_string(),
1752 body: String::new(),
1753 diff: Diff::Reserved(Box::new(reserved_state)),
1754 }))
1755 .unwrap_err();
1756 }
1757
1758 #[test]
1759 fn invalid_reserved_state_with_consensus_leader_not_in_members() {
1761 let (_, mut reserved_state, mut csv) = setup_test(4);
1762 reserved_state
1764 .consensus_leader_order
1765 .push("stranger".to_string());
1766 csv.apply_commit(&Commit::Transaction(Transaction {
1768 author: "doesn't matter".to_owned(),
1769 timestamp: 3,
1770 head: "Test reserved-diff commit".to_string(),
1771 body: String::new(),
1772 diff: Diff::Reserved(Box::new(reserved_state.clone())),
1773 }))
1774 .unwrap_err();
1775 }
1776
1777 #[test]
1778 fn invalid_reserved_state_with_expelled_member_in_consensus_leader_order() {
1780 let (_, mut reserved_state, mut csv) = setup_test(4);
1781 reserved_state.members[0].expelled = true;
1783 csv.apply_commit(&Commit::Transaction(Transaction {
1785 author: "doesn't matter".to_owned(),
1786 timestamp: 3,
1787 head: "Test reserved-diff commit".to_string(),
1788 body: String::new(),
1789 diff: Diff::Reserved(Box::new(reserved_state)),
1790 }))
1791 .unwrap_err();
1792 }
1793
1794 #[test]
1795 fn invalid_reserved_state_with_changed_genesis_info() {
1797 let (validator_keypair, mut reserved_state, mut csv) = setup_test(4);
1798 let author_index = 1;
1800 let new_genesis_header: BlockHeader = BlockHeader {
1801 author: validator_keypair[author_index].0.clone(),
1802 prev_block_finalization_proof: FinalizationProof::genesis(),
1803 previous_hash: Hash256::zero(),
1804 height: 0,
1805 timestamp: 0,
1806 commit_merkle_root: OneshotMerkleTree::create(vec![]).root(),
1807 repository_merkle_root: Hash256::zero(),
1808 validator_set: validator_keypair
1809 .iter()
1810 .map(|(public_key, _)| (public_key.clone(), 1))
1811 .collect(),
1812 version: SIMPERBY_CORE_PROTOCOL_VERSION.to_string(),
1813 };
1814 reserved_state.genesis_info = GenesisInfo {
1816 header: new_genesis_header.clone(),
1817 genesis_proof: generate_unanimous_finalization_proof(
1818 &validator_keypair,
1819 &new_genesis_header,
1820 0,
1821 ),
1822 chain_name: "PDAO Chain".to_string(),
1823 };
1824 csv.apply_commit(&Commit::Transaction(Transaction {
1826 author: "doesn't matter".to_owned(),
1827 timestamp: 3,
1828 head: "Test reserved-diff commit".to_string(),
1829 body: String::new(),
1830 diff: Diff::Reserved(Box::new(reserved_state)),
1831 }))
1832 .unwrap_err();
1833 }
1834
1835 #[test]
1836 fn invalid_reserved_state_with_duplicate_member_name() {
1838 let (mut validator_keypair, mut reserved_state, mut csv) = setup_test(4);
1839 validator_keypair.push(generate_keypair([4]));
1841 reserved_state.members.push(Member {
1842 public_key: validator_keypair.last().unwrap().0.clone(),
1843 name: "member0".to_string(), governance_voting_power: 1,
1845 consensus_voting_power: 1,
1846 governance_delegatee: None,
1847 consensus_delegatee: None,
1848 expelled: false,
1849 });
1850 csv.apply_commit(&Commit::Transaction(Transaction {
1852 author: "doesn't matter".to_owned(),
1853 timestamp: 3,
1854 head: "Test reserved-diff commit".to_string(),
1855 body: String::new(),
1856 diff: Diff::Reserved(Box::new(reserved_state.clone())),
1857 }))
1858 .unwrap_err();
1859 }
1860
1861 #[test]
1862 fn invalid_reserved_state_with_duplicate_public_key() {
1864 let (validator_keypair, mut reserved_state, mut csv) = setup_test(4);
1865 reserved_state.members.push(Member {
1867 public_key: validator_keypair[0].0.clone(), name: format!("member{}", validator_keypair.len() - 1),
1869 governance_voting_power: 1,
1870 consensus_voting_power: 1,
1871 governance_delegatee: None,
1872 consensus_delegatee: None,
1873 expelled: false,
1874 });
1875 csv.apply_commit(&Commit::Transaction(Transaction {
1877 author: "doesn't matter".to_owned(),
1878 timestamp: 3,
1879 head: "Test reserved-diff commit".to_string(),
1880 body: String::new(),
1881 diff: Diff::Reserved(Box::new(reserved_state.clone())),
1882 }))
1883 .unwrap_err();
1884 }
1885
1886 #[test]
1887 fn invalid_reserved_state_with_non_monotonic_increased_member_names() {
1889 let (_, mut reserved_state, mut csv) = setup_test(5);
1890 reserved_state.members.pop();
1892 reserved_state.consensus_leader_order.pop();
1893 csv.apply_commit(&Commit::Transaction(Transaction {
1895 author: "doesn't matter".to_owned(),
1896 timestamp: 3,
1897 head: "Test reserved-diff commit".to_string(),
1898 body: String::new(),
1899 diff: Diff::Reserved(Box::new(reserved_state.clone())),
1900 }))
1901 .unwrap_err();
1902 }
1903
1904 #[test]
1905 fn test_verify_reserved_state_version_advance() {
1906 let (mut _validator_keypair, reserved_state, csv) = setup_test(4);
1908
1909 let mut valid_rs = reserved_state;
1911 valid_rs.version = "1.1.0".to_string(); assert!(csv.verify_reserved_state(&valid_rs).is_ok());
1914 }
1915
1916 #[ignore]
1917 #[test]
1918 fn phase_mismatch_for_agenda_proof_commit3() {
1920 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1921 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
1923 let agenda: Agenda = Agenda {
1924 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1925 timestamp: 1,
1926 transactions_hash: agenda_transactions_hash,
1927 height: csv.header.height + 1,
1928 previous_block_hash: csv.header.to_hash256(),
1929 };
1930 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
1931 csv.apply_commit(&generate_agenda_proof_commit(
1933 &validator_keypair,
1934 &agenda,
1935 agenda.to_hash256(),
1936 ))
1937 .unwrap();
1938 let delegator = reserved_state.members[0].clone();
1941 let delegator_private_key = validator_keypair[0].1.clone();
1942 let delegatee = reserved_state.members[1].clone();
1943 let delegation_transaction_data: DelegationTransactionData = DelegationTransactionData {
1944 delegator: delegator.name,
1945 delegatee: delegatee.name,
1946 governance: true,
1947 block_height: csv.header.height + 1,
1948 timestamp: 2,
1949 chain_name: reserved_state.genesis_info.chain_name.clone(),
1950 };
1951 let proof =
1952 TypedSignature::sign(&delegation_transaction_data, &delegator_private_key).unwrap();
1953 csv.apply_commit(&generate_delegation_transaction_commit(
1954 &delegation_transaction_data,
1955 proof,
1956 ))
1957 .unwrap();
1958 let agenda_transactions_hash = Agenda::calculate_transactions_hash(&[]);
1960 let agenda: Agenda = Agenda {
1961 author: reserved_state.query_name(&validator_keypair[2].0).unwrap(),
1962 timestamp: 3,
1963 transactions_hash: agenda_transactions_hash,
1964 height: csv.header.height + 1,
1965 previous_block_hash: csv.header.to_hash256(),
1966 };
1967 csv.apply_commit(&generate_agenda_proof_commit(
1968 &validator_keypair,
1969 &agenda,
1970 agenda.to_hash256(),
1971 ))
1972 .unwrap_err();
1973 }
1974
1975 #[test]
1976 fn phase_mismatch_for_agenda_proof_commit4() {
1978 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
1979 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
1981 let agenda: Agenda = Agenda {
1982 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
1983 timestamp: 1,
1984 transactions_hash: agenda_transactions_hash,
1985 height: csv.header.height + 1,
1986 previous_block_hash: csv.header.to_hash256(),
1987 };
1988 csv.apply_commit(&generate_agenda_proof_commit(
1989 &validator_keypair,
1990 &agenda,
1991 agenda.to_hash256(),
1992 ))
1993 .unwrap_err();
1994 }
1995
1996 #[ignore]
1997 #[test]
1998 fn invalid_delegate_transaction_with_invalid_delegator1() {
2000 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
2001 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
2003 let agenda: Agenda = Agenda {
2004 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
2005 timestamp: 1,
2006 transactions_hash: agenda_transactions_hash,
2007 height: csv.header.height + 1,
2008 previous_block_hash: csv.header.to_hash256(),
2009 };
2010 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
2011 csv.apply_commit(&generate_agenda_proof_commit(
2013 &validator_keypair,
2014 &agenda,
2015 agenda.to_hash256(),
2016 ))
2017 .unwrap();
2018 let (delegator_public_key, delegator_private_key) = generate_keypair_random();
2021 let delegator = Member {
2022 public_key: delegator_public_key,
2023 name: "not-a-member".to_string(),
2024 governance_voting_power: 100,
2025 consensus_voting_power: 100,
2026 governance_delegatee: None,
2027 consensus_delegatee: None,
2028 expelled: false,
2029 };
2030 let delegatee = reserved_state.members[0].clone();
2031 let delegation_transaction_data: DelegationTransactionData = DelegationTransactionData {
2032 delegator: delegator.name,
2033 delegatee: delegatee.name,
2034 governance: true,
2035 block_height: csv.header.height + 1,
2036 timestamp: 2,
2037 chain_name: reserved_state.genesis_info.chain_name,
2038 };
2039 let proof: TypedSignature<DelegationTransactionData> =
2040 TypedSignature::sign(&delegation_transaction_data, &delegator_private_key).unwrap();
2041 csv.apply_commit(&generate_delegation_transaction_commit(
2042 &delegation_transaction_data,
2043 proof,
2044 ))
2045 .unwrap_err();
2046 }
2047
2048 #[ignore]
2049 #[test]
2050 fn invalid_delegate_transaction_with_invalid_delegator2() {
2052 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
2053 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
2055 let agenda: Agenda = Agenda {
2056 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
2057 timestamp: 1,
2058 transactions_hash: agenda_transactions_hash,
2059 height: csv.header.height + 1,
2060 previous_block_hash: csv.header.to_hash256(),
2061 };
2062 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
2063 csv.apply_commit(&generate_agenda_proof_commit(
2065 &validator_keypair,
2066 &agenda,
2067 agenda.to_hash256(),
2068 ))
2069 .unwrap();
2070 let delegator = reserved_state.members[1].clone();
2073 let delegator_private_key = validator_keypair[1].1.clone();
2074 let delegatee = reserved_state.members[2].clone();
2075 let delegation_transaction_data: DelegationTransactionData = DelegationTransactionData {
2076 delegator: delegator.name.clone(),
2077 delegatee: delegatee.name,
2078 governance: true,
2079 block_height: csv.header.height + 1,
2080 timestamp: 2,
2081 chain_name: reserved_state.genesis_info.chain_name.clone(),
2082 };
2083 let proof: TypedSignature<DelegationTransactionData> =
2084 TypedSignature::sign(&delegation_transaction_data, &delegator_private_key).unwrap();
2085 csv.apply_commit(&generate_delegation_transaction_commit(
2086 &delegation_transaction_data,
2087 proof,
2088 ))
2089 .unwrap();
2090 let delegatee = reserved_state.members[3].clone();
2093 let delegation_transaction_data: DelegationTransactionData = DelegationTransactionData {
2094 delegator: delegator.name,
2095 delegatee: delegatee.name,
2096 governance: true,
2097 block_height: csv.header.height + 1,
2098 timestamp: 3,
2099 chain_name: reserved_state.genesis_info.chain_name,
2100 };
2101 let proof: TypedSignature<DelegationTransactionData> =
2102 TypedSignature::sign(&delegation_transaction_data, &delegator_private_key).unwrap();
2103 csv.apply_commit(&generate_delegation_transaction_commit(
2104 &delegation_transaction_data,
2105 proof,
2106 ))
2107 .unwrap_err();
2108 }
2109
2110 #[ignore]
2111 #[test]
2112 fn invalid_delegate_transaction_with_invalid_delegatee() {
2114 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
2115 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
2117 let agenda: Agenda = Agenda {
2118 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
2119 timestamp: 1,
2120 transactions_hash: agenda_transactions_hash,
2121 height: csv.header.height + 1,
2122 previous_block_hash: csv.header.to_hash256(),
2123 };
2124 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
2125 csv.apply_commit(&generate_agenda_proof_commit(
2127 &validator_keypair,
2128 &agenda,
2129 agenda.to_hash256(),
2130 ))
2131 .unwrap();
2132 let delegator = reserved_state.members[1].clone();
2135 let delegator_private_key = validator_keypair[1].1.clone();
2136 let delegation_transaction_data: DelegationTransactionData = DelegationTransactionData {
2137 delegator: delegator.name,
2138 delegatee: "not-a-member".to_string(),
2139 governance: true,
2140 block_height: csv.header.height + 1,
2141 timestamp: 2,
2142 chain_name: reserved_state.genesis_info.chain_name,
2143 };
2144 let proof =
2145 TypedSignature::sign(&delegation_transaction_data, &delegator_private_key).unwrap();
2146 csv.apply_commit(&generate_delegation_transaction_commit(
2147 &delegation_transaction_data,
2148 proof,
2149 ))
2150 .unwrap_err();
2151 }
2152
2153 #[ignore]
2154 #[test]
2155 fn invalid_delegate_transaction_with_invalid_signature() {
2157 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
2158 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
2160 let agenda: Agenda = Agenda {
2161 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
2162 timestamp: 1,
2163 transactions_hash: agenda_transactions_hash,
2164 height: csv.header.height + 1,
2165 previous_block_hash: csv.header.to_hash256(),
2166 };
2167 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
2168 csv.apply_commit(&generate_agenda_proof_commit(
2170 &validator_keypair,
2171 &agenda,
2172 agenda.to_hash256(),
2173 ))
2174 .unwrap();
2175 let delegator = reserved_state.members[1].clone();
2179 let non_delegator_private_key = validator_keypair[3].1.clone();
2180 let delegatee = reserved_state.members[2].clone();
2181 let delegation_transaction_data: DelegationTransactionData = DelegationTransactionData {
2182 delegator: delegator.name,
2183 delegatee: delegatee.name,
2184 governance: true,
2185 block_height: csv.header.height + 1,
2186 timestamp: 2,
2187 chain_name: reserved_state.genesis_info.chain_name,
2188 };
2189 let proof =
2190 TypedSignature::sign(&delegation_transaction_data, &non_delegator_private_key).unwrap();
2191 csv.apply_commit(&generate_delegation_transaction_commit(
2192 &delegation_transaction_data,
2193 proof,
2194 ))
2195 .unwrap_err();
2196 }
2197
2198 #[ignore]
2199 #[test]
2200 fn invalid_delegate_transaction_with_invalid_timestamp() {
2204 todo!("Implement this test")
2205 }
2206
2207 #[ignore]
2208 #[test]
2209 fn invalid_undelegate_transaction_with_invalid_delegator1() {
2213 todo!("Implement this test")
2214 }
2215
2216 #[ignore]
2217 #[test]
2218 fn invalid_undelegate_transaction_with_invalid_delegator2() {
2222 todo!("Implement this test")
2223 }
2224
2225 #[ignore]
2226 #[test]
2227 fn invalid_undelegate_transaction_with_invalid_signature() {
2229 let (validator_keypair, reserved_state, mut csv) = setup_test(4);
2230 let agenda_transactions_hash = calculate_agenda_transactions_hash(csv.phase.clone());
2232 let agenda: Agenda = Agenda {
2233 author: reserved_state.query_name(&validator_keypair[0].0).unwrap(),
2234 timestamp: 1,
2235 transactions_hash: agenda_transactions_hash,
2236 height: csv.header.height + 1,
2237 previous_block_hash: csv.header.to_hash256(),
2238 };
2239 csv.apply_commit(&generate_agenda_commit(&agenda)).unwrap();
2240 csv.apply_commit(&generate_agenda_proof_commit(
2242 &validator_keypair,
2243 &agenda,
2244 agenda.to_hash256(),
2245 ))
2246 .unwrap();
2247 let delegator = reserved_state.members[1].clone();
2250 let delegator_private_key = validator_keypair[1].1.clone();
2251 let delegatee = reserved_state.members[2].clone();
2252 let delegation_transaction_data: DelegationTransactionData = DelegationTransactionData {
2253 delegator: delegator.name.clone(),
2254 delegatee: delegatee.name,
2255 governance: true,
2256 block_height: csv.header.height + 1,
2257 timestamp: 2,
2258 chain_name: reserved_state.genesis_info.chain_name.clone(),
2259 };
2260 let proof =
2261 TypedSignature::sign(&delegation_transaction_data, &delegator_private_key).unwrap();
2262 csv.apply_commit(&generate_delegation_transaction_commit(
2263 &delegation_transaction_data,
2264 proof,
2265 ))
2266 .unwrap();
2267 let non_delegator_private_key = validator_keypair[3].1.clone();
2269 let undelegation_transaction_data: UndelegationTransactionData =
2270 UndelegationTransactionData {
2271 delegator: delegator.name,
2272 block_height: csv.header.height + 1,
2273 timestamp: 2,
2274 chain_name: reserved_state.genesis_info.chain_name,
2275 };
2276 let invalid_proof =
2277 TypedSignature::sign(&undelegation_transaction_data, &non_delegator_private_key)
2278 .unwrap();
2279 csv.apply_commit(&generate_undelegation_transaction_commit(
2280 &undelegation_transaction_data,
2281 invalid_proof,
2282 ))
2283 .unwrap_err();
2284 }
2285
2286 #[ignore]
2287 #[test]
2288 fn invalid_undelegate_transaction_with_invalid_timestamp() {
2292 todo!("Implement this test")
2293 }
2294
2295 }