Skip to content

Commit b23f942

Browse files
authored
fix(beacon, blockchain): Fix payload timestamp build and verification (#2160)
1 parent 8a04173 commit b23f942

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+344
-170
lines changed

mod/beacon/blockchain/execution_engine.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"context"
2525
"time"
2626

27+
payloadtime "github.com/berachain/beacon-kit/mod/beacon/payload-time"
2728
"github.com/berachain/beacon-kit/mod/config/pkg/spec"
2829
engineprimitives "github.com/berachain/beacon-kit/mod/engine-primitives/pkg/engine-primitives"
2930
)
@@ -76,7 +77,11 @@ func (s *Service[
7677
return
7778
}
7879

79-
nextPayloadTime := blk.GetNextPayloadTimestamp().Unwrap()
80+
nextPayloadTime := payloadtime.Next(
81+
blk.GetConsensusTime(),
82+
lph.GetTimestamp(),
83+
true, // buildOptimistically
84+
).Unwrap()
8085

8186
// We set timestamp check on Bartio for backward compatibility reasons
8287
// TODO: drop this we drop other Bartio special cases.

mod/beacon/blockchain/process.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@ func (s *Service[
128128
// actually irrelevant at this point.
129129
SkipPayloadVerification: false,
130130

131-
ProposerAddress: blk.GetProposerAddress(),
132-
NextPayloadTimestamp: blk.GetNextPayloadTimestamp(),
131+
ProposerAddress: blk.GetProposerAddress(),
132+
ConsensusTime: blk.GetConsensusTime(),
133133
},
134134
st,
135135
blk.GetBeaconBlock(),

mod/beacon/blockchain/receive.go

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"context"
2525
"time"
2626

27+
payloadtime "github.com/berachain/beacon-kit/mod/beacon/payload-time"
2728
engineerrors "github.com/berachain/beacon-kit/mod/engine-primitives/pkg/errors"
2829
"github.com/berachain/beacon-kit/mod/errors"
2930
"github.com/berachain/beacon-kit/mod/primitives/pkg/transition"
@@ -32,14 +33,14 @@ import (
3233
// VerifyIncomingBlock verifies the state root of an incoming block
3334
// and logs the process.
3435
func (s *Service[
35-
_, ConsensusBlockT, BeaconBlockT, _, _, _, _, _, _, _, _,
36+
_, ConsensusBlockT, BeaconBlockT, _, _, _, _, _, ExecutionPayloadHeaderT, _, _,
3637
]) VerifyIncomingBlock(
3738
ctx context.Context,
3839
blk ConsensusBlockT,
3940
) error {
4041
var (
41-
beaconBlk = blk.GetBeaconBlock()
42-
nextPayloadTimestamp = blk.GetNextPayloadTimestamp()
42+
beaconBlk = blk.GetBeaconBlock()
43+
consensusTime = blk.GetConsensusTime()
4344
)
4445

4546
// Grab a copy of the state to verify the incoming block.
@@ -81,10 +82,20 @@ func (s *Service[
8182
)
8283

8384
if s.shouldBuildOptimisticPayloads() {
85+
var lph ExecutionPayloadHeaderT
86+
lph, err = preState.GetLatestExecutionPayloadHeader()
87+
if err != nil {
88+
return err
89+
}
90+
8491
go s.handleRebuildPayloadForRejectedBlock(
8592
ctx,
8693
preState,
87-
nextPayloadTimestamp,
94+
payloadtime.Next(
95+
consensusTime,
96+
lph.GetTimestamp(),
97+
true, // buildOptimistically
98+
),
8899
)
89100
}
90101

@@ -98,11 +109,20 @@ func (s *Service[
98109
)
99110

100111
if s.shouldBuildOptimisticPayloads() {
112+
lph, err := postState.GetLatestExecutionPayloadHeader()
113+
if err != nil {
114+
return err
115+
}
116+
101117
go s.handleOptimisticPayloadBuild(
102118
ctx,
103119
postState,
104120
beaconBlk,
105-
nextPayloadTimestamp,
121+
payloadtime.Next(
122+
consensusTime,
123+
lph.GetTimestamp(),
124+
true, // buildOptimistically
125+
),
106126
)
107127
}
108128

@@ -129,7 +149,7 @@ func (s *Service[
129149
SkipValidateResult: false,
130150
SkipValidateRandao: false,
131151
ProposerAddress: blk.GetProposerAddress(),
132-
NextPayloadTimestamp: blk.GetNextPayloadTimestamp(),
152+
ConsensusTime: blk.GetConsensusTime(),
133153
},
134154
st, blk.GetBeaconBlock(),
135155
)

mod/beacon/blockchain/types.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,9 @@ type ConsensusBlock[BeaconBlockT any] interface {
4949
// selected by consensus to propose the block
5050
GetProposerAddress() []byte
5151

52-
// GetNextPayloadTimestamp returns the timestamp proposed by
53-
// consensus for the next payload to be proposed. It is also
54-
// used to bound current payload upon validation
55-
GetNextPayloadTimestamp() math.U64
52+
// GetConsensusTime returns the timestamp of current consensus request.
53+
// It is used to build next payload and to validate currentpayload.
54+
GetConsensusTime() math.U64
5655
}
5756

5857
// BeaconBlock represents a beacon block interface.

mod/beacon/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ require (
1111
github.com/berachain/beacon-kit/mod/errors v0.0.0-20240806211103-d1105603bfc0
1212
github.com/berachain/beacon-kit/mod/log v0.0.0-20240809202957-3e3f169ad720
1313
github.com/berachain/beacon-kit/mod/primitives v0.0.0-20240911165923-82f71ec86570
14+
github.com/stretchr/testify v1.9.0
1415
golang.org/x/sync v0.8.0
1516
)
1617

@@ -84,7 +85,6 @@ require (
8485
github.com/rogpeppe/go-internal v1.12.0 // indirect
8586
github.com/sasha-s/go-deadlock v0.3.5 // indirect
8687
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
87-
github.com/stretchr/testify v1.9.0 // indirect
8888
github.com/supranational/blst v0.3.13 // indirect
8989
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
9090
github.com/tklauser/go-sysconf v0.3.14 // indirect

mod/beacon/payload-time/time.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
//
3+
// Copyright (C) 2024, Berachain Foundation. All rights reserved.
4+
// Use of this software is governed by the Business Source License included
5+
// in the LICENSE file of this repository and at www.mariadb.com/bsl11.
6+
//
7+
// ANY USE OF THE LICENSED WORK IN VIOLATION OF THIS LICENSE WILL AUTOMATICALLY
8+
// TERMINATE YOUR RIGHTS UNDER THIS LICENSE FOR THE CURRENT AND ALL OTHER
9+
// VERSIONS OF THE LICENSED WORK.
10+
//
11+
// THIS LICENSE DOES NOT GRANT YOU ANY RIGHT IN ANY TRADEMARK OR LOGO OF
12+
// LICENSOR OR ITS AFFILIATES (PROVIDED THAT YOU MAY USE A TRADEMARK OR LOGO OF
13+
// LICENSOR AS EXPRESSLY REQUIRED BY THIS LICENSE).
14+
//
15+
// TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
16+
// AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
17+
// EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
18+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
19+
// TITLE.
20+
21+
package payloadtime
22+
23+
import (
24+
"errors"
25+
"fmt"
26+
27+
"github.com/berachain/beacon-kit/mod/primitives/pkg/math"
28+
)
29+
30+
// ErrTooFarInTheFuture is returned when the payload timestamp
31+
// in a block exceeds the time bound.
32+
var ErrTooFarInTheFuture = errors.New("timestamp too far in the future")
33+
34+
func Verify(
35+
consensusTime,
36+
parentPayloadTimestamp,
37+
payloadTimestamp math.U64,
38+
) error {
39+
bound := max(
40+
consensusTime+1,
41+
parentPayloadTimestamp+1,
42+
)
43+
if payloadTimestamp > bound {
44+
return fmt.Errorf(
45+
"%w: timestamp bound: %d, got: %d",
46+
ErrTooFarInTheFuture,
47+
bound, payloadTimestamp,
48+
)
49+
}
50+
return nil
51+
}
52+
53+
func Next(
54+
consensusTime,
55+
parentPayloadTimestamp math.U64,
56+
buildOptimistically bool,
57+
) math.U64 {
58+
delta := math.U64(0)
59+
if buildOptimistically {
60+
// we're building a payload to be included into next block.
61+
// We estimate it to be included next second. If this estimate
62+
// turns out wrong (cause consensus block are finalized faster or
63+
// slower than consensusTime+1 sec), we're still fine as long as
64+
// Verify pass which should always to since:
65+
// Next.consensusTime <= Verify.consensusTime
66+
delta = 1
67+
}
68+
return max(
69+
consensusTime+delta,
70+
parentPayloadTimestamp+1,
71+
)
72+
}

mod/beacon/payload-time/time_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
//
3+
// Copyright (C) 2024, Berachain Foundation. All rights reserved.
4+
// Use of this software is governed by the Business Source License included
5+
// in the LICENSE file of this repository and at www.mariadb.com/bsl11.
6+
//
7+
// ANY USE OF THE LICENSED WORK IN VIOLATION OF THIS LICENSE WILL AUTOMATICALLY
8+
// TERMINATE YOUR RIGHTS UNDER THIS LICENSE FOR THE CURRENT AND ALL OTHER
9+
// VERSIONS OF THE LICENSED WORK.
10+
//
11+
// THIS LICENSE DOES NOT GRANT YOU ANY RIGHT IN ANY TRADEMARK OR LOGO OF
12+
// LICENSOR OR ITS AFFILIATES (PROVIDED THAT YOU MAY USE A TRADEMARK OR LOGO OF
13+
// LICENSOR AS EXPRESSLY REQUIRED BY THIS LICENSE).
14+
//
15+
// TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
16+
// AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
17+
// EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
18+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
19+
// TITLE.
20+
21+
package payloadtime_test
22+
23+
import (
24+
"testing"
25+
"time"
26+
27+
payloadtime "github.com/berachain/beacon-kit/mod/beacon/payload-time"
28+
"github.com/berachain/beacon-kit/mod/primitives/pkg/math"
29+
"github.com/stretchr/testify/require"
30+
)
31+
32+
// TestNextTimestampVerifies checks that next payload timestamp
33+
// built via payloadtime.Next always pass payloadtime.Verify.
34+
func TestNextTimestampVerifies(t *testing.T) {
35+
tests := []struct {
36+
name string
37+
times func() (time.Time, time.Time)
38+
expectedErr error
39+
}{
40+
{
41+
name: "Payload timestamp < consensus timestamp",
42+
times: func() (time.Time, time.Time) {
43+
consensusTime := time.Now().Truncate(time.Second)
44+
parentPayloadTimestamp := consensusTime.Add(-10 * time.Second)
45+
return consensusTime, parentPayloadTimestamp
46+
},
47+
expectedErr: nil,
48+
},
49+
{
50+
name: "Payload timestamp == consensus timestamp",
51+
times: func() (time.Time, time.Time) {
52+
consensusTime := time.Now().Truncate(time.Second)
53+
parentPayloadTimestamp := consensusTime
54+
return consensusTime, parentPayloadTimestamp
55+
},
56+
expectedErr: nil,
57+
},
58+
{
59+
name: "Payload timestamp > consensus timestamp",
60+
times: func() (time.Time, time.Time) {
61+
consensusTime := time.Now().Truncate(time.Second)
62+
parentPayloadTimestamp := consensusTime.Add(10 * time.Second)
63+
return consensusTime, parentPayloadTimestamp
64+
},
65+
expectedErr: nil,
66+
},
67+
}
68+
69+
for _, tt := range tests {
70+
t.Run(tt.name, func(t *testing.T) {
71+
consensusTime, parentPayloadTimestamp := tt.times()
72+
73+
// Optimistic build case
74+
nextPayload := payloadtime.Next(
75+
math.U64(consensusTime.Unix()),
76+
math.U64(parentPayloadTimestamp.Unix()),
77+
true, // buildOptimistically
78+
)
79+
80+
gotErr := payloadtime.Verify(
81+
math.U64(consensusTime.Unix()),
82+
math.U64(parentPayloadTimestamp.Unix()),
83+
nextPayload,
84+
)
85+
if tt.expectedErr == nil {
86+
require.NoError(t, gotErr)
87+
} else {
88+
require.ErrorIs(t, tt.expectedErr, gotErr)
89+
}
90+
91+
// Just in time build case
92+
nextPayload = payloadtime.Next(
93+
math.U64(consensusTime.Unix()),
94+
math.U64(parentPayloadTimestamp.Unix()),
95+
false, // buildOptimistically
96+
)
97+
98+
gotErr = payloadtime.Verify(
99+
math.U64(consensusTime.Unix()),
100+
math.U64(parentPayloadTimestamp.Unix()),
101+
nextPayload,
102+
)
103+
if tt.expectedErr == nil {
104+
require.NoError(t, gotErr)
105+
} else {
106+
require.ErrorIs(t, tt.expectedErr, gotErr)
107+
}
108+
})
109+
}
110+
}

0 commit comments

Comments
 (0)