penumbra_sdk_mock_consensus/block/
signature.rs1use {
2 super::Builder,
3 crate::TestNode,
4 sha2::{Digest, Sha256},
5 tendermint::{
6 abci::types::{BlockSignatureInfo, CommitInfo, VoteInfo},
7 account,
8 block::{BlockIdFlag, Commit, CommitSig, Round},
9 vote::Power,
10 },
11};
12
13mod sign {
15 use tendermint::{account::Id, block::CommitSig, time::Time};
16
17 pub(super) fn commit(
21 validator_address: Id,
22 validator_key: &ed25519_consensus::SigningKey,
23 canonical: &tendermint::vote::CanonicalVote,
24 ) -> CommitSig {
25 use tendermint_proto::v0_37::types::CanonicalVote as RawCanonicalVote;
30 let sign_bytes =
31 tendermint_proto::Protobuf::<RawCanonicalVote>::encode_length_delimited_vec(
32 canonical.clone(),
33 );
34
35 let signature: tendermint::Signature = validator_key
36 .sign(sign_bytes.as_slice())
37 .try_into()
38 .unwrap();
39
40 CommitSig::BlockIdFlagCommit {
44 validator_address,
45 timestamp: canonical.timestamp.expect("timestamp should be present"),
46 signature: Some(signature.into()),
47 }
48 }
49
50 #[allow(dead_code)]
54 pub(super) fn nil(validator_address: Id, timestamp: Time) -> CommitSig {
55 CommitSig::BlockIdFlagNil {
56 validator_address,
57 timestamp,
58 signature: None,
59 }
60 }
61}
62
63impl<C> TestNode<C> {
66 pub(super) fn generate_signatures(
73 &self,
74 header: &tendermint::block::Header,
75 ) -> impl Iterator<Item = CommitSig> + '_ {
76 let block_id = tendermint::block::Id {
77 hash: header.hash(),
78 part_set_header: tendermint::block::parts::Header::new(0, tendermint::Hash::None)
79 .unwrap(),
80 };
81 let canonical = tendermint::vote::CanonicalVote {
82 vote_type: tendermint::vote::Type::Precommit,
84 height: tendermint::block::Height::from(header.height),
85 round: 0u8.into(),
87 block_id: Some(block_id),
88 timestamp: Some(header.time.clone()),
90 chain_id: self.chain_id.clone(),
92 };
93 tracing::trace!(vote=?canonical,"canonical vote constructed");
94
95 return self
96 .keyring
97 .iter()
98 .map(|(vk, sk)| {
99 (
100 <Sha256 as Digest>::digest(vk).as_slice()[0..20]
101 .try_into()
102 .expect(""),
103 sk,
104 )
105 })
106 .map(move |(id, sk)| self::sign::commit(account::Id::new(id), sk, &canonical));
107 }
108}
109
110impl<'e, C: 'e> Builder<'e, C> {
113 pub(super) fn last_commit_info(last_commit: Option<Commit>) -> CommitInfo {
115 let Some(Commit {
116 round, signatures, ..
117 }) = last_commit
118 else {
119 return CommitInfo {
121 round: Round::default(),
122 votes: Vec::default(),
123 };
124 };
125
126 CommitInfo {
127 round,
128 votes: signatures.into_iter().filter_map(Self::vote).collect(),
129 }
130 }
131
132 fn vote(commit_sig: CommitSig) -> Option<VoteInfo> {
136 use tendermint::abci::types::Validator;
137
138 let sig_info = BlockSignatureInfo::Flag(match commit_sig {
140 CommitSig::BlockIdFlagAbsent => BlockIdFlag::Absent,
141 CommitSig::BlockIdFlagCommit { .. } => BlockIdFlag::Commit,
142 CommitSig::BlockIdFlagNil { .. } => BlockIdFlag::Nil,
143 });
144
145 let address: [u8; 20] = commit_sig
146 .validator_address()?
147 .as_bytes()
149 .try_into()
150 .expect("validator address should be 20 bytes");
151 let power = Power::from(1_u8); let validator = Validator { address, power };
153
154 Some(VoteInfo {
155 validator,
156 sig_info,
157 })
158 }
159}