Skip to content

Commit 61cd975

Browse files
committed
Configurable max clock drift and minor updates
1 parent dabcacd commit 61cd975

File tree

5 files changed

+150
-49
lines changed

5 files changed

+150
-49
lines changed

modules/network/keeper/msg_server.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,15 @@ func (k msgServer) Attest(goCtx context.Context, msg *types.MsgAttest) (*types.M
4747
return nil, errors.Wrapf(sdkerrors.ErrNotFound, "validator index not found for %s", msg.Validator)
4848
}
4949

50-
if err := k.updateAttestationBitmap(ctx, msg, valIndexPos); err != nil {
51-
return nil, errors.Wrap(err, "update attestation bitmap")
52-
}
53-
5450
vote, err := k.verifyVote(ctx, msg)
5551
if err != nil {
5652
return nil, err
5753
}
5854

55+
if err := k.updateAttestationBitmap(ctx, msg, valIndexPos); err != nil {
56+
return nil, errors.Wrap(err, "update attestation bitmap")
57+
}
58+
5959
if err := k.SetSignature(ctx, msg.Height, msg.Validator, vote.Signature); err != nil {
6060
return nil, errors.Wrap(err, "store signature")
6161
}
@@ -156,6 +156,14 @@ func (k msgServer) verifyVote(ctx sdk.Context, msg *types.MsgAttest) (*cmtproto.
156156
if msg.Height != vote.Height {
157157
return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "vote height does not match attestation height")
158158
}
159+
if senderAddr, err := sdk.ValAddressFromBech32(msg.Validator); err != nil {
160+
return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid validator address: %s", err)
161+
} else if !bytes.Equal(senderAddr, vote.ValidatorAddress) {
162+
return nil, errors.Wrapf(sdkerrors.ErrUnauthorized, "vote validator address does not match attestation validator address")
163+
}
164+
if len(vote.Signature) == 0 {
165+
return nil, sdkerrors.ErrInvalidRequest.Wrap("empty signature")
166+
}
159167
header, _, err := k.blockSource.GetBlockData(ctx, uint64(vote.Height))
160168
if err != nil {
161169
return nil, errors.Wrapf(err, "block data for height %d", vote.Height)
@@ -168,8 +176,8 @@ func (k msgServer) verifyVote(ctx sdk.Context, msg *types.MsgAttest) (*cmtproto.
168176
return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "vote block ID hash does not match app hash for height %d", vote.Height)
169177
}
170178

171-
maxClockDrift := 5 * time.Second // todo (Alex): make this a parameter?
172-
if drift := vote.Timestamp.Sub(header.Time()); drift < 0*time.Nanosecond || drift > maxClockDrift {
179+
maxClockDrift := time.Duration(k.GetParams(ctx).MaxClockDrift)
180+
if drift := vote.Timestamp.Sub(header.Time()); drift < 0 || drift > maxClockDrift {
173181
return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "vote timestamp drift exceeds limit: %s", drift)
174182
}
175183

modules/network/keeper/msg_server_test.go

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -275,10 +275,11 @@ func TestAttest(t *testing.T) {
275275

276276
func TestVerifyVote(t *testing.T) {
277277
var (
278-
myHash = sha256.Sum256([]byte("app_hash"))
279-
myAppHash = myHash[:]
280-
validatorPrivKey = ed25519.GenPrivKey()
281-
valAddrStr = sdk.ValAddress(validatorPrivKey.PubKey().Address()).String()
278+
myHash = sha256.Sum256([]byte("app_hash"))
279+
myAppHash = myHash[:]
280+
validatorPrivKey = ed25519.GenPrivKey()
281+
valAddrStr = sdk.ValAddress(validatorPrivKey.PubKey().Address()).String()
282+
otherValidatorPrivKey = ed25519.GenPrivKey()
282283
)
283284

284285
// Setup test environment with block store
@@ -299,12 +300,14 @@ func TestVerifyVote(t *testing.T) {
299300

300301
testCases := map[string]struct {
301302
voteFn func(t *testing.T) *cmtproto.Vote
303+
sender string
302304
expErr error
303305
}{
304306
"valid vote": {
305307
voteFn: func(t *testing.T) *cmtproto.Vote {
306308
return VoteFixture(myAppHash, validatorPrivKey)
307309
},
310+
sender: valAddrStr,
308311
},
309312
"timestamp drift": {
310313
voteFn: func(t *testing.T) *cmtproto.Vote {
@@ -313,6 +316,7 @@ func TestVerifyVote(t *testing.T) {
313316
vote.Signature = must(validatorPrivKey.Sign(cmttypes.VoteSignBytes("testing", vote)))
314317
})
315318
},
319+
sender: valAddrStr,
316320
expErr: sdkerrors.ErrInvalidRequest,
317321
},
318322
"block data not found": {
@@ -322,36 +326,51 @@ func TestVerifyVote(t *testing.T) {
322326
vote.Signature = must(validatorPrivKey.Sign(cmttypes.VoteSignBytes("testing", vote)))
323327
})
324328
},
329+
sender: valAddrStr,
325330
expErr: sdkerrors.ErrInvalidRequest,
326331
},
327332
"vote and block hash mismatch": {
328333
voteFn: func(t *testing.T) *cmtproto.Vote {
329334
hash := sha256.Sum256([]byte("wrong_hash"))
330335
return VoteFixture(hash[:], validatorPrivKey)
331336
},
337+
sender: valAddrStr,
332338
expErr: sdkerrors.ErrInvalidRequest,
333339
},
334340
"validator not found": {
335341
voteFn: func(t *testing.T) *cmtproto.Vote {
336342
return VoteFixture(myAppHash, ed25519.GenPrivKey())
337343
},
338-
expErr: sdkerrors.ErrNotFound,
344+
expErr: sdkerrors.ErrUnauthorized,
345+
sender: sdk.ValAddress(otherValidatorPrivKey.PubKey().Address()).String(),
339346
},
340347
"invalid vote signature": {
341348
voteFn: func(t *testing.T) *cmtproto.Vote {
342349
return VoteFixture(myAppHash, validatorPrivKey, func(vote *cmtproto.Vote) {
343350
vote.Signature = []byte("invalid signature")
344351
})
345352
},
353+
sender: valAddrStr,
346354
expErr: sdkerrors.ErrInvalidRequest,
347355
},
356+
"invalid sender": {
357+
voteFn: func(t *testing.T) *cmtproto.Vote {
358+
return VoteFixture(myAppHash, validatorPrivKey)
359+
},
360+
sender: sdk.ValAddress(otherValidatorPrivKey.PubKey().Address()).String(),
361+
expErr: sdkerrors.ErrUnauthorized,
362+
},
348363
}
349364
for name, spec := range testCases {
350365
t.Run(name, func(t *testing.T) {
351366
ctx, _ := parentCtx.CacheContext()
352367

353368
// when
354-
vote, err := env.Server.verifyVote(ctx, &types.MsgAttest{Height: 10, Vote: must(proto.Marshal(spec.voteFn(t)))})
369+
vote, err := env.Server.verifyVote(ctx, &types.MsgAttest{
370+
Height: 10,
371+
Validator: spec.sender,
372+
Vote: must(proto.Marshal(spec.voteFn(t))),
373+
})
355374

356375
// then
357376
if spec.expErr != nil {

modules/network/types/params.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package types
22

33
import (
44
"fmt"
5+
"time"
56

67
"cosmossdk.io/math"
78
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
@@ -32,13 +33,15 @@ func NewParams(
3233
minParticipation math.LegacyDec,
3334
pruneAfter uint64,
3435
signMode SignMode,
36+
maxClockDrift time.Duration,
3537
) Params {
3638
return Params{
3739
EpochLength: epochLength,
3840
QuorumFraction: quorumFraction.String(),
3941
MinParticipation: minParticipation.String(),
4042
PruneAfter: pruneAfter,
4143
SignMode: signMode,
44+
MaxClockDrift: maxClockDrift,
4245
}
4346
}
4447

@@ -50,6 +53,7 @@ func DefaultParams() Params {
5053
DefaultMinParticipation,
5154
DefaultPruneAfter,
5255
DefaultSignMode,
56+
5*time.Second,
5357
)
5458
}
5559

@@ -81,6 +85,9 @@ func (p Params) Validate() error {
8185
if err := validateSignMode(p.SignMode); err != nil {
8286
return err
8387
}
88+
if p.MaxClockDrift < 0 {
89+
return fmt.Errorf("max clock drift must be positive: %s", p.MaxClockDrift)
90+
}
8491
return nil
8592
}
8693

modules/network/types/types.pb.go

Lines changed: 97 additions & 37 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

modules/proto/rollkitsdk/network/v1/types.proto

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ option go_package = "github.com/rollkit/go-execution-abci/modules/network/types"
77
import "gogoproto/gogo.proto";
88
import "cosmos_proto/cosmos.proto";
99
import "google/protobuf/duration.proto";
10+
import "amino/amino.proto";
11+
1012

1113
// Params defines the parameters for the network module.
1214
message Params {
@@ -24,6 +26,11 @@ message Params {
2426

2527
// sign_mode determines when validators must sign
2628
SignMode sign_mode = 5;
29+
30+
// max_clock_drift maximum a timestamp for a vote can drift
31+
google.protobuf.Duration max_clock_drift = 6
32+
[(gogoproto.stdduration) = true, (gogoproto.nullable) = false, (amino.dont_omitempty) = true];
33+
2734
}
2835

2936
// SignMode defines when validators must sign

0 commit comments

Comments
 (0)