Skip to content

perf(light): Use cache when verifying signatures#4322

Merged
melekes merged 18 commits intocometbft:mainfrom
wllmshao:ws/dedupe_light_sig
Nov 5, 2024
Merged

perf(light): Use cache when verifying signatures#4322
melekes merged 18 commits intocometbft:mainfrom
wllmshao:ws/dedupe_light_sig

Conversation

@wllmshao
Copy link
Copy Markdown
Contributor

@wllmshao wllmshao commented Oct 23, 2024

Closes #2365

This PR adds two new methods to light package: VerifyCommitLightTrustingWithCache and VerifyCommitLightWithCache so that we don't have to verify the same signatures twice.

Pending some perf tests/benchmarks to measure performance improvement.

@wllmshao wllmshao force-pushed the ws/dedupe_light_sig branch 4 times, most recently from 791470a to 76c5e4f Compare October 23, 2024 18:24
Comment on lines +281 to +282
cacheKey := string(val.Address) + string(voteSignBytes)
_, ok := verifiedSignatureCache[cacheKey]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! This has a malleabiltiy vector though if addresses weren't fixed size. Its also not zero-allocation, which is good to have at the sig verification level.

Perhaps structure the map using vote sign bytes as the key, and then the val address as the value? Not sure we can avoid the string casts for immutability reasons, so maybe we just live with the allocs. Should see how much it slows it down in the normal case.

Also can we allow passing in a nil map (skipping the check if nil)

Copy link
Copy Markdown
Contributor Author

@wllmshao wllmshao Oct 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to make these changes.

  • Using string(voteSignBytes) as the key and val address as the value
  • Check verifiedSignatureCache != nil before any usage. Pass nil where ever the cache does not need to be used

Copy link
Copy Markdown
Contributor

@ValarDragon ValarDragon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, nice work!

But we should add a tests around non-nil caches being provided, and tally logic for counting to 2/3rds still working!

@wllmshao wllmshao force-pushed the ws/dedupe_light_sig branch from ea25d8e to d01fd92 Compare October 23, 2024 21:17
@wllmshao
Copy link
Copy Markdown
Contributor Author

@ValarDragon thinking about this more, aren't the VoteSignBytes actually likely to be the same across signatures? I see a comment that says they differ only in the timestamp, the rest of the fields are identical. Would it not be better to use the validator address or the commit signature as the cache key?

@ValarDragon
Copy link
Copy Markdown
Contributor

So we need to be sure that all 3 arguments for Verify(sign_bytes, signature, public_key) are the same, ideally with 0 allocations.

The key being the signature makes sense to me, since your right sign_bytes can get re-used. And then the value of the map being struct{sign_bytes, public_key} perhaps, where we check those two fields

@wllmshao wllmshao force-pushed the ws/dedupe_light_sig branch from 54590a6 to 6c3d2c6 Compare October 28, 2024 18:42
@wllmshao wllmshao force-pushed the ws/dedupe_light_sig branch from 1dd5304 to 1955212 Compare October 28, 2024 19:08
@wllmshao
Copy link
Copy Markdown
Contributor Author

wllmshao commented Oct 28, 2024

I added unit tests and changed the cache schema. Opening this for review.

Will still do some perf tests / benchmarks to try to measure performance improvement.

@wllmshao wllmshao marked this pull request as ready for review October 28, 2024 19:13
@wllmshao wllmshao requested a review from a team as a code owner October 28, 2024 19:13
@wllmshao wllmshao requested review from a team and ValarDragon October 28, 2024 19:13
Copy link
Copy Markdown
Collaborator

@melekes melekes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @wllmshao ❤️ Would love to see this merged

@wllmshao wllmshao requested a review from melekes October 29, 2024 23:02
@wllmshao wllmshao force-pushed the ws/dedupe_light_sig branch from 4f588b3 to 94ce647 Compare October 29, 2024 23:02
@melekes

This comment was marked as resolved.

@wllmshao wllmshao force-pushed the ws/dedupe_light_sig branch from 94ce647 to 31835c8 Compare October 30, 2024 15:15
@melekes
Copy link
Copy Markdown
Collaborator

melekes commented Oct 31, 2024

Please don't force-push. It makes reviewing impossible

Copy link
Copy Markdown

@cason cason left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder whether the signatures cache should I type by itself, instead of using a map[string]SignatureCacheValue. This will probably render it simple to implement and test.

Moreover, I wonder whether the tests can indeed confirm that the cache is being used. I think that a mock signature verifier/key should be used in this case, so we are really sure that the verification methods are not invoked.

@cason

This comment was marked as resolved.

@cason
Copy link
Copy Markdown

cason commented Oct 31, 2024

This PR assumes that pubkeys are uniquely identified by the Address. Is that safe?

Yes. The consensus logic uses it, for instance proposerAddr := cs.privValidatorPubKey.Address(). And the Vote type include the same Address() that should match the address in the validator set.

@melekes melekes added enhancement New feature or request light-client Light client-related labels Nov 1, 2024
@melekes melekes changed the title perf: Remove duplicated signature checks in light.VerifyNonAdjacent perf(light): Use cache when verifying signatures Nov 1, 2024
@wllmshao
Copy link
Copy Markdown
Contributor Author

wllmshao commented Nov 4, 2024

I wonder whether the signatures cache should I type by itself, instead of using a map[string]SignatureCacheValue. This will probably render it simple to implement and test.

Moreover, I wonder whether the tests can indeed confirm that the cache is being used. I think that a mock signature verifier/key should be used in this case, so we are really sure that the verification methods are not invoked.

Created a new type for SignatureCache to replace the map usage.
Added unit tests that use mocks to ensure that the cache is being used and signature verifications are not happening.

Copy link
Copy Markdown
Collaborator

@melekes melekes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks 🙏

cache map[string]SignatureCacheValue
}

func NewSignatureCache() SignatureCache {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see how this is better than the original map[string]SignatureCacheValue. @cason is this what you had in mind?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, so we can implement this abstraction in the way we prefer.

Copy link
Copy Markdown

@cason cason left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you a lot for this effort.

There are some details that we should take care of, but I'd say that we can take it over from here.

cache map[string]SignatureCacheValue
}

func NewSignatureCache() SignatureCache {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, so we can implement this abstraction in the way we prefer.

sig := commit.Signatures[idx]
verifiedSignatureCache.Add(string(sig.Signature), SignatureCacheValue{
ValidatorAddress: sig.ValidatorAddress,
VoteSignBytes: commit.VoteSignBytes(chainID, int32(idx)),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This call requires proto encoding, namely is kind of expensive. Don't we have these bytes already computed?

}

type signatureCache struct {
cache map[string]SignatureCacheValue
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This cache grows over time, should we somehow restrict its size?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a new changelog entry for the creation of the SignatureCache type?

@melekes melekes added this pull request to the merge queue Nov 5, 2024
Merged via the queue into cometbft:main with commit 3e3ee5d Nov 5, 2024
mattac21 pushed a commit that referenced this pull request Sep 9, 2025
Closes #2365

This PR adds two new methods to `light` package:
`VerifyCommitLightTrustingWithCache` and `VerifyCommitLightWithCache` so
that we don't have to verify the same signatures twice.

Pending some perf tests/benchmarks to measure performance improvement.

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Anton Kaliaev <anton.kalyaev@gmail.com>
Co-authored-by: Daniel <daniel.cason@informal.systems>
mattac21 pushed a commit that referenced this pull request Sep 10, 2025
Closes #2365

This PR adds two new methods to `light` package:
`VerifyCommitLightTrustingWithCache` and `VerifyCommitLightWithCache` so
that we don't have to verify the same signatures twice.

Pending some perf tests/benchmarks to measure performance improvement.

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Anton Kaliaev <anton.kalyaev@gmail.com>
Co-authored-by: Daniel <daniel.cason@informal.systems>
mattac21 pushed a commit that referenced this pull request Sep 10, 2025
Closes #2365

This PR adds two new methods to `light` package:
`VerifyCommitLightTrustingWithCache` and `VerifyCommitLightWithCache` so
that we don't have to verify the same signatures twice.

Pending some perf tests/benchmarks to measure performance improvement.

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Anton Kaliaev <anton.kalyaev@gmail.com>
Co-authored-by: Daniel <daniel.cason@informal.systems>
mattac21 pushed a commit that referenced this pull request Sep 10, 2025
Closes #2365

This PR adds two new methods to `light` package:
`VerifyCommitLightTrustingWithCache` and `VerifyCommitLightWithCache` so
that we don't have to verify the same signatures twice.

Pending some perf tests/benchmarks to measure performance improvement.

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Anton Kaliaev <anton.kalyaev@gmail.com>
Co-authored-by: Daniel <daniel.cason@informal.systems>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request light-client Light client-related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[perf]: Remove duplicated signature checks in light.VerifyNonAdjacent

4 participants