mirror of https://github.com/hyperledger/besu
Add new JSON_RPC endpoints to get miner data by block number and block hash. (#1538)
* #1066 Switched to use unprefixed hex strings for memory and stack values Signed-off-by: David Mechler <david.mechler@consensys.net> * Disable flaky tests per Ben Burns(Yeti) request Signed-off-by: David Mechler <david.mechler@consensys.net> * Revert last commit and enable ignored tests. Signed-off-by: David Mechler <david.mechler@consensys.net> * #1157 - updated to create 2 agents so that proper bonding can occur Signed-off-by: David Mechler <david.mechler@consensys.net> * #1162 - Updated test to mock the local peer PING packet creation so that the hash can be managed. Signed-off-by: David Mechler <david.mechler@consensys.net> * Added admin_logsRepairCache end point Signed-off-by: David Mechler <david.mechler@consensys.net> * Added admin_logsRepairCache end point Signed-off-by: David Mechler <david.mechler@consensys.net> * Remove p2p network code per PR comments Signed-off-by: David Mechler <david.mechler@consensys.net> * Updates from PR comments Signed-off-by: David Mechler <david.mechler@consensys.net> * Spotless Apply fixes Signed-off-by: David Mechler <david.mechler@consensys.net> * PR updates Signed-off-by: David Mechler <david.mechler@consensys.net> * Admin force cache refresh when called through end point per PR comments Signed-off-by: David Mechler <david.mechler@consensys.net> * Pr updates Signed-off-by: David Mechler <david.mechler@consensys.net> * Update changelog for 1.5.1 Signed-off-by: David Mechler <david.mechler@consensys.net> * Remove check for 0x prefix on addresses to match expectations Signed-off-by: David Mechler <david.mechler@consensys.net> * Update graphql pending to allow for sorting of transactions Signed-off-by: David Mechler <david.mechler@consensys.net> * #1408 Add Miner data endpoints Signed-off-by: David Mechler <david.mechler@consensys.net> * #1408 Add Miner data endpoints Signed-off-by: David Mechler <david.mechler@consensys.net> * #1408 Add Miner data endpoints Signed-off-by: David Mechler <david.mechler@consensys.net> * #1408 Added tests for new miner endpoints Signed-off-by: David Mechler <david.mechler@consensys.net> * #1408 - PR updates Signed-off-by: David Mechler <david.mechler@consensys.net> * SpotlessApply updtes Signed-off-by: David Mechler <david.mechler@consensys.net> * SpotlessApply updtes Signed-off-by: David Mechler <david.mechler@consensys.net> Co-authored-by: David Mechler <davemec@users.noreply.github.com>pull/1553/head
parent
b9364ed243
commit
67191aac76
@ -0,0 +1,136 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* 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; |
||||
|
||||
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.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.MinerDataResult; |
||||
import org.hyperledger.besu.ethereum.api.query.BlockWithMetadata; |
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; |
||||
import org.hyperledger.besu.ethereum.api.query.TransactionReceiptWithMetadata; |
||||
import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata; |
||||
import org.hyperledger.besu.ethereum.core.Address; |
||||
import org.hyperledger.besu.ethereum.core.BlockHeader; |
||||
import org.hyperledger.besu.ethereum.core.Hash; |
||||
import org.hyperledger.besu.ethereum.core.Transaction; |
||||
import org.hyperledger.besu.ethereum.core.Wei; |
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; |
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.Optional; |
||||
import java.util.function.Supplier; |
||||
|
||||
import com.google.common.base.Suppliers; |
||||
import org.apache.tuweni.units.bigints.BaseUInt256Value; |
||||
|
||||
public class EthGetMinerDataByBlockHash implements JsonRpcMethod { |
||||
private final Supplier<BlockchainQueries> blockchain; |
||||
private final ProtocolSchedule protocolSchedule; |
||||
|
||||
public EthGetMinerDataByBlockHash( |
||||
final BlockchainQueries blockchain, final ProtocolSchedule protocolSchedule) { |
||||
this(Suppliers.ofInstance(blockchain), protocolSchedule); |
||||
} |
||||
|
||||
public EthGetMinerDataByBlockHash( |
||||
final Supplier<BlockchainQueries> blockchain, final ProtocolSchedule protocolSchedule) { |
||||
this.blockchain = blockchain; |
||||
this.protocolSchedule = protocolSchedule; |
||||
} |
||||
|
||||
@Override |
||||
public String getName() { |
||||
return RpcMethod.ETH_GET_MINER_DATA_BY_BLOCK_HASH.getMethodName(); |
||||
} |
||||
|
||||
@Override |
||||
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { |
||||
final Hash hash = requestContext.getRequest().getRequiredParameter(0, Hash.class); |
||||
|
||||
BlockWithMetadata<TransactionWithMetadata, Hash> block = |
||||
blockchain.get().blockByHash(hash).orElse(null); |
||||
|
||||
MinerDataResult minerDataResult = null; |
||||
if (block != null) { |
||||
if (!blockchain |
||||
.get() |
||||
.getWorldStateArchive() |
||||
.isWorldStateAvailable(block.getHeader().getStateRoot())) { |
||||
return new JsonRpcErrorResponse( |
||||
requestContext.getRequest().getId(), JsonRpcError.WORLD_STATE_UNAVAILABLE); |
||||
} |
||||
|
||||
minerDataResult = createMinerDataResult(block, protocolSchedule, blockchain.get()); |
||||
} |
||||
|
||||
return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), minerDataResult); |
||||
} |
||||
|
||||
public static MinerDataResult createMinerDataResult( |
||||
final BlockWithMetadata<TransactionWithMetadata, Hash> block, |
||||
final ProtocolSchedule protocolSchedule, |
||||
final BlockchainQueries blockchainQueries) { |
||||
final BlockHeader blockHeader = block.getHeader(); |
||||
final ProtocolSpec protocolSpec = protocolSchedule.getByBlockNumber(blockHeader.getNumber()); |
||||
final Wei staticBlockReward = protocolSpec.getBlockReward(); |
||||
final Wei transactionFee = |
||||
block.getTransactions().stream() |
||||
.map( |
||||
t -> { |
||||
Transaction transaction = t.getTransaction(); |
||||
Optional<TransactionReceiptWithMetadata> transactionReceiptWithMetadata = |
||||
blockchainQueries.transactionReceiptByTransactionHash(transaction.getHash()); |
||||
Wei refundAmount = |
||||
Wei.of( |
||||
transactionReceiptWithMetadata |
||||
.flatMap(tr -> tr.getReceipt().getGasRemaining()) |
||||
.orElse(0L)) |
||||
.multiply(transaction.getGasPrice()); |
||||
return t.getTransaction().getUpfrontCost().subtract(refundAmount); |
||||
}) |
||||
.reduce(Wei.ZERO, BaseUInt256Value::add); |
||||
final Wei uncleInclusionReward = |
||||
staticBlockReward.multiply(block.getOmmers().size()).divide(32); |
||||
final Wei netBlockReward = staticBlockReward.add(transactionFee).add(uncleInclusionReward); |
||||
final Map<Hash, Address> uncleRewards = new HashMap<>(); |
||||
blockchainQueries |
||||
.getBlockchain() |
||||
.getBlockByNumber(block.getHeader().getNumber()) |
||||
.ifPresent( |
||||
blockBody -> |
||||
blockBody |
||||
.getBody() |
||||
.getOmmers() |
||||
.forEach(header -> uncleRewards.put(header.getHash(), header.getCoinbase()))); |
||||
|
||||
return new MinerDataResult( |
||||
netBlockReward, |
||||
staticBlockReward, |
||||
transactionFee, |
||||
uncleInclusionReward, |
||||
uncleRewards, |
||||
blockHeader.getCoinbase(), |
||||
blockHeader.getExtraData(), |
||||
blockHeader.getDifficulty(), |
||||
block.getTotalDifficulty()); |
||||
} |
||||
} |
@ -0,0 +1,71 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* 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; |
||||
|
||||
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.parameters.BlockParameter; |
||||
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.results.MinerDataResult; |
||||
import org.hyperledger.besu.ethereum.api.query.BlockWithMetadata; |
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; |
||||
import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata; |
||||
import org.hyperledger.besu.ethereum.core.Hash; |
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; |
||||
|
||||
public class EthGetMinerDataByBlockNumber extends AbstractBlockParameterMethod { |
||||
private final ProtocolSchedule protocolSchedule; |
||||
|
||||
public EthGetMinerDataByBlockNumber( |
||||
final BlockchainQueries blockchain, final ProtocolSchedule protocolSchedule) { |
||||
super(blockchain); |
||||
this.protocolSchedule = protocolSchedule; |
||||
} |
||||
|
||||
@Override |
||||
public String getName() { |
||||
return RpcMethod.ETH_GET_MINER_DATA_BY_BLOCK_NUMBER.getMethodName(); |
||||
} |
||||
|
||||
@Override |
||||
protected BlockParameter blockParameter(final JsonRpcRequestContext request) { |
||||
return request.getRequiredParameter(0, BlockParameter.class); |
||||
} |
||||
|
||||
@Override |
||||
protected Object resultByBlockNumber( |
||||
final JsonRpcRequestContext request, final long blockNumber) { |
||||
BlockWithMetadata<TransactionWithMetadata, Hash> block = |
||||
getBlockchainQueries().blockByNumber(blockNumber).orElse(null); |
||||
|
||||
MinerDataResult minerDataResult = null; |
||||
if (block != null) { |
||||
if (!getBlockchainQueries() |
||||
.getWorldStateArchive() |
||||
.isWorldStateAvailable(block.getHeader().getStateRoot())) { |
||||
return new JsonRpcErrorResponse( |
||||
request.getRequest().getId(), JsonRpcError.WORLD_STATE_UNAVAILABLE); |
||||
} |
||||
|
||||
minerDataResult = |
||||
EthGetMinerDataByBlockHash.createMinerDataResult( |
||||
block, protocolSchedule, getBlockchainQueries()); |
||||
} |
||||
|
||||
return minerDataResult; |
||||
} |
||||
} |
@ -0,0 +1,119 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* 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.core.Address; |
||||
import org.hyperledger.besu.ethereum.core.Difficulty; |
||||
import org.hyperledger.besu.ethereum.core.Hash; |
||||
import org.hyperledger.besu.ethereum.core.Wei; |
||||
|
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.stream.Collectors; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
|
||||
public class MinerDataResult implements JsonRpcResult { |
||||
private final String netBlockReward; |
||||
private final String staticBlockReward; |
||||
private final String transactionFee; |
||||
private final String uncleInclusionReward; |
||||
private final List<UncleRewardResult> uncleRewards; |
||||
private final String coinbase; |
||||
private final String extraData; |
||||
private final String difficulty; |
||||
private final String totalDifficulty; |
||||
|
||||
public MinerDataResult( |
||||
final Wei netBlockReward, |
||||
final Wei staticBlockReward, |
||||
final Wei transactionFee, |
||||
final Wei uncleInclusionReward, |
||||
final Map<Hash, Address> uncleRewards, |
||||
final Address coinbase, |
||||
final Bytes extraData, |
||||
final Difficulty difficulty, |
||||
final Difficulty totalDifficulty) { |
||||
this.netBlockReward = Quantity.create(netBlockReward); |
||||
this.staticBlockReward = Quantity.create(staticBlockReward); |
||||
this.transactionFee = Quantity.create(transactionFee); |
||||
this.uncleInclusionReward = Quantity.create(uncleInclusionReward); |
||||
this.uncleRewards = setUncleRewards(uncleRewards); |
||||
this.coinbase = coinbase.toString(); |
||||
this.extraData = extraData.toString(); |
||||
this.difficulty = Quantity.create(difficulty); |
||||
this.totalDifficulty = Quantity.create(totalDifficulty); |
||||
} |
||||
|
||||
public String getNetBlockReward() { |
||||
return netBlockReward; |
||||
} |
||||
|
||||
public String getStaticBlockReward() { |
||||
return staticBlockReward; |
||||
} |
||||
|
||||
public String getTransactionFee() { |
||||
return transactionFee; |
||||
} |
||||
|
||||
public String getUncleInclusionReward() { |
||||
return uncleInclusionReward; |
||||
} |
||||
|
||||
public List<UncleRewardResult> getUncleRewards() { |
||||
return uncleRewards; |
||||
} |
||||
|
||||
public String getCoinbase() { |
||||
return coinbase; |
||||
} |
||||
|
||||
public String getExtraData() { |
||||
return extraData; |
||||
} |
||||
|
||||
public String getDifficulty() { |
||||
return difficulty; |
||||
} |
||||
|
||||
public String getTotalDifficulty() { |
||||
return totalDifficulty; |
||||
} |
||||
|
||||
private List<UncleRewardResult> setUncleRewards(final Map<Hash, Address> uncleRewards) { |
||||
return uncleRewards.entrySet().stream() |
||||
.map(b -> new UncleRewardResult(b.getKey().toString(), b.getValue().toString())) |
||||
.collect(Collectors.toList()); |
||||
} |
||||
|
||||
private static class UncleRewardResult { |
||||
private final String hash; |
||||
private final String coinbase; |
||||
|
||||
private UncleRewardResult(final String hash, final String coinbase) { |
||||
this.hash = hash; |
||||
this.coinbase = coinbase; |
||||
} |
||||
|
||||
public String getHash() { |
||||
return hash; |
||||
} |
||||
|
||||
public String getCoinbase() { |
||||
return coinbase; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,158 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* 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; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.WORLD_STATE_UNAVAILABLE; |
||||
import static org.mockito.ArgumentMatchers.any; |
||||
import static org.mockito.Mockito.verifyNoMoreInteractions; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
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.exception.InvalidJsonRpcParameters; |
||||
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.query.BlockWithMetadata; |
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; |
||||
import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata; |
||||
import org.hyperledger.besu.ethereum.chain.Blockchain; |
||||
import org.hyperledger.besu.ethereum.core.BlockHeader; |
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; |
||||
import org.hyperledger.besu.ethereum.core.Difficulty; |
||||
import org.hyperledger.besu.ethereum.core.Hash; |
||||
import org.hyperledger.besu.ethereum.core.Wei; |
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; |
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; |
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.Optional; |
||||
|
||||
import com.google.common.base.Suppliers; |
||||
import org.assertj.core.util.Arrays; |
||||
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 EthGetMinerDataByBlockHashTest { |
||||
@Mock private BlockchainQueries blockchainQueries; |
||||
@Mock private ProtocolSchedule protocolSchedule; |
||||
@Mock private WorldStateArchive worldStateArchive; |
||||
@Mock private ProtocolSpec protocolSpec; |
||||
@Mock private Blockchain blockChain; |
||||
private EthGetMinerDataByBlockHash method; |
||||
private final String ETH_METHOD = "eth_getMinerDataByBlockHash"; |
||||
private final BlockHeaderTestFixture blockHeaderTestFixture = new BlockHeaderTestFixture(); |
||||
|
||||
@Before |
||||
public void before() { |
||||
this.method = |
||||
new EthGetMinerDataByBlockHash(Suppliers.ofInstance(blockchainQueries), protocolSchedule); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldReturnExpectedMethodNameTest() { |
||||
assertThat(method.getName()).isEqualTo(ETH_METHOD); |
||||
} |
||||
|
||||
@Test |
||||
public void successTest() { |
||||
final BlockHeader header = blockHeaderTestFixture.buildHeader(); |
||||
final BlockWithMetadata<TransactionWithMetadata, Hash> blockWithMetadata = |
||||
new BlockWithMetadata<>( |
||||
header, Collections.emptyList(), Collections.emptyList(), Difficulty.of(100L), 5); |
||||
|
||||
when(blockchainQueries.blockByHash(any())).thenReturn(Optional.of(blockWithMetadata)); |
||||
when(blockchainQueries.getWorldStateArchive()).thenReturn(worldStateArchive); |
||||
when(blockchainQueries.getWorldStateArchive().isWorldStateAvailable(any())).thenReturn(true); |
||||
when(protocolSchedule.getByBlockNumber(header.getNumber())).thenReturn(protocolSpec); |
||||
when(protocolSpec.getBlockReward()).thenReturn(Wei.fromEth(2)); |
||||
when(blockchainQueries.getBlockchain()).thenReturn(blockChain); |
||||
|
||||
JsonRpcRequest request = |
||||
new JsonRpcRequest( |
||||
"2.0", |
||||
ETH_METHOD, |
||||
Arrays.array("0x1349e5d4002e72615ae371dc173ba530bf98a7bef886d5b3b00ca5f217565039")); |
||||
JsonRpcRequestContext requestContext = new JsonRpcRequestContext(request); |
||||
JsonRpcResponse response = method.response(requestContext); |
||||
|
||||
assertThat(response).isNotNull().isInstanceOf(JsonRpcSuccessResponse.class); |
||||
assertThat(((JsonRpcSuccessResponse) response).getResult()).isNotNull(); |
||||
assertThat(((JsonRpcSuccessResponse) response).getResult()) |
||||
.hasFieldOrProperty("netBlockReward") |
||||
.hasFieldOrProperty("staticBlockReward") |
||||
.hasFieldOrProperty("transactionFee") |
||||
.hasFieldOrProperty("uncleInclusionReward") |
||||
.hasFieldOrProperty("uncleRewards") |
||||
.hasFieldOrProperty("coinbase") |
||||
.hasFieldOrProperty("extraData") |
||||
.hasFieldOrProperty("difficulty") |
||||
.hasFieldOrProperty("totalDifficulty"); |
||||
} |
||||
|
||||
@Test |
||||
public void worldStateMissingTest() { |
||||
final BlockHeader header = blockHeaderTestFixture.buildHeader(); |
||||
final BlockWithMetadata<TransactionWithMetadata, Hash> blockWithMetadata = |
||||
new BlockWithMetadata<>( |
||||
header, Collections.emptyList(), Collections.emptyList(), Difficulty.of(100L), 5); |
||||
|
||||
when(blockchainQueries.blockByHash(any())).thenReturn(Optional.of(blockWithMetadata)); |
||||
when(blockchainQueries.getWorldStateArchive()).thenReturn(worldStateArchive); |
||||
when(blockchainQueries.getWorldStateArchive().isWorldStateAvailable(any())).thenReturn(false); |
||||
|
||||
JsonRpcRequest request = |
||||
new JsonRpcRequest( |
||||
"2.0", |
||||
ETH_METHOD, |
||||
Arrays.array("0x1349e5d4002e72615ae371dc173ba530bf98a7bef886d5b3b00ca5f217565039")); |
||||
JsonRpcRequestContext requestContext = new JsonRpcRequestContext(request); |
||||
JsonRpcResponse response = method.response(requestContext); |
||||
|
||||
assertThat(response).isNotNull().isInstanceOf(JsonRpcErrorResponse.class); |
||||
assertThat(((JsonRpcErrorResponse) response).getError()).isNotNull(); |
||||
assertThat(((JsonRpcErrorResponse) response).getError()).isEqualTo(WORLD_STATE_UNAVAILABLE); |
||||
} |
||||
|
||||
@Test |
||||
public void exceptionWhenNoHashSuppliedTest() { |
||||
JsonRpcRequest request = new JsonRpcRequest("2.0", ETH_METHOD, Arrays.array()); |
||||
JsonRpcRequestContext requestContext = new JsonRpcRequestContext(request); |
||||
assertThatThrownBy(() -> method.response(requestContext)) |
||||
.isInstanceOf(InvalidJsonRpcParameters.class) |
||||
.hasMessage("Missing required json rpc parameter at index 0"); |
||||
|
||||
verifyNoMoreInteractions(blockchainQueries); |
||||
} |
||||
|
||||
@Test |
||||
public void exceptionWhenHashParamInvalidTest() { |
||||
JsonRpcRequest request = new JsonRpcRequest("2.0", ETH_METHOD, Arrays.array("hash")); |
||||
JsonRpcRequestContext requestContext = new JsonRpcRequestContext(request); |
||||
assertThatThrownBy(() -> method.response(requestContext)) |
||||
.isInstanceOf(InvalidJsonRpcParameters.class) |
||||
.hasMessage("Invalid json rpc parameter at index 0"); |
||||
|
||||
verifyNoMoreInteractions(blockchainQueries); |
||||
} |
||||
} |
@ -0,0 +1,149 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* 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; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.WORLD_STATE_UNAVAILABLE; |
||||
import static org.mockito.ArgumentMatchers.any; |
||||
import static org.mockito.ArgumentMatchers.anyLong; |
||||
import static org.mockito.Mockito.verifyNoMoreInteractions; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
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.exception.InvalidJsonRpcParameters; |
||||
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.query.BlockWithMetadata; |
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; |
||||
import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata; |
||||
import org.hyperledger.besu.ethereum.chain.Blockchain; |
||||
import org.hyperledger.besu.ethereum.core.BlockHeader; |
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; |
||||
import org.hyperledger.besu.ethereum.core.Difficulty; |
||||
import org.hyperledger.besu.ethereum.core.Hash; |
||||
import org.hyperledger.besu.ethereum.core.Wei; |
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; |
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; |
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.Optional; |
||||
|
||||
import org.assertj.core.util.Arrays; |
||||
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 EthGetMinerDataByBlockNumberTest { |
||||
@Mock private BlockchainQueries blockchainQueries; |
||||
@Mock private ProtocolSchedule protocolSchedule; |
||||
@Mock private WorldStateArchive worldStateArchive; |
||||
@Mock private ProtocolSpec protocolSpec; |
||||
@Mock private Blockchain blockChain; |
||||
private EthGetMinerDataByBlockNumber method; |
||||
private final String ETH_METHOD = "eth_getMinerDataByBlockNumber"; |
||||
private final BlockHeaderTestFixture blockHeaderTestFixture = new BlockHeaderTestFixture(); |
||||
|
||||
@Before |
||||
public void before() { |
||||
this.method = new EthGetMinerDataByBlockNumber(blockchainQueries, protocolSchedule); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldReturnExpectedMethodNameTest() { |
||||
assertThat(method.getName()).isEqualTo(ETH_METHOD); |
||||
} |
||||
|
||||
@Test |
||||
public void successTest() { |
||||
final BlockHeader header = blockHeaderTestFixture.buildHeader(); |
||||
final BlockWithMetadata<TransactionWithMetadata, Hash> blockWithMetadata = |
||||
new BlockWithMetadata<>( |
||||
header, Collections.emptyList(), Collections.emptyList(), Difficulty.of(100L), 5); |
||||
|
||||
when(blockchainQueries.blockByNumber(anyLong())).thenReturn(Optional.of(blockWithMetadata)); |
||||
when(blockchainQueries.getWorldStateArchive()).thenReturn(worldStateArchive); |
||||
when(blockchainQueries.getWorldStateArchive().isWorldStateAvailable(any())).thenReturn(true); |
||||
when(protocolSchedule.getByBlockNumber(header.getNumber())).thenReturn(protocolSpec); |
||||
when(protocolSpec.getBlockReward()).thenReturn(Wei.fromEth(2)); |
||||
when(blockchainQueries.getBlockchain()).thenReturn(blockChain); |
||||
|
||||
JsonRpcRequest request = new JsonRpcRequest("2.0", ETH_METHOD, Arrays.array("5094833")); |
||||
JsonRpcRequestContext requestContext = new JsonRpcRequestContext(request); |
||||
JsonRpcResponse response = method.response(requestContext); |
||||
|
||||
assertThat(response).isNotNull().isInstanceOf(JsonRpcSuccessResponse.class); |
||||
assertThat(((JsonRpcSuccessResponse) response).getResult()).isNotNull(); |
||||
assertThat(((JsonRpcSuccessResponse) response).getResult()) |
||||
.hasFieldOrProperty("netBlockReward") |
||||
.hasFieldOrProperty("staticBlockReward") |
||||
.hasFieldOrProperty("transactionFee") |
||||
.hasFieldOrProperty("uncleInclusionReward") |
||||
.hasFieldOrProperty("uncleRewards") |
||||
.hasFieldOrProperty("coinbase") |
||||
.hasFieldOrProperty("extraData") |
||||
.hasFieldOrProperty("difficulty") |
||||
.hasFieldOrProperty("totalDifficulty"); |
||||
} |
||||
|
||||
@Test |
||||
public void worldStateMissingTest() { |
||||
final BlockHeader header = blockHeaderTestFixture.buildHeader(); |
||||
final BlockWithMetadata<TransactionWithMetadata, Hash> blockWithMetadata = |
||||
new BlockWithMetadata<>( |
||||
header, Collections.emptyList(), Collections.emptyList(), Difficulty.of(100L), 5); |
||||
|
||||
when(blockchainQueries.blockByNumber(anyLong())).thenReturn(Optional.of(blockWithMetadata)); |
||||
when(blockchainQueries.getWorldStateArchive()).thenReturn(worldStateArchive); |
||||
when(blockchainQueries.getWorldStateArchive().isWorldStateAvailable(any())).thenReturn(false); |
||||
|
||||
JsonRpcRequest request = new JsonRpcRequest("2.0", ETH_METHOD, Arrays.array("5094833")); |
||||
JsonRpcRequestContext requestContext = new JsonRpcRequestContext(request); |
||||
JsonRpcResponse response = method.response(requestContext); |
||||
|
||||
assertThat(response).isNotNull().isInstanceOf(JsonRpcErrorResponse.class); |
||||
assertThat(((JsonRpcErrorResponse) response).getError()).isNotNull(); |
||||
assertThat(((JsonRpcErrorResponse) response).getError()).isEqualTo(WORLD_STATE_UNAVAILABLE); |
||||
} |
||||
|
||||
@Test |
||||
public void exceptionWhenNoNumberSuppliedTest() { |
||||
JsonRpcRequest request = new JsonRpcRequest("2.0", ETH_METHOD, Arrays.array()); |
||||
JsonRpcRequestContext requestContext = new JsonRpcRequestContext(request); |
||||
assertThatThrownBy(() -> method.response(requestContext)) |
||||
.isInstanceOf(InvalidJsonRpcParameters.class) |
||||
.hasMessage("Missing required json rpc parameter at index 0"); |
||||
|
||||
verifyNoMoreInteractions(blockchainQueries); |
||||
} |
||||
|
||||
@Test |
||||
public void exceptionWhenNumberParamInvalidTest() { |
||||
JsonRpcRequest request = new JsonRpcRequest("2.0", ETH_METHOD, Arrays.array("number")); |
||||
JsonRpcRequestContext requestContext = new JsonRpcRequestContext(request); |
||||
assertThatThrownBy(() -> method.response(requestContext)) |
||||
.isInstanceOf(InvalidJsonRpcParameters.class) |
||||
.hasMessage("Invalid json rpc parameter at index 0"); |
||||
|
||||
verifyNoMoreInteractions(blockchainQueries); |
||||
} |
||||
} |
Loading…
Reference in new issue