Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ef38da4

Browse files
sergio-menajmalicevic
andauthoredDec 14, 2023
Introduce countAllSignatures in VerifyCommitLight & VerifyCommitLightTrusting (#1806) (#1815)
* Introduce `countAllSignatures` in `VerifyCommitLight` & `VerifyCommitLightTrusting` * Revert unneded change * Addressed @insumity's comments --------- Co-authored-by: Jasmina Malicevic <[email protected]>
1 parent b7b07fd commit ef38da4

File tree

8 files changed

+129
-27
lines changed

8 files changed

+129
-27
lines changed
 

‎blockchain/v0/reactor.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ FOR_LOOP:
363363
// NOTE: we can probably make this more efficient, but note that calling
364364
// first.Hash() doesn't verify the tx contents, so MakePartSet() is
365365
// currently necessary.
366-
err := state.Validators.VerifyCommitLight(
366+
err := state.Validators.VerifyCommitLightAllSignatures(
367367
chainID, firstID, first.Height, second.LastCommit)
368368

369369
if err == nil {

‎blockchain/v1/reactor.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ func (bcR *BlockchainReactor) processBlock() error {
475475
// NOTE: we can probably make this more efficient, but note that calling
476476
// first.Hash() doesn't verify the tx contents, so MakePartSet() is
477477
// currently necessary.
478-
err = bcR.state.Validators.VerifyCommitLight(chainID, firstID, first.Height, second.LastCommit)
478+
err = bcR.state.Validators.VerifyCommitLightAllSignatures(chainID, firstID, first.Height, second.LastCommit)
479479
if err != nil {
480480
bcR.Logger.Error("error during commit verification", "err", err,
481481
"first", first.Height, "second", second.Height)

‎blockchain/v2/processor_context.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func (pc *pContext) setState(state state.State) {
4444
}
4545

4646
func (pc pContext) verifyCommit(chainID string, blockID types.BlockID, height int64, commit *types.Commit) error {
47-
return pc.state.Validators.VerifyCommitLight(chainID, blockID, height, commit)
47+
return pc.state.Validators.VerifyCommitLightAllSignatures(chainID, blockID, height, commit)
4848
}
4949

5050
func (pc *pContext) saveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) {

‎evidence/pool.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -132,19 +132,19 @@ func (evpool *Pool) Update(state sm.State, ev types.EvidenceList) {
132132

133133
// AddEvidence checks the evidence is valid and adds it to the pool.
134134
func (evpool *Pool) AddEvidence(ev types.Evidence) error {
135-
evpool.logger.Debug("Attempting to add evidence", "ev", ev)
135+
evpool.logger.Info("Attempting to add evidence", "ev", ev)
136136

137137
// We have already verified this piece of evidence - no need to do it again
138138
if evpool.isPending(ev) {
139-
evpool.logger.Debug("Evidence already pending, ignoring this one", "ev", ev)
139+
evpool.logger.Info("Evidence already pending, ignoring this one", "ev", ev)
140140
return nil
141141
}
142142

143143
// check that the evidence isn't already committed
144144
if evpool.isCommitted(ev) {
145145
// this can happen if the peer that sent us the evidence is behind so we shouldn't
146146
// punish the peer.
147-
evpool.logger.Debug("Evidence was already committed, ignoring this one", "ev", ev)
147+
evpool.logger.Info("Evidence was already committed, ignoring this one", "ev", ev)
148148
return nil
149149
}
150150

@@ -505,13 +505,13 @@ func (evpool *Pool) processConsensusBuffer(state sm.State) {
505505

506506
// check if we already have this evidence
507507
if evpool.isPending(dve) {
508-
evpool.logger.Debug("evidence already pending; ignoring", "evidence", dve)
508+
evpool.logger.Info("evidence already pending; ignoring", "evidence", dve)
509509
continue
510510
}
511511

512512
// check that the evidence is not already committed on chain
513513
if evpool.isCommitted(dve) {
514-
evpool.logger.Debug("evidence already committed; ignoring", "evidence", dve)
514+
evpool.logger.Info("evidence already committed; ignoring", "evidence", dve)
515515
continue
516516
}
517517

‎evidence/verify.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ func (evpool *Pool) verify(evidence types.Evidence) error {
106106
// the conflicting header's commit
107107
// - 2/3+ of the conflicting validator set correctly signed the conflicting block
108108
// - the nodes trusted header at the same height as the conflicting header has a different hash
109+
// - all signatures must be checked as this will be used as evidence
109110
//
110111
// CONTRACT: must run ValidateBasic() on the evidence before verifying
111112
//
@@ -115,7 +116,7 @@ func VerifyLightClientAttack(e *types.LightClientAttackEvidence, commonHeader, t
115116
// In the case of lunatic attack there will be a different commonHeader height. Therefore the node perform a single
116117
// verification jump between the common header and the conflicting one
117118
if commonHeader.Height != e.ConflictingBlock.Height {
118-
err := commonVals.VerifyCommitLightTrusting(trustedHeader.ChainID, e.ConflictingBlock.Commit, light.DefaultTrustLevel)
119+
err := commonVals.VerifyCommitLightTrustingAllSignatures(trustedHeader.ChainID, e.ConflictingBlock.Commit, light.DefaultTrustLevel)
119120
if err != nil {
120121
return fmt.Errorf("skipping verification of conflicting block failed: %w", err)
121122
}
@@ -127,7 +128,7 @@ func VerifyLightClientAttack(e *types.LightClientAttackEvidence, commonHeader, t
127128
}
128129

129130
// Verify that the 2/3+ commits from the conflicting validator set were for the conflicting header
130-
if err := e.ConflictingBlock.ValidatorSet.VerifyCommitLight(trustedHeader.ChainID, e.ConflictingBlock.Commit.BlockID,
131+
if err := e.ConflictingBlock.ValidatorSet.VerifyCommitLightAllSignatures(trustedHeader.ChainID, e.ConflictingBlock.Commit.BlockID,
131132
e.ConflictingBlock.Height, e.ConflictingBlock.Commit); err != nil {
132133
return fmt.Errorf("invalid commit from conflicting block: %w", err)
133134
}

‎test/e2e/app/app.go

+13
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,19 @@ func (app *Application) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDelive
149149
return abci.ResponseDeliverTx{Code: code.CodeTypeOK}
150150
}
151151

152+
func (app *Application) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock {
153+
for _, ev := range req.ByzantineValidators {
154+
app.logger.Info("Misbehavior. Slashing validator",
155+
"validator_address", ev.GetValidator().Address,
156+
"type", ev.GetType(),
157+
"height", ev.GetHeight(),
158+
"time", ev.GetTime(),
159+
"total_voting_power", ev.GetTotalVotingPower(),
160+
)
161+
}
162+
return abci.ResponseBeginBlock{}
163+
}
164+
152165
// EndBlock implements ABCI.
153166
func (app *Application) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock {
154167
valUpdates, err := app.validatorUpdates(uint64(req.Height))

‎types/validator_set.go

+73-12
Original file line numberDiff line numberDiff line change
@@ -717,10 +717,36 @@ func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID,
717717

718718
// VerifyCommitLight verifies +2/3 of the set had signed the given commit.
719719
//
720-
// This method is primarily used by the light client and does not check all the
720+
// This method is primarily used by the light client and does NOT check all the
721721
// signatures.
722-
func (vals *ValidatorSet) VerifyCommitLight(chainID string, blockID BlockID,
723-
height int64, commit *Commit) error {
722+
func (vals *ValidatorSet) VerifyCommitLight(
723+
chainID string,
724+
blockID BlockID,
725+
height int64,
726+
commit *Commit,
727+
) error {
728+
return vals.verifyCommitLightInternal(chainID, blockID, height, commit, false)
729+
}
730+
731+
// VerifyCommitLightAllSignatures verifies +2/3 of the set had signed the given commit.
732+
//
733+
// This method DOES check all the signatures.
734+
func (vals *ValidatorSet) VerifyCommitLightAllSignatures(
735+
chainID string,
736+
blockID BlockID,
737+
height int64,
738+
commit *Commit,
739+
) error {
740+
return vals.verifyCommitLightInternal(chainID, blockID, height, commit, true)
741+
}
742+
743+
func (vals *ValidatorSet) verifyCommitLightInternal(
744+
chainID string,
745+
blockID BlockID,
746+
height int64,
747+
commit *Commit,
748+
countAllSignatures bool,
749+
) error {
724750

725751
if vals.Size() != len(commit.Signatures) {
726752
return NewErrInvalidCommitSignatures(vals.Size(), len(commit.Signatures))
@@ -738,8 +764,9 @@ func (vals *ValidatorSet) VerifyCommitLight(chainID string, blockID BlockID,
738764
talliedVotingPower := int64(0)
739765
votingPowerNeeded := vals.TotalVotingPower() * 2 / 3
740766
for idx, commitSig := range commit.Signatures {
741-
// No need to verify absent or nil votes.
742-
if !commitSig.ForBlock() {
767+
// No need to verify absent or nil votes if not counting all signatures,
768+
// but need to verify nil votes if counting all signatures.
769+
if (!countAllSignatures && !commitSig.ForBlock()) || (countAllSignatures && commitSig.Absent()) {
743770
continue
744771
}
745772

@@ -756,11 +783,14 @@ func (vals *ValidatorSet) VerifyCommitLight(chainID string, blockID BlockID,
756783
talliedVotingPower += val.VotingPower
757784

758785
// return as soon as +2/3 of the signatures are verified
759-
if talliedVotingPower > votingPowerNeeded {
786+
if !countAllSignatures && talliedVotingPower > votingPowerNeeded {
760787
return nil
761788
}
762789
}
763790

791+
if talliedVotingPower > votingPowerNeeded {
792+
return nil
793+
}
764794
return ErrNotEnoughVotingPowerSigned{Got: talliedVotingPower, Needed: votingPowerNeeded}
765795
}
766796

@@ -770,9 +800,37 @@ func (vals *ValidatorSet) VerifyCommitLight(chainID string, blockID BlockID,
770800
// NOTE the given validators do not necessarily correspond to the validator set
771801
// for this commit, but there may be some intersection.
772802
//
773-
// This method is primarily used by the light client and does not check all the
803+
// This method is primarily used by the light client and does NOT check all the
774804
// signatures.
775-
func (vals *ValidatorSet) VerifyCommitLightTrusting(chainID string, commit *Commit, trustLevel cmtmath.Fraction) error {
805+
func (vals *ValidatorSet) VerifyCommitLightTrusting(
806+
chainID string,
807+
commit *Commit,
808+
trustLevel cmtmath.Fraction,
809+
) error {
810+
return vals.verifyCommitLightTrustingInternal(chainID, commit, trustLevel, false)
811+
}
812+
813+
// VerifyCommitLightTrustingAllSignatures verifies that trustLevel of the validator
814+
// set signed this commit.
815+
//
816+
// NOTE the given validators do not necessarily correspond to the validator set
817+
// for this commit, but there may be some intersection.
818+
//
819+
// This method DOES check all the signatures.
820+
func (vals *ValidatorSet) VerifyCommitLightTrustingAllSignatures(
821+
chainID string,
822+
commit *Commit,
823+
trustLevel cmtmath.Fraction,
824+
) error {
825+
return vals.verifyCommitLightTrustingInternal(chainID, commit, trustLevel, true)
826+
}
827+
828+
func (vals *ValidatorSet) verifyCommitLightTrustingInternal(
829+
chainID string,
830+
commit *Commit,
831+
trustLevel cmtmath.Fraction,
832+
countAllSignatures bool,
833+
) error {
776834
// sanity check
777835
if trustLevel.Denominator == 0 {
778836
return errors.New("trustLevel has zero Denominator")
@@ -791,8 +849,9 @@ func (vals *ValidatorSet) VerifyCommitLightTrusting(chainID string, commit *Comm
791849
votingPowerNeeded := totalVotingPowerMulByNumerator / int64(trustLevel.Denominator)
792850

793851
for idx, commitSig := range commit.Signatures {
794-
// No need to verify absent or nil votes.
795-
if !commitSig.ForBlock() {
852+
// No need to verify absent or nil votes if not counting all signatures,
853+
// but need to verify nil votes if counting all signatures.
854+
if (!countAllSignatures && !commitSig.ForBlock()) || (countAllSignatures && commitSig.Absent()) {
796855
continue
797856
}
798857

@@ -816,12 +875,14 @@ func (vals *ValidatorSet) VerifyCommitLightTrusting(chainID string, commit *Comm
816875

817876
talliedVotingPower += val.VotingPower
818877

819-
if talliedVotingPower > votingPowerNeeded {
878+
if !countAllSignatures && talliedVotingPower > votingPowerNeeded {
820879
return nil
821880
}
822881
}
823882
}
824-
883+
if talliedVotingPower > votingPowerNeeded {
884+
return nil
885+
}
825886
return ErrNotEnoughVotingPowerSigned{Got: talliedVotingPower, Needed: votingPowerNeeded}
826887
}
827888

‎types/validator_set_test.go

+32-5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"math"
77
"sort"
8+
"strconv"
89
"strings"
910
"testing"
1011
"testing/quick"
@@ -725,7 +726,8 @@ func TestValidatorSet_VerifyCommit_All(t *testing.T) {
725726

726727
for _, tc := range testCases {
727728
tc := tc
728-
t.Run(tc.description, func(t *testing.T) {
729+
countAllSignatures := false
730+
f := func(t *testing.T) {
729731
err := vset.VerifyCommit(tc.chainID, tc.blockID, tc.height, tc.commit)
730732
if tc.expErr {
731733
if assert.Error(t, err, "VerifyCommit") {
@@ -735,15 +737,22 @@ func TestValidatorSet_VerifyCommit_All(t *testing.T) {
735737
assert.NoError(t, err, "VerifyCommit")
736738
}
737739

738-
err = vset.VerifyCommitLight(tc.chainID, tc.blockID, tc.height, tc.commit)
740+
if countAllSignatures {
741+
err = vset.VerifyCommitLightAllSignatures(tc.chainID, tc.blockID, tc.height, tc.commit)
742+
} else {
743+
err = vset.VerifyCommitLight(tc.chainID, tc.blockID, tc.height, tc.commit)
744+
}
739745
if tc.expErr {
740746
if assert.Error(t, err, "VerifyCommitLight") {
741747
assert.Contains(t, err.Error(), tc.description, "VerifyCommitLight")
742748
}
743749
} else {
744750
assert.NoError(t, err, "VerifyCommitLight")
745751
}
746-
})
752+
}
753+
t.Run(tc.description+"/"+strconv.FormatBool(countAllSignatures), f)
754+
countAllSignatures = true
755+
t.Run(tc.description+"/"+strconv.FormatBool(countAllSignatures), f)
747756
}
748757
}
749758

@@ -772,7 +781,7 @@ func TestValidatorSet_VerifyCommit_CheckAllSignatures(t *testing.T) {
772781
}
773782
}
774783

775-
func TestValidatorSet_VerifyCommitLight_ReturnsAsSoonAsMajorityOfVotingPowerSigned(t *testing.T) {
784+
func TestValidatorSet_VerifyCommitLight_ReturnsAsSoonAsMajOfVotingPowerSignedIffNotAllSigs(t *testing.T) {
776785
var (
777786
chainID = "test_chain_id"
778787
h = int64(3)
@@ -783,6 +792,9 @@ func TestValidatorSet_VerifyCommitLight_ReturnsAsSoonAsMajorityOfVotingPowerSign
783792
commit, err := MakeCommit(blockID, h, 0, voteSet, vals, time.Now())
784793
require.NoError(t, err)
785794

795+
err = valSet.VerifyCommitLightAllSignatures(chainID, blockID, h, commit)
796+
assert.NoError(t, err)
797+
786798
// malleate 4th signature (3 signatures are enough for 2/3+)
787799
vote := voteSet.GetByIndex(3)
788800
v := vote.ToProto()
@@ -793,9 +805,11 @@ func TestValidatorSet_VerifyCommitLight_ReturnsAsSoonAsMajorityOfVotingPowerSign
793805

794806
err = valSet.VerifyCommitLight(chainID, blockID, h, commit)
795807
assert.NoError(t, err)
808+
err = valSet.VerifyCommitLightAllSignatures(chainID, blockID, h, commit)
809+
assert.Error(t, err) // counting all signatures detects the malleated signature
796810
}
797811

798-
func TestValidatorSet_VerifyCommitLightTrusting_ReturnsAsSoonAsTrustLevelOfVotingPowerSigned(t *testing.T) {
812+
func TestValidatorSet_VerifyCommitLightTrusting_ReturnsAsSoonAsTrustLevelSignedIffNotAllSigs(t *testing.T) {
799813
var (
800814
chainID = "test_chain_id"
801815
h = int64(3)
@@ -806,6 +820,13 @@ func TestValidatorSet_VerifyCommitLightTrusting_ReturnsAsSoonAsTrustLevelOfVotin
806820
commit, err := MakeCommit(blockID, h, 0, voteSet, vals, time.Now())
807821
require.NoError(t, err)
808822

823+
err = valSet.VerifyCommitLightTrustingAllSignatures(
824+
chainID,
825+
commit,
826+
cmtmath.Fraction{Numerator: 1, Denominator: 3},
827+
)
828+
assert.NoError(t, err)
829+
809830
// malleate 3rd signature (2 signatures are enough for 1/3+ trust level)
810831
vote := voteSet.GetByIndex(2)
811832
v := vote.ToProto()
@@ -816,6 +837,12 @@ func TestValidatorSet_VerifyCommitLightTrusting_ReturnsAsSoonAsTrustLevelOfVotin
816837

817838
err = valSet.VerifyCommitLightTrusting(chainID, commit, cmtmath.Fraction{Numerator: 1, Denominator: 3})
818839
assert.NoError(t, err)
840+
err = valSet.VerifyCommitLightTrustingAllSignatures(
841+
chainID,
842+
commit,
843+
cmtmath.Fraction{Numerator: 1, Denominator: 3},
844+
)
845+
assert.Error(t, err) // counting all signatures detects the malleated signature
819846
}
820847

821848
func TestEmptySet(t *testing.T) {

0 commit comments

Comments
 (0)
Please sign in to comment.