diff --git a/beacon-chain/blockchain/process_block_test.go b/beacon-chain/blockchain/process_block_test.go index 4c290fa4c..b8e007e9b 100644 --- a/beacon-chain/blockchain/process_block_test.go +++ b/beacon-chain/blockchain/process_block_test.go @@ -92,10 +92,10 @@ func TestStore_OnBlock(t *testing.T) { wantErrString: "provided block root does not have block saved in the db", }, { - name: "block is from the feature", + name: "block is from the future", blk: ðpb.BeaconBlock{ParentRoot: randomParentRoot[:], Slot: params.BeaconConfig().FarFutureEpoch}, s: st.Copy(), - wantErrString: "could not process slot from the future", + wantErrString: "far distant future", }, { name: "could not get finalized block", diff --git a/beacon-chain/core/helpers/BUILD.bazel b/beacon-chain/core/helpers/BUILD.bazel index 0515f2626..a64d97eee 100644 --- a/beacon-chain/core/helpers/BUILD.bazel +++ b/beacon-chain/core/helpers/BUILD.bazel @@ -70,6 +70,7 @@ go_test( "//shared/featureconfig:go_default_library", "//shared/hashutil:go_default_library", "//shared/params:go_default_library", + "//shared/roughtime:go_default_library", "//shared/sliceutil:go_default_library", "//shared/testutil:go_default_library", "@com_github_google_gofuzz//:go_default_library", diff --git a/beacon-chain/core/helpers/slot_epoch.go b/beacon-chain/core/helpers/slot_epoch.go index 1f69af56a..0f54b8457 100644 --- a/beacon-chain/core/helpers/slot_epoch.go +++ b/beacon-chain/core/helpers/slot_epoch.go @@ -2,6 +2,7 @@ package helpers import ( "fmt" + "math" "time" stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state" @@ -94,16 +95,28 @@ const TimeShiftTolerance = 500 * time.Millisecond // ms // VerifySlotTime validates the input slot is not from the future. func VerifySlotTime(genesisTime uint64, slot uint64, timeTolerance time.Duration) error { - // denominate everything in milliseconds - slotTime := 1000 * (genesisTime + slot*params.BeaconConfig().SecondsPerSlot) - currentTime := 1000 * uint64(roughtime.Now().Unix()) - tolerance := uint64(timeTolerance.Milliseconds()) - if slotTime > currentTime+tolerance { - return fmt.Errorf("could not process slot from the future, slot time(ms) %d > current time(ms) %d", slotTime, currentTime) + slotTime, err := SlotToTime(genesisTime, slot) + if err != nil { + return err + } + currentTime := roughtime.Now() + diff := slotTime.Sub(currentTime) + + if diff > timeTolerance { + return fmt.Errorf("could not process slot from the future, slot time %s > current time %s", slotTime, currentTime) } return nil } +// SlotToTime takes the given slot and genesis time to determine the start time of the slot. +func SlotToTime(genesisTimeSec uint64, slot uint64) (time.Time, error) { + if slot >= math.MaxInt64 { + return time.Unix(0, 0), fmt.Errorf("slot (%d) is in the far distant future", slot) + } + timeSinceGenesis := slot * params.BeaconConfig().SecondsPerSlot + return time.Unix(int64(genesisTimeSec+timeSinceGenesis), 0), nil +} + // SlotsSince computes the number of time slots that have occurred since the given timestamp. func SlotsSince(time time.Time) uint64 { return uint64(roughtime.Since(time).Seconds()) / params.BeaconConfig().SecondsPerSlot diff --git a/beacon-chain/core/helpers/slot_epoch_test.go b/beacon-chain/core/helpers/slot_epoch_test.go index e81fc94e5..84695f50e 100644 --- a/beacon-chain/core/helpers/slot_epoch_test.go +++ b/beacon-chain/core/helpers/slot_epoch_test.go @@ -1,7 +1,11 @@ package helpers import ( + "github.com/prysmaticlabs/prysm/shared/roughtime" + "math" + "reflect" "testing" + "time" beaconstate "github.com/prysmaticlabs/prysm/beacon-chain/state" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" @@ -200,3 +204,104 @@ func TestRoundUpToNearestEpoch_OK(t *testing.T) { } } } + +func TestSlotToTime(t *testing.T) { + type args struct { + genesisTimeSec uint64 + slot uint64 + } + tests := []struct { + name string + args args + want time.Time + wantErr bool + }{ + { + name: "slot_0", + args: args{ + genesisTimeSec: 0, + slot: 0, + }, + want: time.Unix(0, 0), + wantErr: false, + }, + { + name: "slot_1", + args: args{ + genesisTimeSec: 0, + slot: 1, + }, + want: time.Unix(int64(1*params.BeaconConfig().SecondsPerSlot), 0), + wantErr: false, + }, + { + name: "slot_12", + args: args{ + genesisTimeSec: 500, + slot: 12, + }, + want: time.Unix(500+int64(12*params.BeaconConfig().SecondsPerSlot), 0), + wantErr: false, + }, + { + name: "overflow", + args: args{ + genesisTimeSec: 500, + slot: math.MaxUint64, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got, err := SlotToTime(tt.args.genesisTimeSec, tt.args.slot); (err != nil ) != tt.wantErr && !reflect.DeepEqual(got, tt.want) { + t.Errorf("SlotToTime() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestVerifySlotTime(t *testing.T) { + type args struct { + genesisTime int64 + slot uint64 + timeTolerance time.Duration + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "Past slot", + args: args{ + genesisTime: roughtime.Now().Add(-1 * 5 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second).Unix(), + slot: 3, + }, + wantErr: false, + }, + { + name: "within tolerance", + args: args{ + genesisTime: roughtime.Now().Add(-1 * 5 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second).Add(20 * time.Millisecond).Unix(), + slot: 5, + }, + wantErr: false, + }, + { + name: "future slot", + args: args{ + genesisTime: roughtime.Now().Add(-1 * 5 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second).Unix(), + slot: 6, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := VerifySlotTime(uint64(tt.args.genesisTime), tt.args.slot, tt.args.timeTolerance); (err != nil) != tt.wantErr { + t.Errorf("VerifySlotTime() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +}