From 7f5cfdac90485604f97f25ca545297ea87729d4c Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Fri, 12 Apr 2024 16:49:23 +0200 Subject: [PATCH] light-client-verifier: reuse sign_bytes buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than allocating a new vector each time canonical vote is encoded, allocate a vector in NonAbsentCommitVotes once and then reuse it each time sign_bytes are needed. This saves on doing multiple allocations and deallocations. For overly verbose description of the length calculation: * CanonicalVote. Note that length prefix doesn’t count bytes used for encoding the length, e.g. if the CanonicalVote message is 127 bytes than the length prefix is one byte and thus sign_bytes is 128 bytes. The table assumes all fields are set. | | tag | value | |-----------------+-----+-------+ | (length) | | 1–2 | 2 if (length) is > 127 | type | 1 | 1 | SignedMsgType is at most 32 | height | 1 | 8 | The field is sfixed64 | round | 1 | 8 | The field is sfixed64 | block_id | 1 | 73–77 | See CanonicalBlockId below | timestamp | 1 | 7—13 | See Timestamp below | chain_id | 1 | 1+N | Assuming N < 128 |-----------------+-----+-------+ | (total) | 105–116+N | If N≤13 than (length) is | | | 1 byte and this is 115+N * CanonicalBlockId. | | tag | value | |-----------------+-----+-------+ | (length) | | 1 | | hash | 1 | 33 | Assuming 32-byte hash. | part_set_header | 1 | 37–41 | See CanonicalPartSetHeader |-----------------+-----+-------+ | (total) | 73–77 | * CanonicalPartSetHeader. | | tag | value | |-----------------+-----+-------+ | (length) | | 1 | | total | 1 | 1–5 | Varint of u32; at most 5 bytes. | hash | 1 | 33 | Assuming 32-byte hash. |-----------------+-----+-------+ | (total) | 37–41 | * Timestamp. In unlikely event vote was made exactly at a second, nanos is zero and thus the field is not present. Assuming uniform probability of the nanos, in a bit over 73% of cases the filed will occupy it’s maximum size. | | tag | value | |-----------------+-----+-------+ | (length) | | 1 | | seconds | 1 | 5 | Assuming timestamps around now. | nanos | 0–1 | 0–5 | |-----------------+-----+-------+ | (total) | | 7–13 | --- .../improvements/1412-reuse-sign-bytes.md | 3 +++ .../src/operations/voting_power.rs | 22 +++++++++++++++---- tendermint/src/vote.rs | 4 ++++ 3 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 .changelog/unreleased/improvements/1412-reuse-sign-bytes.md diff --git a/.changelog/unreleased/improvements/1412-reuse-sign-bytes.md b/.changelog/unreleased/improvements/1412-reuse-sign-bytes.md new file mode 100644 index 000000000..08fd6939e --- /dev/null +++ b/.changelog/unreleased/improvements/1412-reuse-sign-bytes.md @@ -0,0 +1,3 @@ +- `[tendermint-light-client-verifier]` Reuse buffer used to store + sign_bytes to reduce number of allocations and deallocations. + ([\#1412](https://github.com/informalsystems/tendermint-rs/pull/1412)) \ No newline at end of file diff --git a/light-client-verifier/src/operations/voting_power.rs b/light-client-verifier/src/operations/voting_power.rs index 1f7e9f0ed..0511cc426 100644 --- a/light-client-verifier/src/operations/voting_power.rs +++ b/light-client-verifier/src/operations/voting_power.rs @@ -264,6 +264,11 @@ impl NonAbsentCommitVote { struct NonAbsentCommitVotes { /// Votes sorted by validator address. votes: Vec, + /// Internal buffer for storing sign_bytes. + /// + /// The buffer is reused for each canonical vote so that we allocate it + /// once. + sign_bytes: Vec, } impl NonAbsentCommitVotes { @@ -297,7 +302,12 @@ impl NonAbsentCommitVotes { pair[0].validator_id(), )) } else { - Ok(Self { votes }) + // As of protocol 0.38, 160 buffer is enough for chain identifier up + // to 44 bytes. + Ok(Self { + votes, + sign_bytes: Vec::with_capacity(160), + }) } } @@ -319,14 +329,18 @@ impl NonAbsentCommitVotes { }; if !vote.verified { - let sign_bytes = vote.signed_vote.sign_bytes(); + self.sign_bytes.truncate(0); + vote.signed_vote + .sign_bytes_into(&mut self.sign_bytes) + .unwrap(); + let sign_bytes = self.sign_bytes.as_slice(); validator - .verify_signature::(&sign_bytes, vote.signed_vote.signature()) + .verify_signature::(sign_bytes, vote.signed_vote.signature()) .map_err(|_| { VerificationError::invalid_signature( vote.signed_vote.signature().as_bytes().to_vec(), Box::new(validator.clone()), - sign_bytes, + sign_bytes.to_vec(), ) })?; } diff --git a/tendermint/src/vote.rs b/tendermint/src/vote.rs index cd37c728d..a4cd82ce0 100644 --- a/tendermint/src/vote.rs +++ b/tendermint/src/vote.rs @@ -329,6 +329,10 @@ impl SignedVote { Protobuf::::encode_length_delimited_vec(self.vote.clone()) } + pub fn sign_bytes_into(&self, buf: &mut impl BufMut) -> Result<(), ProtobufError> { + Protobuf::::encode_length_delimited(self.vote.clone(), buf) + } + /// Return the actual signature on the canonicalized vote. pub fn signature(&self) -> &Signature { &self.signature