Add getPayloadBodiesByRangeV1 and getPayloadBodiesByHash engine methods (#4980)

* Add engine get payload body methods and test

Signed-off-by: Zhenyang Shi <wcgcyx@gmail.com>
Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Add header

Signed-off-by: Zhenyang Shi <wcgcyx@gmail.com>

* Update result struct & add test

Signed-off-by: Zhenyang Shi <wcgcyx@gmail.com>

* Change constant to use upper case

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Add PayloadBody class and withdrawals to response of methods

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Add unit tests

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Add changelog

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* spotless

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Add check to prevent returning trailing null results past the head

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Add test to check trailing null post head scenario

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Split tests into pre and post shanghai

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* spotless

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Rename methods

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Use getName() to log method name

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* spotless

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Rename variable

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Call constructor directly

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Fix ByHash json parsing
Signed-off-by: Simon Dudley <simon.dudley@consensys.net>

* Fix json parsing again
Signed-off-by: Simon Dudley <simon.dudley@consensys.net>

* Add check to prevent unnecessary queries

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Refactor method

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Add new error code for EngineGetPayloadBodies methods

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Add return error for request above the API limit

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Add constructor for empty response

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* add check for number of blocks requested and for requests of post head

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Add test to check error code when request exceeds API limits

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* add constant for max blocks allowed per request

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* spotless

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Fix some nits

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Add invalid params check

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Add tests for invalid params check

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Fix test and spotless

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Revert "Fix json parsing again"

This reverts commit 558d325bf3.

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Revert "Fix ByHash json parsing Signed-off-by: Simon Dudley <simon.dudley@consensys.net>"

This reverts commit 663e11e2

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Use UnsignedLongParameter to cast params of the request

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Add optional withdrawals to the NewPayload log (#5021)

Signed-off-by: Simon Dudley <simon.dudley@consensys.net>

* kubernetes and errorprone - update versions (#5013)

* update errorprone and kubernetes versions
* fixed errorprone issues in prod cod
* fixed errorprone issues in test code

---------

Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com>

* Rename JsonRpcService to EngineJsonRpcService (#5036)

* rename JsonRpcService to EngineJsonRpcService

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* Params should be single item of array type, not outer array of strings (#5037)

Signed-off-by: Simon Dudley <simon.dudley@consensys.net>

* Add EIP-2537 (BLS12-381 curve precompiles) to Cancun (#5017)

Add the BLS curve precompiles into the registry for cancun.  All of the
curve precompiles have been here since berlin, so this is just wiring
them in.

Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>

* Use UnsignedLongParameter to cast params of the request

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

* Add optional withdrawals to the NewPayload log (#5021)

Signed-off-by: Simon Dudley <simon.dudley@consensys.net>

* kubernetes and errorprone - update versions (#5013)

* update errorprone and kubernetes versions
* fixed errorprone issues in prod cod
* fixed errorprone issues in test code

---------

Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com>

* Rename JsonRpcService to EngineJsonRpcService (#5036)

* rename JsonRpcService to EngineJsonRpcService

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* Params should be single item of array type, not outer array of strings (#5037)

Signed-off-by: Simon Dudley <simon.dudley@consensys.net>

* Add EIP-2537 (BLS12-381 curve precompiles) to Cancun (#5017)

Add the BLS curve precompiles into the registry for cancun.  All of the
curve precompiles have been here since berlin, so this is just wiring
them in.

Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>

* Convert start and count from hex to match JSON-RPC Spec standard

Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>

---------

Signed-off-by: Zhenyang Shi <wcgcyx@gmail.com>
Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com>
Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com>
Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>
Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
Co-authored-by: Zhenyang Shi <wcgcyx@gmail.com>
Co-authored-by: Simon Dudley <simon.dudley@consensys.net>
Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>
Co-authored-by: Daniel Lehrner <daniel.lehrner@consensys.net>
Co-authored-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
pull/5073/head
Gabriel Fukushima 2 years ago committed by GitHub
parent fd90643792
commit 05bf6aba6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      CHANGELOG.md
  2. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java
  3. 85
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadBodiesByHashV1.java
  4. 113
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadBodiesByRangeV1.java
  5. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java
  6. 12
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/BlockResultFactory.java
  7. 79
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/EngineGetPayloadBodiesResultV1.java
  8. 6
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/ExecutionEngineJsonRpcMethods.java
  9. 267
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadBodiesByHashV1Test.java
  10. 352
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadBodiesByRangeV1Test.java

@ -20,6 +20,8 @@ tests are updated to use EC private keys instead of RSA keys.
- Improve get account performance by using the world state updater cache [#4897](https://github.com/hyperledger/besu/pull/4897) - Improve get account performance by using the world state updater cache [#4897](https://github.com/hyperledger/besu/pull/4897)
- Add new KZG precompile and option to override the trusted setup being used [#4822](https://github.com/hyperledger/besu/issues/4822) - Add new KZG precompile and option to override the trusted setup being used [#4822](https://github.com/hyperledger/besu/issues/4822)
- Add implementation for eth_createAccessList RPC method [#4942](https://github.com/hyperledger/besu/pull/4942) - Add implementation for eth_createAccessList RPC method [#4942](https://github.com/hyperledger/besu/pull/4942)
- Add implementation for engine_exchangeCapabilities [#4997](https://github.com/hyperledger/besu/pull/4997)
- Add implementation for engine_getPayloadBodiesByRangeV1 and engine_getPayloadBodiesByHashV1 [#4980](https://github.com/hyperledger/besu/pull/4980)
- Updated reference tests to v11.3 [#4996](https://github.com/hyperledger/besu/pull/4996) - Updated reference tests to v11.3 [#4996](https://github.com/hyperledger/besu/pull/4996)
- Add DebugGetRawBlock and DebugGetRawHeader RPC methods [#5011](https://github.com/hyperledger/besu/pull/5011) - Add DebugGetRawBlock and DebugGetRawHeader RPC methods [#5011](https://github.com/hyperledger/besu/pull/5011)
- Besu requires minimum Java 17 and up to build and run [#3320](https://github.com/hyperledger/besu/issues/3320) - Besu requires minimum Java 17 and up to build and run [#3320](https://github.com/hyperledger/besu/issues/3320)

@ -56,6 +56,8 @@ public enum RpcMethod {
ENGINE_FORKCHOICE_UPDATED_V1("engine_forkchoiceUpdatedV1"), ENGINE_FORKCHOICE_UPDATED_V1("engine_forkchoiceUpdatedV1"),
ENGINE_FORKCHOICE_UPDATED_V2("engine_forkchoiceUpdatedV2"), ENGINE_FORKCHOICE_UPDATED_V2("engine_forkchoiceUpdatedV2"),
ENGINE_EXCHANGE_TRANSITION_CONFIGURATION("engine_exchangeTransitionConfigurationV1"), ENGINE_EXCHANGE_TRANSITION_CONFIGURATION("engine_exchangeTransitionConfigurationV1"),
ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1("engine_getPayloadBodiesByHashV1"),
ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1("engine_getPayloadBodiesByRangeV1"),
ENGINE_EXCHANGE_CAPABILITIES("engine_exchangeCapabilities"), ENGINE_EXCHANGE_CAPABILITIES("engine_exchangeCapabilities"),
GOQUORUM_ETH_GET_QUORUM_PAYLOAD("eth_getQuorumPayload"), GOQUORUM_ETH_GET_QUORUM_PAYLOAD("eth_getQuorumPayload"),

@ -0,0 +1,85 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
import static org.hyperledger.besu.util.Slf4jLambdaHelper.traceLambda;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.EngineGetPayloadBodiesResultV1;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import java.util.Arrays;
import java.util.stream.Collectors;
import io.vertx.core.Vertx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EngineGetPayloadBodiesByHashV1 extends ExecutionEngineJsonRpcMethod {
private static final int MAX_REQUEST_BLOCKS = 1024;
private static final Logger LOG = LoggerFactory.getLogger(EngineGetPayloadBodiesByHashV1.class);
private final BlockResultFactory blockResultFactory;
public EngineGetPayloadBodiesByHashV1(
final Vertx vertx,
final ProtocolContext protocolContext,
final BlockResultFactory blockResultFactory,
final EngineCallListener engineCallListener) {
super(vertx, protocolContext, engineCallListener);
this.blockResultFactory = blockResultFactory;
}
@Override
public String getName() {
return RpcMethod.ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1.getMethodName();
}
@Override
public JsonRpcResponse syncResponse(final JsonRpcRequestContext request) {
engineCallListener.executionEngineCalled();
final Object reqId = request.getRequest().getId();
final Hash[] blockHashes = request.getRequiredParameter(0, Hash[].class);
traceLambda(LOG, "{} parameters: blockHashes {}", () -> getName(), () -> blockHashes);
if (blockHashes.length > getMaxRequestBlocks()) {
return new JsonRpcErrorResponse(reqId, JsonRpcError.INVALID_RANGE_REQUEST_TOO_LARGE);
}
final Blockchain blockchain = protocolContext.getBlockchain();
final EngineGetPayloadBodiesResultV1 engineGetPayloadBodiesResultV1 =
blockResultFactory.payloadBodiesCompleteV1(
Arrays.stream(blockHashes).map(blockchain::getBlockBody).collect(Collectors.toList()));
return new JsonRpcSuccessResponse(reqId, engineGetPayloadBodiesResultV1);
}
protected int getMaxRequestBlocks() {
return MAX_REQUEST_BLOCKS;
}
}

@ -0,0 +1,113 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
import static org.hyperledger.besu.util.Slf4jLambdaHelper.traceLambda;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.UnsignedLongParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.EngineGetPayloadBodiesResultV1;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import java.util.stream.Collectors;
import java.util.stream.LongStream;
import io.vertx.core.Vertx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EngineGetPayloadBodiesByRangeV1 extends ExecutionEngineJsonRpcMethod {
private static final Logger LOG = LoggerFactory.getLogger(EngineGetPayloadBodiesByRangeV1.class);
private static final int MAX_REQUEST_BLOCKS = 1024;
private final BlockResultFactory blockResultFactory;
public EngineGetPayloadBodiesByRangeV1(
final Vertx vertx,
final ProtocolContext protocolContext,
final BlockResultFactory blockResultFactory,
final EngineCallListener engineCallListener) {
super(vertx, protocolContext, engineCallListener);
this.blockResultFactory = blockResultFactory;
}
@Override
public String getName() {
return RpcMethod.ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1.getMethodName();
}
@Override
public JsonRpcResponse syncResponse(final JsonRpcRequestContext request) {
engineCallListener.executionEngineCalled();
final long startBlockNumber =
request.getRequiredParameter(0, UnsignedLongParameter.class).getValue();
final long count = request.getRequiredParameter(1, UnsignedLongParameter.class).getValue();
final Object reqId = request.getRequest().getId();
traceLambda(
LOG,
"{} parameters: start block number {} count {}",
() -> getName(),
() -> startBlockNumber,
() -> count);
if (startBlockNumber < 1 || count < 1) {
return new JsonRpcErrorResponse(reqId, JsonRpcError.INVALID_PARAMS);
}
if (count > getMaxRequestBlocks()) {
return new JsonRpcErrorResponse(reqId, JsonRpcError.INVALID_RANGE_REQUEST_TOO_LARGE);
}
final Blockchain blockchain = protocolContext.getBlockchain();
final long chainHeadBlockNumber = blockchain.getChainHeadBlockNumber();
// request startBlockNumber is beyond head of chain
if (chainHeadBlockNumber < startBlockNumber) {
// Empty List of payloadBodies
return new JsonRpcSuccessResponse(reqId, new EngineGetPayloadBodiesResultV1());
}
final long upperBound = startBlockNumber + count;
// if we've received request from blocks beyond the head we exclude those from the query
final long endExclusiveBlockNumber =
chainHeadBlockNumber < upperBound ? chainHeadBlockNumber + 1 : upperBound;
EngineGetPayloadBodiesResultV1 engineGetPayloadBodiesResultV1 =
blockResultFactory.payloadBodiesCompleteV1(
LongStream.range(startBlockNumber, endExclusiveBlockNumber)
.mapToObj(
blockNumber ->
blockchain
.getBlockHashByNumber(blockNumber)
.flatMap(blockchain::getBlockBody))
.collect(Collectors.toList()));
return new JsonRpcSuccessResponse(reqId, engineGetPayloadBodiesResultV1);
}
protected int getMaxRequestBlocks() {
return MAX_REQUEST_BLOCKS;
}
}

@ -83,7 +83,7 @@ public enum JsonRpcError {
INVALID_TERMINAL_BLOCK(-32002, "Terminal block doesn't satisfy terminal block conditions"), INVALID_TERMINAL_BLOCK(-32002, "Terminal block doesn't satisfy terminal block conditions"),
INVALID_FORKCHOICE_STATE(-38002, "Invalid forkchoice state"), INVALID_FORKCHOICE_STATE(-38002, "Invalid forkchoice state"),
INVALID_PAYLOAD_ATTRIBUTES(-38003, "Invalid payload attributes"), INVALID_PAYLOAD_ATTRIBUTES(-38003, "Invalid payload attributes"),
INVALID_RANGE_REQUEST_TOO_LARGE(-38004, "Too large request"),
// Miner failures // Miner failures
COINBASE_NOT_SET(-32010, "Coinbase not set. Unable to start mining without a coinbase"), COINBASE_NOT_SET(-32010, "Coinbase not set. Unable to start mining without a coinbase"),
NO_HASHES_PER_SECOND(-32011, "No hashes being generated by the current node"), NO_HASHES_PER_SECOND(-32011, "No hashes being generated by the current node"),

@ -16,9 +16,11 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results;
import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.EngineGetPayloadBodiesResultV1.PayloadBody;
import org.hyperledger.besu.ethereum.api.query.BlockWithMetadata; import org.hyperledger.besu.ethereum.api.query.BlockWithMetadata;
import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata; import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata;
import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockValueCalculator; import org.hyperledger.besu.ethereum.core.BlockValueCalculator;
import org.hyperledger.besu.ethereum.core.BlockWithReceipts; import org.hyperledger.besu.ethereum.core.BlockWithReceipts;
@ -26,6 +28,7 @@ import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
@ -115,6 +118,15 @@ public class BlockResultFactory {
Quantity.create(blockValue)); Quantity.create(blockValue));
} }
public EngineGetPayloadBodiesResultV1 payloadBodiesCompleteV1(
final List<Optional<BlockBody>> blockBodies) {
final List<PayloadBody> payloadBodies =
blockBodies.stream()
.map(maybeBody -> maybeBody.map(PayloadBody::new).orElse(null))
.collect(Collectors.toList());
return new EngineGetPayloadBodiesResultV1(payloadBodies);
}
public BlockResult transactionHash(final BlockWithMetadata<Hash, Hash> blockWithMetadata) { public BlockResult transactionHash(final BlockWithMetadata<Hash, Hash> blockWithMetadata) {
return transactionHash(blockWithMetadata, false); return transactionHash(blockWithMetadata, false);
} }

@ -0,0 +1,79 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.WithdrawalParameter;
import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonValue;
import org.apache.tuweni.bytes.Bytes;
@JsonPropertyOrder({"payloadBodies"})
public class EngineGetPayloadBodiesResultV1 {
private final List<PayloadBody> payloadBodies;
public EngineGetPayloadBodiesResultV1() {
this.payloadBodies = Collections.<PayloadBody>emptyList();
}
public EngineGetPayloadBodiesResultV1(final List<PayloadBody> payloadBody) {
this.payloadBodies = payloadBody;
}
@JsonValue
public List<PayloadBody> getPayloadBodies() {
return payloadBodies;
}
public static class PayloadBody {
private final List<String> transactions;
private final List<WithdrawalParameter> withdrawals;
public PayloadBody(final BlockBody blockBody) {
this.transactions =
blockBody.getTransactions().stream()
.map(TransactionEncoder::encodeOpaqueBytes)
.map(Bytes::toHexString)
.collect(Collectors.toList());
this.withdrawals =
blockBody
.getWithdrawals()
.map(
ws ->
ws.stream()
.map(WithdrawalParameter::fromWithdrawal)
.collect(Collectors.toList()))
.orElse(null);
}
@JsonGetter(value = "transactions")
public List<String> getTransactions() {
return transactions;
}
@JsonGetter(value = "withdrawals")
public List<WithdrawalParameter> getWithdrawals() {
return withdrawals;
}
}
}

@ -22,6 +22,8 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineE
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineExchangeTransitionConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineExchangeTransitionConfiguration;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineForkchoiceUpdatedV1; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineForkchoiceUpdatedV1;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineForkchoiceUpdatedV2; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineForkchoiceUpdatedV2;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetPayloadBodiesByHashV1;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetPayloadBodiesByRangeV1;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetPayloadV1; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetPayloadV1;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetPayloadV2; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetPayloadV2;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineNewPayloadV1; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineNewPayloadV1;
@ -114,6 +116,10 @@ public class ExecutionEngineJsonRpcMethods extends ApiGroupJsonRpcMethods {
engineQosTimer), engineQosTimer),
new EngineExchangeTransitionConfiguration( new EngineExchangeTransitionConfiguration(
consensusEngineServer, protocolContext, engineQosTimer), consensusEngineServer, protocolContext, engineQosTimer),
new EngineGetPayloadBodiesByHashV1(
consensusEngineServer, protocolContext, blockResultFactory, engineQosTimer),
new EngineGetPayloadBodiesByRangeV1(
consensusEngineServer, protocolContext, blockResultFactory, engineQosTimer),
new EngineExchangeCapabilities(consensusEngineServer, protocolContext, engineQosTimer)); new EngineExchangeCapabilities(consensusEngineServer, protocolContext, engineQosTimer));
} else { } else {
return mapOf( return mapOf(

@ -0,0 +1,267 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.INVALID_RANGE_REQUEST_TOO_LARGE;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.GWei;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponseType;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.EngineGetPayloadBodiesResultV1;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import io.vertx.core.Vertx;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt64;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class EngineGetPayloadBodiesByHashV1Test {
private EngineGetPayloadBodiesByHashV1 method;
private static final Vertx vertx = Vertx.vertx();
private static final BlockResultFactory blockResultFactory = new BlockResultFactory();
@Mock private ProtocolContext protocolContext;
@Mock private EngineCallListener engineCallListener;
@Mock private MutableBlockchain blockchain;
@Before
public void before() {
when(protocolContext.getBlockchain()).thenReturn(blockchain);
this.method =
spy(
new EngineGetPayloadBodiesByHashV1(
vertx, protocolContext, blockResultFactory, engineCallListener));
}
@Test
public void shouldReturnExpectedMethodName() {
assertThat(method.getName()).isEqualTo("engine_getPayloadBodiesByHashV1");
}
@Test
public void shouldReturnEmptyPayloadBodiesWithEmptyHash() {
final var resp = resp(new Hash[] {});
final EngineGetPayloadBodiesResultV1 result = fromSuccessResp(resp);
assertThat(result.getPayloadBodies().isEmpty()).isTrue();
}
@Test
public void shouldReturnPayloadForKnownHashes() {
final SignatureAlgorithm sig = SignatureAlgorithmFactory.getInstance();
final Hash blockHash1 = Hash.wrap(Bytes32.random());
final Hash blockHash2 = Hash.wrap(Bytes32.random());
final Hash blockHash3 = Hash.wrap(Bytes32.random());
final BlockBody blockBody1 =
new BlockBody(
List.of(new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList());
final BlockBody blockBody2 =
new BlockBody(
List.of(
new TransactionTestFixture().createTransaction(sig.generateKeyPair()),
new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList());
final BlockBody blockBody3 =
new BlockBody(
List.of(
new TransactionTestFixture().createTransaction(sig.generateKeyPair()),
new TransactionTestFixture().createTransaction(sig.generateKeyPair()),
new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList());
when(blockchain.getBlockBody(blockHash1)).thenReturn(Optional.of(blockBody1));
when(blockchain.getBlockBody(blockHash2)).thenReturn(Optional.of(blockBody2));
when(blockchain.getBlockBody(blockHash3)).thenReturn(Optional.of(blockBody3));
final var resp = resp(new Hash[] {blockHash1, blockHash2, blockHash3});
final var result = fromSuccessResp(resp);
assertThat(result.getPayloadBodies().size()).isEqualTo(3);
assertThat(result.getPayloadBodies().get(0).getTransactions().size()).isEqualTo(1);
assertThat(result.getPayloadBodies().get(1).getTransactions().size()).isEqualTo(2);
assertThat(result.getPayloadBodies().get(2).getTransactions().size()).isEqualTo(3);
}
@Test
public void shouldReturnNullForUnknownHashes() {
final Hash blockHash1 = Hash.wrap(Bytes32.random());
final Hash blockHash2 = Hash.wrap(Bytes32.random());
final Hash blockHash3 = Hash.wrap(Bytes32.random());
final var resp = resp(new Hash[] {blockHash1, blockHash2, blockHash3});
final var result = fromSuccessResp(resp);
assertThat(result.getPayloadBodies().size()).isEqualTo(3);
assertThat(result.getPayloadBodies().get(0)).isNull();
assertThat(result.getPayloadBodies().get(1)).isNull();
assertThat(result.getPayloadBodies().get(2)).isNull();
}
@Test
public void shouldReturnNullForUnknownHashAndPayloadForKnownHash() {
final SignatureAlgorithm sig = SignatureAlgorithmFactory.getInstance();
final Hash blockHash1 = Hash.wrap(Bytes32.random());
final Hash blockHash2 = Hash.wrap(Bytes32.random());
final Hash blockHash3 = Hash.wrap(Bytes32.random());
final BlockBody blockBody1 =
new BlockBody(
List.of(new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList());
final BlockBody blockBody3 =
new BlockBody(
List.of(
new TransactionTestFixture().createTransaction(sig.generateKeyPair()),
new TransactionTestFixture().createTransaction(sig.generateKeyPair()),
new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList());
when(blockchain.getBlockBody(blockHash1)).thenReturn(Optional.of(blockBody1));
when(blockchain.getBlockBody(blockHash3)).thenReturn(Optional.of(blockBody3));
final var resp = resp(new Hash[] {blockHash1, blockHash2, blockHash3});
final var result = fromSuccessResp(resp);
assertThat(result.getPayloadBodies().size()).isEqualTo(3);
assertThat(result.getPayloadBodies().get(0).getTransactions().size()).isEqualTo(1);
assertThat(result.getPayloadBodies().get(1)).isNull();
assertThat(result.getPayloadBodies().get(2).getTransactions().size()).isEqualTo(3);
}
@Test
public void shouldReturnWithdrawalNullWhenBlockIsPreShanghai() {
final SignatureAlgorithm sig = SignatureAlgorithmFactory.getInstance();
final Hash blockHash1 = Hash.wrap(Bytes32.random());
final Hash blockHash2 = Hash.wrap(Bytes32.random());
final BlockBody preShanghaiBlockBody =
new BlockBody(
List.of(
new TransactionTestFixture().createTransaction(sig.generateKeyPair()),
new TransactionTestFixture().createTransaction(sig.generateKeyPair()),
new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList());
final BlockBody preShanghaiBlockBody2 =
new BlockBody(
List.of(new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList(),
Optional.empty());
when(blockchain.getBlockBody(blockHash1)).thenReturn(Optional.of(preShanghaiBlockBody));
when(blockchain.getBlockBody(blockHash2)).thenReturn(Optional.of(preShanghaiBlockBody2));
final var resp = resp(new Hash[] {blockHash1, blockHash2});
final var result = fromSuccessResp(resp);
assertThat(result.getPayloadBodies().size()).isEqualTo(2);
assertThat(result.getPayloadBodies().get(0).getTransactions().size()).isEqualTo(3);
assertThat(result.getPayloadBodies().get(0).getWithdrawals()).isNull();
assertThat(result.getPayloadBodies().get(1).getTransactions().size()).isEqualTo(1);
assertThat(result.getPayloadBodies().get(1).getWithdrawals()).isNull();
}
@Test
public void shouldReturnWithdrawalsWhenBlockIsPostShanghai() {
final SignatureAlgorithm sig = SignatureAlgorithmFactory.getInstance();
final Hash blockHash1 = Hash.wrap(Bytes32.random());
final Hash blockHash2 = Hash.wrap(Bytes32.random());
final Withdrawal withdrawal =
new Withdrawal(UInt64.ONE, UInt64.ONE, Address.fromHexString("0x1"), GWei.ONE);
final Withdrawal withdrawal2 =
new Withdrawal(UInt64.ONE, UInt64.ONE, Address.fromHexString("0x2"), GWei.ONE);
final BlockBody shanghaiBlockBody =
new BlockBody(
List.of(
new TransactionTestFixture().createTransaction(sig.generateKeyPair()),
new TransactionTestFixture().createTransaction(sig.generateKeyPair()),
new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList(),
Optional.of(List.of(withdrawal)));
final BlockBody shanghaiBlockBody2 =
new BlockBody(
List.of(new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList(),
Optional.of(List.of(withdrawal2)));
when(blockchain.getBlockBody(blockHash1)).thenReturn(Optional.of(shanghaiBlockBody));
when(blockchain.getBlockBody(blockHash2)).thenReturn(Optional.of(shanghaiBlockBody2));
final var resp = resp(new Hash[] {blockHash1, blockHash2});
final var result = fromSuccessResp(resp);
assertThat(result.getPayloadBodies().size()).isEqualTo(2);
assertThat(result.getPayloadBodies().get(0).getTransactions().size()).isEqualTo(3);
assertThat(result.getPayloadBodies().get(0).getWithdrawals().size()).isEqualTo(1);
assertThat(result.getPayloadBodies().get(1).getTransactions().size()).isEqualTo(1);
assertThat(result.getPayloadBodies().get(1).getWithdrawals().size()).isEqualTo(1);
}
@Test
public void shouldReturnErrorWhenRequestExceedsPermittedNumberOfBlocks() {
final Hash blockHash1 = Hash.wrap(Bytes32.random());
final Hash blockHash2 = Hash.wrap(Bytes32.random());
final Hash[] hashes = new Hash[] {blockHash1, blockHash2};
doReturn(1).when(method).getMaxRequestBlocks();
final JsonRpcResponse resp = resp(hashes);
final var result = fromErrorResp(resp);
assertThat(result.getCode()).isEqualTo(INVALID_RANGE_REQUEST_TOO_LARGE.getCode());
}
private JsonRpcResponse resp(final Hash[] hashes) {
return method.response(
new JsonRpcRequestContext(
new JsonRpcRequest(
"2.0",
RpcMethod.ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1.getMethodName(),
new Object[] {hashes})));
}
private EngineGetPayloadBodiesResultV1 fromSuccessResp(final JsonRpcResponse resp) {
assertThat(resp.getType()).isEqualTo(JsonRpcResponseType.SUCCESS);
return Optional.of(resp)
.map(JsonRpcSuccessResponse.class::cast)
.map(JsonRpcSuccessResponse::getResult)
.map(EngineGetPayloadBodiesResultV1.class::cast)
.get();
}
private JsonRpcError fromErrorResp(final JsonRpcResponse resp) {
assertThat(resp.getType()).isEqualTo(JsonRpcResponseType.ERROR);
return Optional.of(resp)
.map(JsonRpcErrorResponse.class::cast)
.map(JsonRpcErrorResponse::getError)
.get();
}
}

@ -0,0 +1,352 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.INVALID_PARAMS;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.INVALID_RANGE_REQUEST_TOO_LARGE;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.GWei;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponseType;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.EngineGetPayloadBodiesResultV1;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import io.vertx.core.Vertx;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt64;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class EngineGetPayloadBodiesByRangeV1Test {
private EngineGetPayloadBodiesByRangeV1 method;
private static final Vertx vertx = Vertx.vertx();
private static final BlockResultFactory blockResultFactory = new BlockResultFactory();
@Mock private ProtocolContext protocolContext;
@Mock private EngineCallListener engineCallListener;
@Mock private MutableBlockchain blockchain;
@Before
public void before() {
when(protocolContext.getBlockchain()).thenReturn(blockchain);
this.method =
spy(
new EngineGetPayloadBodiesByRangeV1(
vertx, protocolContext, blockResultFactory, engineCallListener));
}
@Test
public void shouldReturnExpectedMethodName() {
assertThat(method.getName()).isEqualTo("engine_getPayloadBodiesByRangeV1");
}
@Test
public void shouldReturnPayloadForKnownNumber() {
final SignatureAlgorithm sig = SignatureAlgorithmFactory.getInstance();
final Hash blockHash1 = Hash.wrap(Bytes32.random());
final Hash blockHash2 = Hash.wrap(Bytes32.random());
final Hash blockHash3 = Hash.wrap(Bytes32.random());
final BlockBody blockBody1 =
new BlockBody(
List.of(new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList());
final BlockBody blockBody2 =
new BlockBody(
List.of(
new TransactionTestFixture().createTransaction(sig.generateKeyPair()),
new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList());
final BlockBody blockBody3 =
new BlockBody(
List.of(
new TransactionTestFixture().createTransaction(sig.generateKeyPair()),
new TransactionTestFixture().createTransaction(sig.generateKeyPair()),
new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList());
when(blockchain.getChainHeadBlockNumber()).thenReturn(Long.valueOf(130));
when(blockchain.getBlockBody(blockHash1)).thenReturn(Optional.of(blockBody1));
when(blockchain.getBlockBody(blockHash2)).thenReturn(Optional.of(blockBody2));
when(blockchain.getBlockBody(blockHash3)).thenReturn(Optional.of(blockBody3));
when(blockchain.getBlockHashByNumber(123)).thenReturn(Optional.of(blockHash1));
when(blockchain.getBlockHashByNumber(124)).thenReturn(Optional.of(blockHash2));
when(blockchain.getBlockHashByNumber(125)).thenReturn(Optional.of(blockHash3));
final var resp = resp("0x7b", "0x3");
final EngineGetPayloadBodiesResultV1 result = fromSuccessResp(resp);
assertThat(result.getPayloadBodies().size()).isEqualTo(3);
assertThat(result.getPayloadBodies().get(0).getTransactions().size()).isEqualTo(1);
assertThat(result.getPayloadBodies().get(1).getTransactions().size()).isEqualTo(2);
assertThat(result.getPayloadBodies().get(2).getTransactions().size()).isEqualTo(3);
}
@Test
public void shouldReturnNullForUnknownNumber() {
when(blockchain.getChainHeadBlockNumber()).thenReturn(Long.valueOf(130));
final var resp = resp("0x7b", "0x3");
final EngineGetPayloadBodiesResultV1 result = fromSuccessResp(resp);
assertThat(result.getPayloadBodies().size()).isEqualTo(3);
assertThat(result.getPayloadBodies().get(0)).isNull();
assertThat(result.getPayloadBodies().get(1)).isNull();
assertThat(result.getPayloadBodies().get(2)).isNull();
}
@Test
public void shouldReturnNullForUnknownNumberAndPayloadForKnownNumber() {
final SignatureAlgorithm sig = SignatureAlgorithmFactory.getInstance();
final Hash blockHash1 = Hash.wrap(Bytes32.random());
final Hash blockHash3 = Hash.wrap(Bytes32.random());
final BlockBody blockBody1 =
new BlockBody(
List.of(new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList());
final BlockBody blockBody3 =
new BlockBody(
List.of(
new TransactionTestFixture().createTransaction(sig.generateKeyPair()),
new TransactionTestFixture().createTransaction(sig.generateKeyPair()),
new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList());
when(blockchain.getChainHeadBlockNumber()).thenReturn(Long.valueOf(130));
when(blockchain.getBlockBody(blockHash1)).thenReturn(Optional.of(blockBody1));
when(blockchain.getBlockBody(blockHash3)).thenReturn(Optional.of(blockBody3));
when(blockchain.getBlockHashByNumber(123)).thenReturn(Optional.of(blockHash1));
when(blockchain.getBlockHashByNumber(125)).thenReturn(Optional.of(blockHash3));
final var resp = resp("0x7b", "0x3");
final var result = fromSuccessResp(resp);
assertThat(result.getPayloadBodies().size()).isEqualTo(3);
assertThat(result.getPayloadBodies().get(0).getTransactions().size()).isEqualTo(1);
assertThat(result.getPayloadBodies().get(1)).isNull();
assertThat(result.getPayloadBodies().get(2).getTransactions().size()).isEqualTo(3);
}
@Test
public void shouldReturnNullForWithdrawalsWhenBlockIsPreShanghai() {
final SignatureAlgorithm sig = SignatureAlgorithmFactory.getInstance();
final Hash blockHash1 = Hash.wrap(Bytes32.random());
final Hash blockHash2 = Hash.wrap(Bytes32.random());
final BlockBody preShanghaiBlockBody =
new BlockBody(
List.of(
new TransactionTestFixture().createTransaction(sig.generateKeyPair()),
new TransactionTestFixture().createTransaction(sig.generateKeyPair()),
new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList());
final BlockBody preShanghaiBlockBody2 =
new BlockBody(
List.of(new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList(),
Optional.empty());
when(blockchain.getChainHeadBlockNumber()).thenReturn(Long.valueOf(130));
when(blockchain.getBlockBody(blockHash1)).thenReturn(Optional.of(preShanghaiBlockBody));
when(blockchain.getBlockBody(blockHash2)).thenReturn(Optional.of(preShanghaiBlockBody2));
when(blockchain.getBlockHashByNumber(123)).thenReturn(Optional.of(blockHash1));
when(blockchain.getBlockHashByNumber(124)).thenReturn(Optional.of(blockHash2));
final var resp = resp("0x7b", "0x2");
final var result = fromSuccessResp(resp);
assertThat(result.getPayloadBodies().size()).isEqualTo(2);
assertThat(result.getPayloadBodies().get(0).getTransactions().size()).isEqualTo(3);
assertThat(result.getPayloadBodies().get(0).getWithdrawals()).isNull();
assertThat(result.getPayloadBodies().get(1).getTransactions().size()).isEqualTo(1);
assertThat(result.getPayloadBodies().get(1).getWithdrawals()).isNull();
;
}
@Test
public void shouldReturnWithdrawalsWhenBlockIsPostShanghai() {
final SignatureAlgorithm sig = SignatureAlgorithmFactory.getInstance();
final Hash blockHash1 = Hash.wrap(Bytes32.random());
final Hash blockHash2 = Hash.wrap(Bytes32.random());
final Withdrawal withdrawal =
new Withdrawal(UInt64.ONE, UInt64.ONE, Address.fromHexString("0x1"), GWei.ONE);
final Withdrawal withdrawal2 =
new Withdrawal(UInt64.ONE, UInt64.ONE, Address.fromHexString("0x2"), GWei.ONE);
final BlockBody shanghaiBlockBody =
new BlockBody(
List.of(
new TransactionTestFixture().createTransaction(sig.generateKeyPair()),
new TransactionTestFixture().createTransaction(sig.generateKeyPair()),
new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList(),
Optional.of(List.of(withdrawal)));
final BlockBody shanghaiBlockBody2 =
new BlockBody(
List.of(new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList(),
Optional.of(List.of(withdrawal2)));
when(blockchain.getChainHeadBlockNumber()).thenReturn(Long.valueOf(130));
when(blockchain.getBlockBody(blockHash1)).thenReturn(Optional.of(shanghaiBlockBody));
when(blockchain.getBlockBody(blockHash2)).thenReturn(Optional.of(shanghaiBlockBody2));
when(blockchain.getBlockHashByNumber(123)).thenReturn(Optional.of(blockHash1));
when(blockchain.getBlockHashByNumber(124)).thenReturn(Optional.of(blockHash2));
final var resp = resp("0x7b", "0x2");
final var result = fromSuccessResp(resp);
assertThat(result.getPayloadBodies().size()).isEqualTo(2);
assertThat(result.getPayloadBodies().get(0).getTransactions().size()).isEqualTo(3);
assertThat(result.getPayloadBodies().get(0).getWithdrawals().size()).isEqualTo(1);
assertThat(result.getPayloadBodies().get(1).getTransactions().size()).isEqualTo(1);
assertThat(result.getPayloadBodies().get(1).getWithdrawals().size()).isEqualTo(1);
}
@Test
public void shouldNotContainTrailingNullForBlocksPastTheCurrentHead() {
final SignatureAlgorithm sig = SignatureAlgorithmFactory.getInstance();
final Hash blockHash1 = Hash.wrap(Bytes32.random());
final Withdrawal withdrawal =
new Withdrawal(UInt64.ONE, UInt64.ONE, Address.fromHexString("0x1"), GWei.ONE);
final BlockBody shanghaiBlockBody =
new BlockBody(
List.of(
new TransactionTestFixture().createTransaction(sig.generateKeyPair()),
new TransactionTestFixture().createTransaction(sig.generateKeyPair()),
new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList(),
Optional.of(List.of(withdrawal)));
when(blockchain.getChainHeadBlockNumber()).thenReturn(Long.valueOf(123));
when(blockchain.getBlockBody(blockHash1)).thenReturn(Optional.of(shanghaiBlockBody));
when(blockchain.getBlockHashByNumber(123)).thenReturn(Optional.of(blockHash1));
final var resp = resp("0x7b", "0x3");
final var result = fromSuccessResp(resp);
assertThat(result.getPayloadBodies().size()).isEqualTo(1);
}
@Test
public void shouldReturnUpUntilHeadWhenStartBlockPlusCountEqualsHeadNumber() {
final SignatureAlgorithm sig = SignatureAlgorithmFactory.getInstance();
final Hash blockHash1 = Hash.wrap(Bytes32.random());
final Hash blockHash2 = Hash.wrap(Bytes32.random());
final Hash blockHash3 = Hash.wrap(Bytes32.random());
final Withdrawal withdrawal =
new Withdrawal(UInt64.ONE, UInt64.ONE, Address.fromHexString("0x1"), GWei.ONE);
final BlockBody shanghaiBlockBody =
new BlockBody(
List.of(new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList(),
Optional.of(List.of(withdrawal)));
final BlockBody shanghaiBlockBody2 =
new BlockBody(
List.of(new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList(),
Optional.of(List.of(withdrawal)));
final BlockBody shanghaiBlockBody3 =
new BlockBody(
List.of(new TransactionTestFixture().createTransaction(sig.generateKeyPair())),
Collections.emptyList(),
Optional.of(List.of(withdrawal)));
when(blockchain.getChainHeadBlockNumber()).thenReturn(Long.valueOf(125));
when(blockchain.getBlockBody(blockHash1)).thenReturn(Optional.of(shanghaiBlockBody));
when(blockchain.getBlockBody(blockHash2)).thenReturn(Optional.of(shanghaiBlockBody2));
when(blockchain.getBlockBody(blockHash3)).thenReturn(Optional.of(shanghaiBlockBody3));
when(blockchain.getBlockHashByNumber(123)).thenReturn(Optional.of(blockHash1));
when(blockchain.getBlockHashByNumber(124)).thenReturn(Optional.of(blockHash2));
when(blockchain.getBlockHashByNumber(125)).thenReturn(Optional.of(blockHash3));
final var resp = resp("0x7b", "0x3");
final var result = fromSuccessResp(resp);
assertThat(result.getPayloadBodies().size()).isEqualTo(3);
}
@Test
public void ShouldReturnEmptyPayloadForRequestsPastCurrentHead() {
when(blockchain.getChainHeadBlockNumber()).thenReturn(Long.valueOf(123));
final JsonRpcResponse resp = resp("0x7d", "0x3");
final var result = fromSuccessResp(resp);
assertThat(result.getPayloadBodies()).isEqualTo(Collections.EMPTY_LIST);
}
@Test
public void shouldReturnErrorWhenRequestExceedsPermittedNumberOfBlocks() {
doReturn(3).when(method).getMaxRequestBlocks();
final JsonRpcResponse resp = resp("0x539", "0x4");
final var result = fromErrorResp(resp);
assertThat(result.getCode()).isEqualTo(INVALID_RANGE_REQUEST_TOO_LARGE.getCode());
}
@Test
public void shouldReturnInvalidParamsIfStartIsZero() {
final JsonRpcResponse resp = resp("0x0", "0x539");
final var result = fromErrorResp(resp);
assertThat(result.getCode()).isEqualTo(INVALID_PARAMS.getCode());
}
@Test
public void shouldReturnInvalidParamsIfCountIsZero() {
final JsonRpcResponse resp = resp("0x539", "0x0");
final var result = fromErrorResp(resp);
assertThat(result.getCode()).isEqualTo(INVALID_PARAMS.getCode());
}
private JsonRpcResponse resp(final String startBlockNumber, final String range) {
return method.response(
new JsonRpcRequestContext(
new JsonRpcRequest(
"2.0",
RpcMethod.ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1.getMethodName(),
new Object[] {startBlockNumber, range})));
}
private EngineGetPayloadBodiesResultV1 fromSuccessResp(final JsonRpcResponse resp) {
assertThat(resp.getType()).isEqualTo(JsonRpcResponseType.SUCCESS);
return Optional.of(resp)
.map(JsonRpcSuccessResponse.class::cast)
.map(JsonRpcSuccessResponse::getResult)
.map(EngineGetPayloadBodiesResultV1.class::cast)
.get();
}
private JsonRpcError fromErrorResp(final JsonRpcResponse resp) {
assertThat(resp.getType()).isEqualTo(JsonRpcResponseType.ERROR);
return Optional.of(resp)
.map(JsonRpcErrorResponse.class::cast)
.map(JsonRpcErrorResponse::getError)
.get();
}
}
Loading…
Cancel
Save