Skip to content

Commit 75b338b

Browse files
committed
Add EngineNewPayloadV5Test
Signed-off-by: Miroslav Kovar <[email protected]>
1 parent 8e21140 commit 75b338b

File tree

2 files changed

+276
-11
lines changed

2 files changed

+276
-11
lines changed

ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV4Test.java

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ protected Set<ScheduledProtocolSpec.Hardfork> supportedHardforks() {
7373
return Set.of(pragueHardfork);
7474
}
7575

76-
private static final List<Request> VALID_REQUESTS =
76+
protected static final List<Request> VALID_REQUESTS =
7777
List.of(
7878
new Request(RequestType.DEPOSIT, Bytes.of(1)),
7979
new Request(RequestType.WITHDRAWAL, Bytes.of(1)),
@@ -113,7 +113,7 @@ public void shouldReturnUnsupportedForkIfBlockTimestampIsAfterPragueMilestone()
113113

114114
@Test
115115
public void shouldReturnUnsupportedForkIfBlockTimestampIsBeforePragueMilestone() {
116-
final BlockHeader cancunHeader = createBlockHeaderFixtureForV3(Optional.empty()).buildHeader();
116+
final BlockHeader cancunHeader = createPreActivationBlockHeader();
117117

118118
var resp = resp(mockEnginePayload(cancunHeader, emptyList()));
119119

@@ -128,7 +128,7 @@ public void shouldReturnUnsupportedForkIfBlockTimestampIsAtOrAfterFutureEipsMile
128128
.thenReturn(Optional.of(pragueHardfork.milestone() + 10L));
129129

130130
final BlockHeader futureEipsHeader =
131-
createBlockHeaderFixtureForV3(Optional.empty())
131+
createActivationBlockHeaderFixture(Optional.empty())
132132
.timestamp(pragueHardfork.milestone() + 10L)
133133
.requestsHash(BodyValidation.requestsHash(VALID_REQUESTS))
134134
.buildHeader();
@@ -144,7 +144,7 @@ public void shouldReturnUnsupportedForkIfBlockTimestampIsAtOrAfterFutureEipsMile
144144
public void shouldReturnInvalidIfRequestsIsNull_WhenRequestsAllowed() {
145145
var resp =
146146
respWithInvalidRequests(
147-
mockEnginePayload(createValidBlockHeaderForV4(Optional.empty()), emptyList()));
147+
mockEnginePayload(createValidBlockHeader(Optional.empty()), emptyList()));
148148

149149
assertThat(fromErrorResp(resp).getCode()).isEqualTo(INVALID_PARAMS.getCode());
150150
assertThat(fromErrorResp(resp).getMessage())
@@ -173,7 +173,7 @@ public void shouldReturnValidIfRequestsIsNotNull_WhenRequestsAllowed() {
173173
public void shouldReturnInvalidIfRequestsIsNotNull_WhenRequestsProhibited() {
174174
mockProhibitedRequestsValidator();
175175

176-
var resp = resp(mockEnginePayload(createValidBlockHeaderForV4(Optional.empty()), emptyList()));
176+
var resp = resp(mockEnginePayload(createValidBlockHeader(Optional.empty()), emptyList()));
177177

178178
final JsonRpcError jsonRpcError = fromErrorResp(resp);
179179
assertThat(jsonRpcError.getCode()).isEqualTo(INVALID_PARAMS.getCode());
@@ -216,17 +216,16 @@ public void validateExecutionRequests_whenPresent() {
216216
assertThat(res.isValid()).isTrue();
217217
}
218218

219-
private BlockHeader createValidBlockHeaderForV4(
220-
final Optional<List<Withdrawal>> maybeWithdrawals) {
221-
return createBlockHeaderFixtureForV3(maybeWithdrawals)
219+
protected BlockHeader createValidBlockHeader(final Optional<List<Withdrawal>> maybeWithdrawals) {
220+
return createActivationBlockHeaderFixture(maybeWithdrawals)
222221
.requestsHash(BodyValidation.requestsHash(VALID_REQUESTS))
223222
.timestamp(pragueHardfork.milestone())
224223
.buildHeader();
225224
}
226225

227-
private BlockHeaderTestFixture createBlockHeaderFixtureForV3(
226+
protected BlockHeaderTestFixture createActivationBlockHeaderFixture(
228227
final Optional<List<Withdrawal>> maybeWithdrawals) {
229-
BlockHeader parentBlockHeader =
228+
final BlockHeader parentBlockHeader =
230229
new BlockHeaderTestFixture()
231230
.baseFeePerGas(Wei.ONE)
232231
.timestamp(pragueHardfork.milestone() - 2) // cancun parent
@@ -246,9 +245,15 @@ private BlockHeaderTestFixture createBlockHeaderFixtureForV3(
246245
maybeParentBeaconBlockRoot.isPresent() ? maybeParentBeaconBlockRoot : null);
247246
}
248247

248+
protected BlockHeader createPreActivationBlockHeader() {
249+
return createActivationBlockHeaderFixture(Optional.empty())
250+
.timestamp(pragueHardfork.milestone() - 1)
251+
.buildHeader();
252+
}
253+
249254
@Override
250255
protected BlockHeader createBlockHeader(final Optional<List<Withdrawal>> maybeWithdrawals) {
251-
return createValidBlockHeaderForV4(maybeWithdrawals);
256+
return createValidBlockHeader(maybeWithdrawals);
252257
}
253258

254259
@Override
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
/*
2+
* Copyright contributors to Hyperledger Besu.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*
13+
* SPDX-License-Identifier: Apache-2.0
14+
*/
15+
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
16+
17+
import static java.util.Collections.emptyList;
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.hyperledger.besu.datatypes.HardforkId.MainnetHardforkId.FUTURE_EIPS;
20+
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.INVALID;
21+
import static org.mockito.ArgumentMatchers.any;
22+
import static org.mockito.Mockito.lenient;
23+
import static org.mockito.Mockito.mock;
24+
import static org.mockito.Mockito.times;
25+
import static org.mockito.Mockito.verify;
26+
import static org.mockito.Mockito.when;
27+
28+
import org.hyperledger.besu.datatypes.Address;
29+
import org.hyperledger.besu.datatypes.BlobGas;
30+
import org.hyperledger.besu.datatypes.StorageSlotKey;
31+
import org.hyperledger.besu.datatypes.Wei;
32+
import org.hyperledger.besu.ethereum.BlockProcessingOutputs;
33+
import org.hyperledger.besu.ethereum.BlockProcessingResult;
34+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.EnginePayloadParameter;
35+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.WithdrawalParameter;
36+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
37+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.EnginePayloadStatusResult;
38+
import org.hyperledger.besu.ethereum.core.BlockHeader;
39+
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
40+
import org.hyperledger.besu.ethereum.core.Withdrawal;
41+
import org.hyperledger.besu.ethereum.mainnet.BodyValidation;
42+
import org.hyperledger.besu.ethereum.mainnet.ScheduledProtocolSpec;
43+
import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList;
44+
import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList.AccountChanges;
45+
import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList.BalanceChange;
46+
import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList.CodeChange;
47+
import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList.NonceChange;
48+
import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList.SlotChanges;
49+
import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList.SlotRead;
50+
import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList.StorageChange;
51+
import org.hyperledger.besu.ethereum.mainnet.requests.MainnetRequestsValidator;
52+
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
53+
import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator;
54+
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
55+
56+
import java.util.List;
57+
import java.util.Optional;
58+
import java.util.Set;
59+
60+
import org.apache.tuweni.bytes.Bytes;
61+
import org.apache.tuweni.units.bigints.UInt256;
62+
import org.junit.jupiter.api.BeforeEach;
63+
import org.junit.jupiter.api.Test;
64+
65+
public class EngineNewPayloadV5Test extends EngineNewPayloadV4Test {
66+
67+
private static final BlockAccessList BLOCK_ACCESS_LIST = createSampleBlockAccessList();
68+
private static final String ENCODED_BLOCK_ACCESS_LIST = encodeBlockAccessList(BLOCK_ACCESS_LIST);
69+
private static final String INVALID_BLOCK_ACCESS_LIST_ENCODING = "0xzz";
70+
private static final String INVALID_BLOCK_ACCESS_LIST_RLP = "0x01";
71+
72+
private final ScheduledProtocolSpec.Hardfork futureEipsHardfork =
73+
new ScheduledProtocolSpec.Hardfork("FutureEips", 80);
74+
75+
@BeforeEach
76+
@Override
77+
public void before() {
78+
super.before();
79+
lenient().when(protocolSchedule.hardforkFor(any())).thenReturn(Optional.of(futureEipsHardfork));
80+
lenient()
81+
.when(protocolSchedule.milestoneFor(FUTURE_EIPS))
82+
.thenReturn(Optional.of(futureEipsHardfork.milestone()));
83+
lenient().when(protocolSpec.getGasCalculator()).thenReturn(new PragueGasCalculator());
84+
lenient().when(protocolSpec.getRequestsValidator()).thenReturn(new MainnetRequestsValidator());
85+
this.method =
86+
new EngineNewPayloadV5(
87+
vertx,
88+
protocolSchedule,
89+
protocolContext,
90+
mergeCoordinator,
91+
ethPeers,
92+
engineCallListener,
93+
new NoOpMetricsSystem());
94+
}
95+
96+
@Override
97+
protected Set<ScheduledProtocolSpec.Hardfork> supportedHardforks() {
98+
return Set.of(futureEipsHardfork);
99+
}
100+
101+
@Override
102+
public void shouldReturnExpectedMethodName() {
103+
assertThat(method.getName()).isEqualTo("engine_newPayloadV5");
104+
}
105+
106+
@Override
107+
public void shouldReturnUnsupportedForkIfBlockTimestampIsAtOrAfterFutureEipsMilestone() {
108+
final BlockHeader header =
109+
setupValidPayload(
110+
new BlockProcessingResult(
111+
Optional.of(
112+
new BlockProcessingOutputs(null, List.of(), Optional.of(VALID_REQUESTS)))),
113+
Optional.empty());
114+
when(blockchain.getBlockHeader(header.getParentHash()))
115+
.thenReturn(Optional.of(mock(BlockHeader.class)));
116+
when(mergeCoordinator.getLatestValidAncestor(header)).thenReturn(Optional.of(header.getHash()));
117+
118+
final JsonRpcResponse resp = resp(mockEnginePayload(header, emptyList()));
119+
120+
assertValidResponse(header, resp);
121+
}
122+
123+
@Test
124+
public void shouldReturnInvalidIfBlockAccessListIsMissing() {
125+
final BlockHeader header = createValidBlockHeader(Optional.empty());
126+
127+
final JsonRpcResponse resp = resp(super.mockEnginePayload(header, emptyList(), null, null));
128+
129+
final EnginePayloadStatusResult result = fromSuccessResp(resp);
130+
assertThat(result.getStatusAsString()).isEqualTo(INVALID.name());
131+
assertThat(result.getError()).isEqualTo("Missing block access list field");
132+
assertThat(result.getLatestValidHash()).isEmpty();
133+
verify(engineCallListener, times(1)).executionEngineCalled();
134+
}
135+
136+
@Test
137+
public void shouldReturnInvalidIfBlockAccessListHasInvalidHexEncoding() {
138+
final BlockHeader header = createValidBlockHeader(Optional.empty());
139+
140+
final JsonRpcResponse resp =
141+
resp(
142+
super.mockEnginePayload(header, emptyList(), null, INVALID_BLOCK_ACCESS_LIST_ENCODING));
143+
144+
final EnginePayloadStatusResult result = fromSuccessResp(resp);
145+
assertThat(result.getStatusAsString()).isEqualTo(INVALID.name());
146+
assertThat(result.getError()).isEqualTo("Invalid block access list encoding");
147+
assertThat(result.getLatestValidHash()).isEmpty();
148+
verify(engineCallListener, times(1)).executionEngineCalled();
149+
}
150+
151+
@Test
152+
public void shouldReturnInvalidIfBlockAccessListHasInvalidRlpEncoding() {
153+
final BlockHeader header = createValidBlockHeader(Optional.empty());
154+
155+
final JsonRpcResponse resp =
156+
resp(super.mockEnginePayload(header, emptyList(), null, INVALID_BLOCK_ACCESS_LIST_RLP));
157+
158+
final EnginePayloadStatusResult result = fromSuccessResp(resp);
159+
assertThat(result.getStatusAsString()).isEqualTo(INVALID.name());
160+
assertThat(result.getError()).isEqualTo("Invalid block access list encoding");
161+
assertThat(result.getLatestValidHash()).isEmpty();
162+
verify(engineCallListener, times(1)).executionEngineCalled();
163+
}
164+
165+
@Test
166+
public void shouldReturnValidIfBlockAccessListMatchesHeader() {
167+
assertThat(BLOCK_ACCESS_LIST.getAccountChanges()).isNotEmpty();
168+
169+
final BlockHeader header =
170+
setupValidPayload(
171+
new BlockProcessingResult(
172+
Optional.of(
173+
new BlockProcessingOutputs(null, List.of(), Optional.of(VALID_REQUESTS)))),
174+
Optional.empty());
175+
176+
when(blockchain.getBlockHeader(header.getParentHash()))
177+
.thenReturn(Optional.of(mock(BlockHeader.class)));
178+
when(mergeCoordinator.getLatestValidAncestor(header)).thenReturn(Optional.of(header.getHash()));
179+
when(mergeContext.isSyncing()).thenReturn(false);
180+
181+
final JsonRpcResponse resp = resp(mockEnginePayload(header, emptyList()));
182+
183+
assertValidResponse(header, resp);
184+
}
185+
186+
@Override
187+
protected EnginePayloadParameter mockEnginePayload(
188+
final BlockHeader header, final List<String> txs) {
189+
return super.mockEnginePayload(header, txs, null, ENCODED_BLOCK_ACCESS_LIST);
190+
}
191+
192+
@Override
193+
protected EnginePayloadParameter mockEnginePayload(
194+
final BlockHeader header,
195+
final List<String> txs,
196+
final List<WithdrawalParameter> withdrawals) {
197+
return super.mockEnginePayload(header, txs, withdrawals, ENCODED_BLOCK_ACCESS_LIST);
198+
}
199+
200+
@Override
201+
protected BlockHeader createValidBlockHeader(final Optional<List<Withdrawal>> maybeWithdrawals) {
202+
return createActivationBlockHeaderFixture(maybeWithdrawals)
203+
.timestamp(futureEipsHardfork.milestone())
204+
.buildHeader();
205+
}
206+
207+
@Override
208+
protected BlockHeaderTestFixture createActivationBlockHeaderFixture(
209+
final Optional<List<Withdrawal>> maybeWithdrawals) {
210+
final BlockHeader parentBlockHeader =
211+
new BlockHeaderTestFixture()
212+
.baseFeePerGas(Wei.ONE)
213+
.timestamp(futureEipsHardfork.milestone() - 2)
214+
.excessBlobGas(BlobGas.ZERO)
215+
.blobGasUsed(0L)
216+
.buildHeader();
217+
218+
return new BlockHeaderTestFixture()
219+
.baseFeePerGas(Wei.ONE)
220+
.parentHash(parentBlockHeader.getParentHash())
221+
.number(parentBlockHeader.getNumber() + 1)
222+
.timestamp(parentBlockHeader.getTimestamp() + 1)
223+
.withdrawalsRoot(maybeWithdrawals.map(BodyValidation::withdrawalsRoot).orElse(null))
224+
.excessBlobGas(BlobGas.ZERO)
225+
.blobGasUsed(0L)
226+
.parentBeaconBlockRoot(
227+
maybeParentBeaconBlockRoot.isPresent() ? maybeParentBeaconBlockRoot : null)
228+
.balHash(BodyValidation.balHash(BLOCK_ACCESS_LIST))
229+
.requestsHash(BodyValidation.requestsHash(VALID_REQUESTS));
230+
}
231+
232+
@Override
233+
protected BlockHeader createPreActivationBlockHeader() {
234+
return createActivationBlockHeaderFixture(Optional.empty())
235+
.timestamp(futureEipsHardfork.milestone() - 1)
236+
.buildHeader();
237+
}
238+
239+
private static BlockAccessList createSampleBlockAccessList() {
240+
final Address address = Address.fromHexString("0x0000000000000000000000000000000000000001");
241+
final StorageSlotKey slotKey = new StorageSlotKey(UInt256.ONE);
242+
final SlotChanges slotChanges =
243+
new SlotChanges(slotKey, List.of(new StorageChange(0, UInt256.valueOf(2))));
244+
return new BlockAccessList(
245+
List.of(
246+
new AccountChanges(
247+
address,
248+
List.of(slotChanges),
249+
List.of(new SlotRead(slotKey)),
250+
List.of(new BalanceChange(0, Wei.ONE.toBytes())),
251+
List.of(new NonceChange(0, 1L)),
252+
List.of(new CodeChange(0, Bytes.of(1))))));
253+
}
254+
255+
private static String encodeBlockAccessList(final BlockAccessList blockAccessList) {
256+
final BytesValueRLPOutput output = new BytesValueRLPOutput();
257+
blockAccessList.writeTo(output);
258+
return output.encoded().toHexString();
259+
}
260+
}

0 commit comments

Comments
 (0)