mirror of https://github.com/hyperledger/besu
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 commitpull/5073/head558d325bf3
. Signed-off-by: Gabriel Fukushima <gabrielfukushima@gmail.com> * Revert "Fix ByHash json parsing Signed-off-by: Simon Dudley <simon.dudley@consensys.net>" This reverts commit663e11e2
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>
parent
fd90643792
commit
05bf6aba6a
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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…
Reference in new issue