Skip to content

Commit

Permalink
light-client-verifier: reuse sign_bytes buffer
Browse files Browse the repository at this point in the history
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 |
  • Loading branch information
mina86 committed Apr 22, 2024
1 parent dac61d1 commit a52c7b4
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 4 deletions.
3 changes: 3 additions & 0 deletions .changelog/unreleased/improvements/1413-reuse-sign-bytes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- `[tendermint-light-client-verifier]` Reuse buffer used to store
sign_bytes to reduce number of allocations and deallocations.
([\#1413](https://github.com/informalsystems/tendermint-rs/pull/1413))
22 changes: 18 additions & 4 deletions light-client-verifier/src/operations/voting_power.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,11 @@ impl NonAbsentCommitVote {
struct NonAbsentCommitVotes {
/// Votes sorted by validator address.
votes: Vec<NonAbsentCommitVote>,
/// Internal buffer for storing sign_bytes.
///
/// The buffer is reused for each canonical vote so that we allocate it
/// once.
sign_bytes: Vec<u8>,
}

impl NonAbsentCommitVotes {
Expand Down Expand Up @@ -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),
})
}
}

Expand All @@ -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::<V>(&sign_bytes, vote.signed_vote.signature())
.verify_signature::<V>(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(),
)
})?;
}
Expand Down
4 changes: 4 additions & 0 deletions tendermint/src/vote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,10 @@ impl SignedVote {
Protobuf::<RawCanonicalVote>::encode_length_delimited_vec(self.vote.clone())
}

pub fn sign_bytes_into(&self, buf: &mut impl BufMut) -> Result<(), ProtobufError> {
Protobuf::<RawCanonicalVote>::encode_length_delimited(self.vote.clone(), buf)
}

/// Return the actual signature on the canonicalized vote.
pub fn signature(&self) -> &Signature {
&self.signature
Expand Down

0 comments on commit a52c7b4

Please sign in to comment.