Skip to content

Commit

Permalink
Merge pull request #410 from spacemeshos/351-avoid-registration-slip
Browse files Browse the repository at this point in the history
Add proof deadline to /Submit GRPC
  • Loading branch information
poszu authored Sep 29, 2023
2 parents 01820bd + 060bdcb commit bc84a36
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 138 deletions.
16 changes: 15 additions & 1 deletion registration/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ type roundConfig interface {
RoundEnd(genesis time.Time, epoch uint) time.Time
}

var ErrTooLateToRegister = errors.New("too late to register for the desired round")

// Registration orchestrates rounds functionality
// It is responsible for:
// - registering challenges,
Expand Down Expand Up @@ -287,6 +289,7 @@ func (r *Registration) Submit(
challenge, nodeID []byte,
nonce uint64,
powParams PowParams,
deadline time.Time,
) (epoch uint, roundEnd time.Time, err error) {
logger := logging.FromContext(ctx)

Expand All @@ -299,6 +302,17 @@ func (r *Registration) Submit(

r.openRoundMutex.RLock()
epoch = r.openRound.epoch
endTime := r.roundCfg.RoundEnd(r.genesis, epoch)
if !deadline.IsZero() && endTime.After(deadline) {
r.openRoundMutex.RUnlock()
logger.Debug(
"rejecting registration as too late",
zap.Uint("round", epoch),
zap.Time("deadline", deadline),
zap.Time("end_time", endTime),
)
return epoch, endTime, ErrTooLateToRegister
}
done, err := r.openRound.submit(ctx, nodeID, challenge)
r.openRoundMutex.RUnlock()

Expand All @@ -319,7 +333,7 @@ func (r *Registration) Submit(
return 0, time.Time{}, err
}

return epoch, r.roundCfg.RoundEnd(r.genesis, epoch), nil
return epoch, endTime, nil
}

func (r *Registration) OpenRound() uint {
Expand Down
4 changes: 2 additions & 2 deletions registration/registration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ func TestSubmitIdempotence(t *testing.T) {
eg.Go(func() error { return r.Run(ctx) })

// Submit challenge
epoch, _, err := r.Submit(context.Background(), challenge, nodeID, nonce, registration.PowParams{})
epoch, _, err := r.Submit(context.Background(), challenge, nodeID, nonce, registration.PowParams{}, time.Time{})
req.NoError(err)
req.Equal(uint(0), epoch)

// Try again - it should return the same result
epoch, _, err = r.Submit(context.Background(), challenge, nodeID, nonce, registration.PowParams{})
epoch, _, err = r.Submit(context.Background(), challenge, nodeID, nonce, registration.PowParams{}, time.Time{})
req.NoError(err)
req.Equal(uint(0), epoch)

Expand Down
288 changes: 154 additions & 134 deletions release/proto/go/rpc/api/v1/api.pb.go

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions release/proto/openapiv2/rpc/api/v1/api.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,11 @@
"type": "string",
"format": "byte",
"title": "The user's signature over the challenge"
},
"deadline": {
"type": "string",
"format": "date-time",
"description": "The time by which the proof is needed.\nIf the currently open round will end after this time\nand the proof cannot be generated by this time, the request will be rejected."
}
}
},
Expand Down
5 changes: 5 additions & 0 deletions rpc/api/v1/api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ syntax = "proto3";

import "google/api/annotations.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";

package rpc.api.v1;

Expand Down Expand Up @@ -69,6 +70,10 @@ message SubmitRequest {
bytes pubkey = 5;
// The user's signature over the challenge
bytes signature = 6;
// The time by which the proof is needed.
// If the currently open round will end after this time
// and the proof cannot be generated by this time, the request will be rejected.
google.protobuf.Timestamp deadline = 7;
}

message SubmitResponse {
Expand Down
9 changes: 8 additions & 1 deletion rpc/rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,21 @@ func (r *rpcServer) Submit(ctx context.Context, in *api.SubmitRequest) (*api.Sub
Difficulty: uint(in.GetPowParams().GetDifficulty()),
}

epoch, end, err := r.registration.Submit(ctx, in.Challenge, in.Pubkey, in.Nonce, powParams)
var deadline time.Time
if in.Deadline != nil {
deadline = in.Deadline.AsTime()
}

epoch, end, err := r.registration.Submit(ctx, in.Challenge, in.Pubkey, in.Nonce, powParams, deadline)
switch {
case errors.Is(err, registration.ErrInvalidPow) || errors.Is(err, registration.ErrInvalidPowParams):
return nil, status.Error(codes.InvalidArgument, err.Error())
case errors.Is(err, registration.ErrMaxMembersReached):
return nil, status.Error(codes.ResourceExhausted, err.Error())
case errors.Is(err, registration.ErrConflictingRegistration):
return nil, status.Error(codes.AlreadyExists, err.Error())
case errors.Is(err, registration.ErrTooLateToRegister):
return nil, status.Error(codes.FailedPrecondition, err.Error())
case errors.Is(err, context.Canceled):
return nil, status.Error(codes.Canceled, err.Error())
case err != nil:
Expand Down
53 changes: 53 additions & 0 deletions server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"

"github.com/spacemeshos/poet/hash"
"github.com/spacemeshos/poet/logging"
Expand Down Expand Up @@ -377,6 +378,58 @@ func TestSubmittingChallengeTwice(t *testing.T) {
)
}

func TestSubmittingWithNeedByTimestamp(t *testing.T) {
t.Parallel()
req := require.New(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ctx = logging.NewContext(ctx, zaptest.NewLogger(t))

cfg := server.DefaultConfig()
cfg.PoetDir = t.TempDir()
cfg.RawRPCListener = randomHost
cfg.RawRESTListener = randomHost
cfg.Registration.PowDifficulty = 0
cfg.Round = &server.RoundConfig{
EpochDuration: time.Hour,
PhaseShift: time.Minute * 10,
}

srv, client := spawnPoet(ctx, t, *cfg)
t.Cleanup(func() { assert.NoError(t, srv.Close()) })

var eg errgroup.Group
eg.Go(func() error {
return srv.Start(ctx)
})
t.Cleanup(func() { assert.NoError(t, eg.Wait()) })

powParams, err := client.PowParams(context.Background(), &api.PowParamsRequest{})
req.NoError(err)

submitChallenge := func(ch []byte, deadline time.Time) error {
pubKey, privKey, err := ed25519.GenerateKey(rand.Reader)
req.NoError(err)
_, err = client.Submit(context.Background(), &api.SubmitRequest{
Challenge: ch,
Pubkey: pubKey,
PowParams: powParams.PowParams,
Signature: ed25519.Sign(privKey, ch),
Deadline: timestamppb.New(deadline),
})
return err
}

round0End := cfg.Round.RoundEnd(cfg.Genesis.Time(), 0)

req.NoError(submitChallenge([]byte("at round end"), round0End))
req.NoError(submitChallenge([]byte("after round ends"), round0End.Add(time.Minute)))
req.ErrorIs(
submitChallenge([]byte("before round ends"), round0End.Add(-time.Minute)),
status.Error(codes.FailedPrecondition, registration.ErrTooLateToRegister.Error()),
)
}

func TestPersistingPowParams(t *testing.T) {
t.Parallel()
req := require.New(t)
Expand Down

0 comments on commit bc84a36

Please sign in to comment.