diff --git a/sei-tendermint/types/light.go b/sei-tendermint/types/light.go index 056da36cdc..39f9714dae 100644 --- a/sei-tendermint/types/light.go +++ b/sei-tendermint/types/light.go @@ -94,6 +94,9 @@ func (lb *LightBlock) ToProto() (*tmproto.LightBlock, error) { if err != nil { return nil, err } + if lbp.ValidatorSet == nil { + return nil, ErrValidatorSetEmpty + } } return lbp, nil diff --git a/sei-tendermint/types/light_test.go b/sei-tendermint/types/light_test.go index 39a51041b2..c5415e1e28 100644 --- a/sei-tendermint/types/light_test.go +++ b/sei-tendermint/types/light_test.go @@ -88,8 +88,8 @@ func TestLightBlockProtobuf(t *testing.T) { }{ {"valid light block", sh, vals, false, false}, {"empty signed header", &SignedHeader{}, vals, false, false}, - {"empty validator set", sh, &ValidatorSet{}, false, true}, - {"empty light block", &SignedHeader{}, &ValidatorSet{}, false, true}, + {"empty validator set", sh, &ValidatorSet{}, true, true}, + {"empty light block", &SignedHeader{}, &ValidatorSet{}, true, true}, } for _, tc := range testCases { diff --git a/sei-tendermint/types/validator_set.go b/sei-tendermint/types/validator_set.go index 3318bcd14e..dafcd73a8a 100644 --- a/sei-tendermint/types/validator_set.go +++ b/sei-tendermint/types/validator_set.go @@ -811,7 +811,7 @@ func (valz ValidatorsByAddress) Swap(i, j int) { // ToProto converts ValidatorSet to protobuf func (vals *ValidatorSet) ToProto() (*tmproto.ValidatorSet, error) { if vals.IsNilOrEmpty() { - return &tmproto.ValidatorSet{}, nil // validator set should never be nil + return nil, nil } vp := new(tmproto.ValidatorSet) @@ -857,13 +857,16 @@ func ValidatorSetFromProto(vp *tmproto.ValidatorSet) (*ValidatorSet, error) { } vals.Validators = valsProto - p, err := ValidatorFromProto(vp.GetProposer()) - if err != nil { - return nil, fmt.Errorf("fromProto: validatorSet proposer error: %w", err) + if vp.GetProposer() != nil { + p, err := ValidatorFromProto(vp.GetProposer()) + if err != nil { + return nil, fmt.Errorf("fromProto: validatorSet proposer error: %w", err) + } + vals.Proposer = p + } else if len(vals.Validators) > 0 { + vals.Proposer = vals.findProposer() } - vals.Proposer = p - // NOTE: We can't trust the total voting power given to us by other peers. If someone were to // inject a non-zeo value that wasn't the correct voting power we could assume a wrong total // power hence we need to recompute it. diff --git a/sei-tendermint/types/validator_set_test.go b/sei-tendermint/types/validator_set_test.go index d573f68570..6649456f3b 100644 --- a/sei-tendermint/types/validator_set_test.go +++ b/sei-tendermint/types/validator_set_test.go @@ -1459,6 +1459,56 @@ func TestValidatorSetProtoBuf(t *testing.T) { } } +func TestValidatorSetFromProto_NilProposerWithValidators(t *testing.T) { + pk1 := ed25519.GenerateSecretKey().Public() + pk2 := ed25519.GenerateSecretKey().Public() + + protoValSet := &tmproto.ValidatorSet{ + Validators: []*tmproto.Validator{ + { + Address: pk1.Address(), + PubKey: crypto.PubKeyToProto(pk1), + VotingPower: 50, + ProposerPriority: 10, + }, + { + Address: pk2.Address(), + PubKey: crypto.PubKeyToProto(pk2), + VotingPower: 50, + ProposerPriority: -10, + }, + }, + Proposer: nil, + } + + valSet, err := ValidatorSetFromProto(protoValSet) + require.NoError(t, err) + require.NotNil(t, valSet.Proposer, "proposer should be derived when nil in proto") + require.Len(t, valSet.Validators, 2) + + found := false + for _, v := range valSet.Validators { + if bytes.Equal(v.Address, valSet.Proposer.Address) { + found = true + break + } + } + require.True(t, found, "derived proposer must be a member of the validator set") + + require.Equal(t, valSet.Proposer.Address, pk1.Address(), + "proposer should be the validator with highest ProposerPriority") +} + +func TestValidatorSetFromProto_EmptyProtoRoundtrip(t *testing.T) { + empty := &ValidatorSet{} + proto, err := empty.ToProto() + require.NoError(t, err) + require.Nil(t, proto, "ToProto on empty set should return nil") + + _, err = ValidatorSetFromProto(proto) + require.Error(t, err, "FromProto(nil) should return error") +} + // --------------------- // Sort validators by priority and address type validatorsByPriority []*Validator