Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add proof deadline to /Submit GRPC #410

Merged
merged 2 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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