diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/jsonrpc/methods/IbftGetValidatorsByBlockNumber.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/jsonrpc/methods/IbftGetValidatorsByBlockNumber.java index 192faba3fc..0a84ca3ac0 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/jsonrpc/methods/IbftGetValidatorsByBlockNumber.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/jsonrpc/methods/IbftGetValidatorsByBlockNumber.java @@ -44,13 +44,13 @@ public IbftGetValidatorsByBlockNumber( @Override protected BlockParameter blockParameter(final JsonRpcRequest request) { - return parameters().required(request.getParams(), 0, BlockParameter.class); + return getParameters().required(request.getParams(), 0, BlockParameter.class); } @Override protected Object resultByBlockNumber(final JsonRpcRequest request, final long blockNumber) { final Optional blockHeader = - blockchainQueries().getBlockHeaderByNumber(blockNumber); + getBlockchainQueries().getBlockHeaderByNumber(blockNumber); LOG.trace("Received RPC rpcName={} block={}", getName(), blockNumber); return blockHeader .map( diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpService.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpService.java index 016f2dfd46..dd6921f080 100755 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpService.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpService.java @@ -496,7 +496,7 @@ private JsonRpcResponse process(final JsonObject requestJson, final Optional methods( enabledMethods, new DebugTraceTransaction( blockchainQueries, new TransactionTracer(blockReplay), parameter), + new DebugAccountRange(parameter, blockchainQueries), new DebugStorageRangeAt(parameter, blockchainQueries, blockReplay), new DebugMetrics(metricsSystem), new DebugTraceBlock( @@ -352,7 +354,7 @@ blockchainQueries, new TransactionTracer(blockReplay), parameter), return enabledMethods; } - private void addMethods( + public static void addMethods( final Map methods, final JsonRpcMethod... rpcMethods) { for (final JsonRpcMethod rpcMethod : rpcMethods) { methods.put(rpcMethod.getName(), rpcMethod); diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AbstractBlockParameterMethod.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AbstractBlockParameterMethod.java index 9362b36cb1..359becef3e 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AbstractBlockParameterMethod.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AbstractBlockParameterMethod.java @@ -20,14 +20,22 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse; import java.util.OptionalLong; +import java.util.function.Supplier; + +import com.google.common.base.Suppliers; public abstract class AbstractBlockParameterMethod implements JsonRpcMethod { - private final BlockchainQueries blockchainQueries; + private final Supplier blockchainQueries; private final JsonRpcParameter parameters; protected AbstractBlockParameterMethod( final BlockchainQueries blockchainQueries, final JsonRpcParameter parameters) { + this(Suppliers.ofInstance(blockchainQueries), parameters); + } + + protected AbstractBlockParameterMethod( + final Supplier blockchainQueries, final JsonRpcParameter parameters) { this.blockchainQueries = blockchainQueries; this.parameters = parameters; } @@ -36,11 +44,11 @@ protected AbstractBlockParameterMethod( protected abstract Object resultByBlockNumber(JsonRpcRequest request, long blockNumber); - protected BlockchainQueries blockchainQueries() { - return blockchainQueries; + protected BlockchainQueries getBlockchainQueries() { + return blockchainQueries.get(); } - protected JsonRpcParameter parameters() { + protected JsonRpcParameter getParameters() { return parameters; } @@ -51,7 +59,7 @@ protected Object pendingResult(final JsonRpcRequest request) { } protected Object latestResult(final JsonRpcRequest request) { - return resultByBlockNumber(request, blockchainQueries.headBlockNumber()); + return resultByBlockNumber(request, blockchainQueries.get().headBlockNumber()); } protected Object findResultByParamType(final JsonRpcRequest request) { diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/DebugAccountRange.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/DebugAccountRange.java new file mode 100644 index 0000000000..8b58c6c1e8 --- /dev/null +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/DebugAccountRange.java @@ -0,0 +1,129 @@ +/* + * Copyright 2019 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. + */ +package tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods; + +import tech.pegasys.pantheon.ethereum.core.Account; +import tech.pegasys.pantheon.ethereum.core.BlockHeader; +import tech.pegasys.pantheon.ethereum.core.Hash; +import tech.pegasys.pantheon.ethereum.core.MutableWorldState; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.BlockParameterOrBlockHash; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockWithMetadata; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.DebugAccountRangeAtResult; +import tech.pegasys.pantheon.util.bytes.Bytes32; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import com.google.common.base.Suppliers; + +public class DebugAccountRange implements JsonRpcMethod { + + private final JsonRpcParameter parameters; + private final Supplier blockchainQueries; + + public DebugAccountRange( + final JsonRpcParameter parameters, final BlockchainQueries blockchainQueries) { + this(parameters, Suppliers.ofInstance(blockchainQueries)); + } + + public DebugAccountRange( + final JsonRpcParameter parameters, final Supplier blockchainQueries) { + this.parameters = parameters; + this.blockchainQueries = blockchainQueries; + } + + @Override + public String getName() { + // TODO(shemnon) 5229b899 is the last stable commit of retesteth, after this they rename the + // method to just "debug_accountRange". Once the tool is stable we will support the new name. + return "debug_accountRangeAt"; + } + + @Override + public JsonRpcResponse response(final JsonRpcRequest request) { + final Object[] params = request.getParams(); + final BlockParameterOrBlockHash blockParameterOrBlockHash = + parameters.required(params, 0, BlockParameterOrBlockHash.class); + final String addressHash = parameters.required(params, 2, String.class); + final int maxResults = parameters.required(params, 3, Integer.TYPE); + + final Optional blockHashOptional = hashFromParameter(blockParameterOrBlockHash); + if (blockHashOptional.isEmpty()) { + return emptyResponse(request); + } + final Hash blockHash = blockHashOptional.get(); + final Optional blockHeaderOptional = + blockchainQueries.get().blockByHash(blockHash).map(BlockWithMetadata::getHeader); + if (blockHeaderOptional.isEmpty()) { + return emptyResponse(request); + } + + // TODO deal with mid-block locations + + final Optional state = + blockchainQueries.get().getWorldState(blockHeaderOptional.get().getNumber()); + + if (state.isEmpty()) { + return emptyResponse(request); + } else { + final List accounts = + state + .get() + .streamAccounts(Bytes32.fromHexStringLenient(addressHash), maxResults + 1) + .collect(Collectors.toList()); + Bytes32 nextKey = Bytes32.ZERO; + if (accounts.size() == maxResults + 1) { + nextKey = accounts.get(maxResults).getAddressHash(); + accounts.remove(maxResults); + } + + return new JsonRpcSuccessResponse( + request.getId(), + new DebugAccountRangeAtResult( + accounts.stream() + .collect( + Collectors.toMap( + account -> account.getAddressHash().toString(), + account -> account.getAddress().toString())), + nextKey.toString())); + } + } + + private Optional hashFromParameter(final BlockParameterOrBlockHash blockParameter) { + if (blockParameter.isEarliest()) { + return blockchainQueries.get().getBlockHashByNumber(0); + } else if (blockParameter.isLatest() || blockParameter.isPending()) { + return blockchainQueries + .get() + .latestBlockWithTxHashes() + .map(block -> block.getHeader().getHash()); + } else if (blockParameter.isNumeric()) { + return blockchainQueries.get().getBlockHashByNumber(blockParameter.getNumber().getAsLong()); + } else { + return blockParameter.getHash(); + } + } + + private JsonRpcSuccessResponse emptyResponse(final JsonRpcRequest request) { + return new JsonRpcSuccessResponse( + request.getId(), new DebugAccountRangeAtResult(Collections.emptyNavigableMap(), null)); + } +} diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/DebugStorageRangeAt.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/DebugStorageRangeAt.java index ca09da9278..58f67e53b3 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/DebugStorageRangeAt.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/DebugStorageRangeAt.java @@ -15,12 +15,15 @@ import tech.pegasys.pantheon.ethereum.core.Account; import tech.pegasys.pantheon.ethereum.core.AccountStorageEntry; import tech.pegasys.pantheon.ethereum.core.Address; +import tech.pegasys.pantheon.ethereum.core.BlockHeader; import tech.pegasys.pantheon.ethereum.core.Hash; import tech.pegasys.pantheon.ethereum.core.MutableWorldState; import tech.pegasys.pantheon.ethereum.jsonrpc.RpcMethod; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.BlockParameterOrBlockHash; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.processor.BlockReplay; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockWithMetadata; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.TransactionWithMetadata; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse; @@ -28,22 +31,40 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.DebugStorageRangeAtResult; import tech.pegasys.pantheon.util.bytes.Bytes32; +import java.util.Collections; import java.util.NavigableMap; import java.util.Optional; +import java.util.function.Supplier; + +import com.google.common.base.Suppliers; public class DebugStorageRangeAt implements JsonRpcMethod { private final JsonRpcParameter parameters; - private final BlockchainQueries blockchainQueries; - private final BlockReplay blockReplay; + private final Supplier blockchainQueries; + private final Supplier blockReplay; + private final boolean shortValues; public DebugStorageRangeAt( final JsonRpcParameter parameters, final BlockchainQueries blockchainQueries, final BlockReplay blockReplay) { + this( + parameters, + Suppliers.ofInstance(blockchainQueries), + Suppliers.ofInstance(blockReplay), + false); + } + + public DebugStorageRangeAt( + final JsonRpcParameter parameters, + final Supplier blockchainQueries, + final Supplier blockReplay, + final boolean shortValues) { this.parameters = parameters; this.blockchainQueries = blockchainQueries; this.blockReplay = blockReplay; + this.shortValues = shortValues; } @Override @@ -53,25 +74,63 @@ public String getName() { @Override public JsonRpcResponse response(final JsonRpcRequest request) { - final Hash blockHash = parameters.required(request.getParams(), 0, Hash.class); + final BlockParameterOrBlockHash blockParameterOrBlockHash = + parameters.required(request.getParams(), 0, BlockParameterOrBlockHash.class); final int transactionIndex = parameters.required(request.getParams(), 1, Integer.class); final Address accountAddress = parameters.required(request.getParams(), 2, Address.class); - final Hash startKey = parameters.required(request.getParams(), 3, Hash.class); + final Hash startKey = + Hash.fromHexStringLenient(parameters.required(request.getParams(), 3, String.class)); final int limit = parameters.required(request.getParams(), 4, Integer.class); + final Optional blockHashOptional = hashFromParameter(blockParameterOrBlockHash); + if (blockHashOptional.isEmpty()) { + return emptyResponse(request); + } + final Hash blockHash = blockHashOptional.get(); + final Optional blockHeaderOptional = + blockchainQueries.get().blockByHash(blockHash).map(BlockWithMetadata::getHeader); + if (blockHeaderOptional.isEmpty()) { + return emptyResponse(request); + } + final Optional optional = - blockchainQueries.transactionByBlockHashAndIndex(blockHash, transactionIndex); + blockchainQueries.get().transactionByBlockHashAndIndex(blockHash, transactionIndex); + return optional .map( transactionWithMetadata -> (blockReplay + .get() .afterTransactionInBlock( blockHash, transactionWithMetadata.getTransaction().hash(), (transaction, blockHeader, blockchain, worldState, transactionProcessor) -> extractStorageAt(request, accountAddress, startKey, limit, worldState)) - .orElseGet(() -> new JsonRpcSuccessResponse(request.getId(), null)))) - .orElseGet(() -> new JsonRpcSuccessResponse(request.getId(), null)); + .orElseGet(() -> emptyResponse(request)))) + .orElseGet( + () -> + blockchainQueries + .get() + .getWorldState(blockHeaderOptional.get().getNumber()) + .map( + worldState -> + extractStorageAt(request, accountAddress, startKey, limit, worldState)) + .orElseGet(() -> emptyResponse(request))); + } + + private Optional hashFromParameter(final BlockParameterOrBlockHash blockParameter) { + if (blockParameter.isEarliest()) { + return blockchainQueries.get().getBlockHashByNumber(0); + } else if (blockParameter.isLatest() || blockParameter.isPending()) { + return blockchainQueries + .get() + .latestBlockWithTxHashes() + .map(block -> block.getHeader().getHash()); + } else if (blockParameter.isNumeric()) { + return blockchainQueries.get().getBlockHashByNumber(blockParameter.getNumber().getAsLong()); + } else { + return blockParameter.getHash(); + } } private JsonRpcSuccessResponse extractStorageAt( @@ -90,6 +149,12 @@ private JsonRpcSuccessResponse extractStorageAt( entries.remove(nextKey); } return new JsonRpcSuccessResponse( - request.getId(), new DebugStorageRangeAtResult(entries, nextKey)); + request.getId(), new DebugStorageRangeAtResult(entries, nextKey, shortValues)); + } + + private JsonRpcSuccessResponse emptyResponse(final JsonRpcRequest request) { + return new JsonRpcSuccessResponse( + request.getId(), + new DebugStorageRangeAtResult(Collections.emptyNavigableMap(), null, shortValues)); } } diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthBlockNumber.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthBlockNumber.java index 9f0e5cf484..2c2d18601f 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthBlockNumber.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthBlockNumber.java @@ -19,12 +19,23 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.Quantity; +import java.util.function.Supplier; + +import com.google.common.base.Suppliers; + public class EthBlockNumber implements JsonRpcMethod { - private final BlockchainQueries blockchain; + private final Supplier blockchain; + private final boolean resultAsInteger; public EthBlockNumber(final BlockchainQueries blockchain) { + this(Suppliers.ofInstance(blockchain), false); + } + + public EthBlockNumber( + final Supplier blockchain, final boolean resultAsInteger) { this.blockchain = blockchain; + this.resultAsInteger = resultAsInteger; } @Override @@ -34,6 +45,8 @@ public String getName() { @Override public JsonRpcResponse response(final JsonRpcRequest req) { - return new JsonRpcSuccessResponse(req.getId(), Quantity.create(blockchain.headBlockNumber())); + final long value = blockchain.get().headBlockNumber(); + return new JsonRpcSuccessResponse( + req.getId(), resultAsInteger ? value : Quantity.create(value)); } } diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthCall.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthCall.java index 6384383fab..dea5421b81 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthCall.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthCall.java @@ -46,7 +46,7 @@ public String getName() { @Override protected BlockParameter blockParameter(final JsonRpcRequest request) { - return parameters().required(request.getParams(), 1, BlockParameter.class); + return getParameters().required(request.getParams(), 1, BlockParameter.class); } @Override @@ -80,7 +80,7 @@ public JsonRpcResponse response(final JsonRpcRequest request) { private CallParameter validateAndGetCallParams(final JsonRpcRequest request) { final JsonCallParameter callParams = - parameters().required(request.getParams(), 0, JsonCallParameter.class); + getParameters().required(request.getParams(), 0, JsonCallParameter.class); if (callParams.getTo() == null) { throw new InvalidJsonRpcParameters("Missing \"to\" field in call arguments"); } diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetBalance.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetBalance.java index 915ca8247c..848bcaa236 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetBalance.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetBalance.java @@ -20,9 +20,18 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.Quantity; +import java.util.function.Supplier; + +import com.google.common.base.Suppliers; + public class EthGetBalance extends AbstractBlockParameterMethod { public EthGetBalance(final BlockchainQueries blockchain, final JsonRpcParameter parameters) { + this(Suppliers.ofInstance(blockchain), parameters); + } + + public EthGetBalance( + final Supplier blockchain, final JsonRpcParameter parameters) { super(blockchain, parameters); } @@ -33,13 +42,13 @@ public String getName() { @Override protected BlockParameter blockParameter(final JsonRpcRequest request) { - return parameters().required(request.getParams(), 1, BlockParameter.class); + return getParameters().required(request.getParams(), 1, BlockParameter.class); } @Override protected String resultByBlockNumber(final JsonRpcRequest request, final long blockNumber) { - final Address address = parameters().required(request.getParams(), 0, Address.class); - return blockchainQueries() + final Address address = getParameters().required(request.getParams(), 0, Address.class); + return getBlockchainQueries() .accountBalance(address, blockNumber) .map(Quantity::create) .orElse(null); diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetBlockByNumber.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetBlockByNumber.java index 0a6cc2494e..e366e64ce5 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetBlockByNumber.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetBlockByNumber.java @@ -20,16 +20,30 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.BlockResult; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.BlockResultFactory; +import java.util.function.Supplier; + +import com.google.common.base.Suppliers; + public class EthGetBlockByNumber extends AbstractBlockParameterMethod { private final BlockResultFactory blockResult; + private final boolean includeCoinbase; public EthGetBlockByNumber( final BlockchainQueries blockchain, final BlockResultFactory blockResult, final JsonRpcParameter parameters) { + this(Suppliers.ofInstance(blockchain), blockResult, parameters, false); + } + + public EthGetBlockByNumber( + final Supplier blockchain, + final BlockResultFactory blockResult, + final JsonRpcParameter parameters, + final boolean includeCoinbase) { super(blockchain, parameters); this.blockResult = blockResult; + this.includeCoinbase = includeCoinbase; } @Override @@ -39,7 +53,7 @@ public String getName() { @Override protected BlockParameter blockParameter(final JsonRpcRequest request) { - return parameters().required(request.getParams(), 0, BlockParameter.class); + return getParameters().required(request.getParams(), 0, BlockParameter.class); } @Override @@ -52,20 +66,20 @@ protected Object resultByBlockNumber(final JsonRpcRequest request, final long bl } private BlockResult transactionComplete(final long blockNumber) { - return blockchainQueries() + return getBlockchainQueries() .blockByNumber(blockNumber) - .map(tx -> blockResult.transactionComplete(tx)) + .map(tx -> blockResult.transactionComplete(tx, includeCoinbase)) .orElse(null); } private BlockResult transactionHash(final long blockNumber) { - return blockchainQueries() + return getBlockchainQueries() .blockByNumberWithTxHashes(blockNumber) - .map(tx -> blockResult.transactionHash(tx)) + .map(tx -> blockResult.transactionHash(tx, includeCoinbase)) .orElse(null); } private boolean isCompleteTransactions(final JsonRpcRequest request) { - return parameters().required(request.getParams(), 1, Boolean.class); + return getParameters().required(request.getParams(), 1, Boolean.class); } } diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetBlockTransactionCountByNumber.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetBlockTransactionCountByNumber.java index 64fd2a5082..9fa8e2db22 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetBlockTransactionCountByNumber.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetBlockTransactionCountByNumber.java @@ -33,11 +33,14 @@ public String getName() { @Override protected BlockParameter blockParameter(final JsonRpcRequest request) { - return parameters().required(request.getParams(), 0, BlockParameter.class); + return getParameters().required(request.getParams(), 0, BlockParameter.class); } @Override protected String resultByBlockNumber(final JsonRpcRequest req, final long blockNumber) { - return blockchainQueries().getTransactionCount(blockNumber).map(Quantity::create).orElse(null); + return getBlockchainQueries() + .getTransactionCount(blockNumber) + .map(Quantity::create) + .orElse(null); } } diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetCode.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetCode.java index 53a521b477..e512f3a044 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetCode.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetCode.java @@ -20,9 +20,18 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries; import tech.pegasys.pantheon.util.bytes.BytesValue; +import java.util.function.Supplier; + +import com.google.common.base.Suppliers; + public class EthGetCode extends AbstractBlockParameterMethod { public EthGetCode(final BlockchainQueries blockchainQueries, final JsonRpcParameter parameters) { + super(Suppliers.ofInstance(blockchainQueries), parameters); + } + + public EthGetCode( + final Supplier blockchainQueries, final JsonRpcParameter parameters) { super(blockchainQueries, parameters); } @@ -33,12 +42,15 @@ public String getName() { @Override protected BlockParameter blockParameter(final JsonRpcRequest request) { - return parameters().required(request.getParams(), 1, BlockParameter.class); + return getParameters().required(request.getParams(), 1, BlockParameter.class); } @Override protected String resultByBlockNumber(final JsonRpcRequest request, final long blockNumber) { - final Address address = parameters().required(request.getParams(), 0, Address.class); - return blockchainQueries().getCode(address, blockNumber).map(BytesValue::toString).orElse(null); + final Address address = getParameters().required(request.getParams(), 0, Address.class); + return getBlockchainQueries() + .getCode(address, blockNumber) + .map(BytesValue::toString) + .orElse(null); } } diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetStorageAt.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetStorageAt.java index 055d47e68b..244579da45 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetStorageAt.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetStorageAt.java @@ -34,15 +34,15 @@ public String getName() { @Override protected BlockParameter blockParameter(final JsonRpcRequest request) { - return parameters().required(request.getParams(), 2, BlockParameter.class); + return getParameters().required(request.getParams(), 2, BlockParameter.class); } @Override protected String resultByBlockNumber(final JsonRpcRequest request, final long blockNumber) { - final Address address = parameters().required(request.getParams(), 0, Address.class); + final Address address = getParameters().required(request.getParams(), 0, Address.class); final UInt256 position = - parameters().required(request.getParams(), 1, UInt256Parameter.class).getValue(); - return blockchainQueries() + getParameters().required(request.getParams(), 1, UInt256Parameter.class).getValue(); + return getBlockchainQueries() .storageAt(address, position, blockNumber) .map(UInt256::toHexString) .orElse(null); diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetTransactionByBlockNumberAndIndex.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetTransactionByBlockNumberAndIndex.java index ebe0e1f17d..d7534e6d5a 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetTransactionByBlockNumberAndIndex.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetTransactionByBlockNumberAndIndex.java @@ -37,15 +37,15 @@ public String getName() { @Override protected BlockParameter blockParameter(final JsonRpcRequest request) { - return parameters().required(request.getParams(), 0, BlockParameter.class); + return getParameters().required(request.getParams(), 0, BlockParameter.class); } @Override protected Object resultByBlockNumber(final JsonRpcRequest request, final long blockNumber) { final int index = - parameters().required(request.getParams(), 1, UnsignedIntParameter.class).getValue(); + getParameters().required(request.getParams(), 1, UnsignedIntParameter.class).getValue(); final Optional transactionWithMetadata = - blockchainQueries().transactionByBlockNumberAndIndex(blockNumber, index); + getBlockchainQueries().transactionByBlockNumberAndIndex(blockNumber, index); return transactionWithMetadata.map(TransactionCompleteResult::new).orElse(null); } } diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetTransactionCount.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetTransactionCount.java index 4fdf888017..c7f33d095b 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetTransactionCount.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetTransactionCount.java @@ -22,17 +22,34 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.Quantity; import java.util.OptionalLong; +import java.util.function.Supplier; + +import com.google.common.base.Suppliers; public class EthGetTransactionCount extends AbstractBlockParameterMethod { - private final PendingTransactions pendingTransactions; + private final Supplier pendingTransactions; + private final boolean resultAsDecimal; public EthGetTransactionCount( final BlockchainQueries blockchain, final PendingTransactions pendingTransactions, final JsonRpcParameter parameters) { + this( + Suppliers.ofInstance(blockchain), + Suppliers.ofInstance(pendingTransactions), + parameters, + false); + } + + public EthGetTransactionCount( + final Supplier blockchain, + final Supplier pendingTransactions, + final JsonRpcParameter parameters, + final boolean resultAsDecimal) { super(blockchain, parameters); this.pendingTransactions = pendingTransactions; + this.resultAsDecimal = resultAsDecimal; } @Override @@ -42,13 +59,13 @@ public String getName() { @Override protected BlockParameter blockParameter(final JsonRpcRequest request) { - return parameters().required(request.getParams(), 1, BlockParameter.class); + return getParameters().required(request.getParams(), 1, BlockParameter.class); } @Override protected Object pendingResult(final JsonRpcRequest request) { - final Address address = parameters().required(request.getParams(), 0, Address.class); - final OptionalLong pendingNonce = pendingTransactions.getNextNonceForSender(address); + final Address address = getParameters().required(request.getParams(), 0, Address.class); + final OptionalLong pendingNonce = pendingTransactions.get().getNextNonceForSender(address); if (pendingNonce.isPresent()) { return Quantity.create(pendingNonce.getAsLong()); } else { @@ -58,10 +75,11 @@ protected Object pendingResult(final JsonRpcRequest request) { @Override protected String resultByBlockNumber(final JsonRpcRequest request, final long blockNumber) { - final Address address = parameters().required(request.getParams(), 0, Address.class); - if (blockNumber > blockchainQueries().headBlockNumber()) { + final Address address = getParameters().required(request.getParams(), 0, Address.class); + if (blockNumber > getBlockchainQueries().headBlockNumber()) { return null; } - return Quantity.create(blockchainQueries().getTransactionCount(address, blockNumber)); + final long transactionCount = getBlockchainQueries().getTransactionCount(address, blockNumber); + return resultAsDecimal ? Long.toString(transactionCount) : Quantity.create(transactionCount); } } diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetUncleByBlockNumberAndIndex.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetUncleByBlockNumberAndIndex.java index 3a462d3924..f029455d2b 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetUncleByBlockNumberAndIndex.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetUncleByBlockNumberAndIndex.java @@ -35,14 +35,14 @@ public String getName() { @Override protected BlockParameter blockParameter(final JsonRpcRequest request) { - return parameters().required(request.getParams(), 0, BlockParameter.class); + return getParameters().required(request.getParams(), 0, BlockParameter.class); } @Override protected BlockResult resultByBlockNumber(final JsonRpcRequest request, final long blockNumber) { final int index = - parameters().required(request.getParams(), 1, UnsignedIntParameter.class).getValue(); - return blockchainQueries() + getParameters().required(request.getParams(), 1, UnsignedIntParameter.class).getValue(); + return getBlockchainQueries() .getOmmer(blockNumber, index) .map(UncleBlockResult::build) .orElse(null); diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetUncleCountByBlockNumber.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetUncleCountByBlockNumber.java index a04bafc22d..626e25aa0a 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetUncleCountByBlockNumber.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetUncleCountByBlockNumber.java @@ -33,11 +33,11 @@ public String getName() { @Override protected BlockParameter blockParameter(final JsonRpcRequest request) { - return parameters().required(request.getParams(), 0, BlockParameter.class); + return getParameters().required(request.getParams(), 0, BlockParameter.class); } @Override protected String resultByBlockNumber(final JsonRpcRequest request, final long blockNumber) { - return blockchainQueries().getOmmerCount(blockNumber).map(Quantity::create).orElse(null); + return getBlockchainQueries().getOmmerCount(blockNumber).map(Quantity::create).orElse(null); } } diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthSendRawTransaction.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthSendRawTransaction.java index 708ac200fa..7d54225453 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthSendRawTransaction.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthSendRawTransaction.java @@ -14,6 +14,7 @@ import static tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcErrorConverter.convertTransactionInvalidReason; +import tech.pegasys.pantheon.ethereum.core.Hash; import tech.pegasys.pantheon.ethereum.core.Transaction; import tech.pegasys.pantheon.ethereum.eth.transactions.TransactionPool; import tech.pegasys.pantheon.ethereum.jsonrpc.RpcMethod; @@ -30,20 +31,32 @@ import tech.pegasys.pantheon.ethereum.rlp.RLPException; import tech.pegasys.pantheon.util.bytes.BytesValue; +import java.util.function.Supplier; + +import com.google.common.base.Suppliers; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class EthSendRawTransaction implements JsonRpcMethod { private static final Logger LOG = LogManager.getLogger(); + private final boolean sendEmptyHashOnInvalidBlock; - private final TransactionPool transactionPool; + private final Supplier transactionPool; private final JsonRpcParameter parameters; public EthSendRawTransaction( final TransactionPool transactionPool, final JsonRpcParameter parameters) { + this(Suppliers.ofInstance(transactionPool), parameters, false); + } + + public EthSendRawTransaction( + final Supplier transactionPool, + final JsonRpcParameter parameters, + final boolean sendEmptyHashOnInvalidBlock) { this.transactionPool = transactionPool; this.parameters = parameters; + this.sendEmptyHashOnInvalidBlock = sendEmptyHashOnInvalidBlock; } @Override @@ -66,12 +79,14 @@ public JsonRpcResponse response(final JsonRpcRequest request) { } final ValidationResult validationResult = - transactionPool.addLocalTransaction(transaction); + transactionPool.get().addLocalTransaction(transaction); return validationResult.either( () -> new JsonRpcSuccessResponse(request.getId(), transaction.hash().toString()), errorReason -> - new JsonRpcErrorResponse( - request.getId(), convertTransactionInvalidReason(errorReason))); + sendEmptyHashOnInvalidBlock + ? new JsonRpcSuccessResponse(request.getId(), Hash.EMPTY.toString()) + : new JsonRpcErrorResponse( + request.getId(), convertTransactionInvalidReason(errorReason))); } private Transaction decodeRawTransaction(final String hash) diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/parameters/BlockParameterOrBlockHash.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/parameters/BlockParameterOrBlockHash.java new file mode 100644 index 0000000000..d8c614ce35 --- /dev/null +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/parameters/BlockParameterOrBlockHash.java @@ -0,0 +1,100 @@ +/* + * Copyright 2018 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. + */ +package tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters; + +import tech.pegasys.pantheon.ethereum.core.BlockHeader; +import tech.pegasys.pantheon.ethereum.core.Hash; + +import java.util.Objects; +import java.util.Optional; +import java.util.OptionalLong; + +import com.fasterxml.jackson.annotation.JsonCreator; + +/** + * Represents a block parameter that can be a special value ("pending", "earliest", "latest") or a + * number formatted as a hex string or a block hash. + * + *

When distinguishing between a hash and a number it is presumed that a hash won't have three + * quarters of the leading bytes as zero. This is fined for block hashes but not for precompiled + * contracts. + */ +public class BlockParameterOrBlockHash { + + private final BlockParameterType type; + private final OptionalLong number; + private final Optional blockHash; + + @JsonCreator + public BlockParameterOrBlockHash(final String value) { + final String normalizedValue = value.toLowerCase(); + + if (Objects.equals(normalizedValue, "earliest")) { + type = BlockParameterType.EARLIEST; + number = OptionalLong.of(BlockHeader.GENESIS_BLOCK_NUMBER); + blockHash = Optional.empty(); + } else if (Objects.equals(normalizedValue, "latest")) { + type = BlockParameterType.LATEST; + number = OptionalLong.empty(); + blockHash = Optional.empty(); + } else if (Objects.equals(normalizedValue, "pending")) { + type = BlockParameterType.PENDING; + number = OptionalLong.empty(); + blockHash = Optional.empty(); + } else if (normalizedValue.length() > 16) { + type = BlockParameterType.HASH; + number = OptionalLong.empty(); + blockHash = Optional.of(Hash.fromHexStringLenient(normalizedValue)); + } else { + type = BlockParameterType.NUMERIC; + number = OptionalLong.of(Long.decode(value)); + blockHash = Optional.empty(); + } + } + + public OptionalLong getNumber() { + return number; + } + + public Optional getHash() { + return blockHash; + } + + public boolean isPending() { + return this.type == BlockParameterType.PENDING; + } + + public boolean isLatest() { + return this.type == BlockParameterType.LATEST; + } + + public boolean isEarliest() { + return this.type == BlockParameterType.EARLIEST; + } + + public boolean isNumeric() { + return this.type == BlockParameterType.NUMERIC; + } + + public boolean getBlockHash() { + return this.type == BlockParameterType.HASH; + } + + private enum BlockParameterType { + EARLIEST, + LATEST, + PENDING, + NUMERIC, + HASH + } +} diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/BlockResult.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/BlockResult.java index 9811c3f38c..e70b4f0dd4 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/BlockResult.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/BlockResult.java @@ -18,6 +18,8 @@ import java.util.List; import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.databind.JsonNode; @@ -63,6 +65,7 @@ public class BlockResult implements JsonRpcResult { private final String timestamp; private final List transactions; private final List ommers; + private final String coinbase; public BlockResult( final BlockHeader header, @@ -70,6 +73,16 @@ public BlockResult( final List ommers, final UInt256 totalDifficulty, final int size) { + this(header, transactions, ommers, totalDifficulty, size, false); + } + + public BlockResult( + final BlockHeader header, + final List transactions, + final List ommers, + final UInt256 totalDifficulty, + final int size, + final boolean includeCoinbase) { this.number = Quantity.create(header.getNumber()); this.hash = header.getHash().toString(); this.parentHash = header.getParentHash().toString(); @@ -89,6 +102,7 @@ public BlockResult( this.timestamp = Quantity.create(header.getTimestamp()); this.ommers = ommers; this.transactions = transactions; + this.coinbase = includeCoinbase ? header.getCoinbase().toString() : null; } @JsonGetter(value = "number") @@ -185,4 +199,10 @@ public List getOmmers() { public List getTransactions() { return transactions; } + + @JsonGetter(value = "author") + @JsonInclude(Include.NON_NULL) + public String getCoinbase() { + return coinbase; + } } diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/BlockResultFactory.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/BlockResultFactory.java index c1a07376e9..22a68fedec 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/BlockResultFactory.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/BlockResultFactory.java @@ -26,6 +26,12 @@ public class BlockResultFactory { public BlockResult transactionComplete( final BlockWithMetadata blockWithMetadata) { + return transactionComplete(blockWithMetadata, false); + } + + public BlockResult transactionComplete( + final BlockWithMetadata blockWithMetadata, + final boolean includeCoinbase) { final List txs = blockWithMetadata.getTransactions().stream() .map(TransactionCompleteResult::new) @@ -40,10 +46,16 @@ public BlockResult transactionComplete( txs, ommers, blockWithMetadata.getTotalDifficulty(), - blockWithMetadata.getSize()); + blockWithMetadata.getSize(), + includeCoinbase); } public BlockResult transactionHash(final BlockWithMetadata blockWithMetadata) { + return transactionHash(blockWithMetadata, false); + } + + public BlockResult transactionHash( + final BlockWithMetadata blockWithMetadata, final boolean includeCoinbase) { final List txs = blockWithMetadata.getTransactions().stream() .map(Hash::toString) @@ -59,6 +71,7 @@ public BlockResult transactionHash(final BlockWithMetadata blockWith txs, ommers, blockWithMetadata.getTotalDifficulty(), - blockWithMetadata.getSize()); + blockWithMetadata.getSize(), + includeCoinbase); } } diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/DebugAccountRangeAtResult.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/DebugAccountRangeAtResult.java new file mode 100644 index 0000000000..57bf54ff88 --- /dev/null +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/DebugAccountRangeAtResult.java @@ -0,0 +1,38 @@ +/* + * Copyright 2018 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. + */ +package tech.pegasys.pantheon.ethereum.jsonrpc.internal.results; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonGetter; + +public class DebugAccountRangeAtResult implements JsonRpcResult { + + private final Map addressMap; + private final String nextKey; + + public DebugAccountRangeAtResult(final Map addressMap, final String nextKey) { + this.addressMap = addressMap; + this.nextKey = nextKey; + } + + @JsonGetter(value = "addressMap") + public Map getAddressMap() { + return addressMap; + } + + @JsonGetter(value = "nextKey") + public String getNextKey() { + return nextKey; + } +} diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/DebugStorageRangeAtResult.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/DebugStorageRangeAtResult.java index 2eac1dee2d..c43b3b5f5e 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/DebugStorageRangeAtResult.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/DebugStorageRangeAtResult.java @@ -30,9 +30,37 @@ public class DebugStorageRangeAtResult implements JsonRpcResult { private final String nextKey; public DebugStorageRangeAtResult( - final NavigableMap entries, final Bytes32 nextKey) { - entries.forEach((keyHash, entry) -> storage.put(keyHash.toString(), new StorageEntry(entry))); - this.nextKey = nextKey != null ? nextKey.toString() : null; + final NavigableMap entries, + final Bytes32 nextKey, + final boolean shortValues) { + if (shortValues) { + entries.forEach( + (keyHash, entry) -> + storage.put( + toStrictShortHexString(keyHash.toString()), + new StorageEntry(entry, shortValues))); + this.nextKey = nextKey != null ? nextKey.toString() : null; + + } else { + entries.forEach( + (keyHash, entry) -> + storage.put(keyHash.toString(), new StorageEntry(entry, shortValues))); + this.nextKey = nextKey != null ? nextKey.toString() : null; + } + } + + private static String toStrictShortHexString(final String hex) { + // Skipping '0x' + if (hex.charAt(2) != '0') return hex; + + int i = 3; + while (i < hex.length() - 1 && hex.charAt(i) == '0') { + i++; + } + // Align the trim so we get full bytes, not stray nybbles. + i = i & 0xFFFFFFFE; + + return "0x" + hex.substring(i); } @JsonGetter(value = "storage") @@ -45,14 +73,24 @@ public String getNextKey() { return nextKey; } + @JsonGetter(value = "complete") + public boolean getComplete() { + return nextKey == null; + } + @JsonPropertyOrder(value = {"key", "value"}) public static class StorageEntry { private final String value; private final String key; - public StorageEntry(final AccountStorageEntry entry) { - this.value = entry.getValue().toHexString(); - this.key = entry.getKey().map(UInt256::toHexString).orElse(null); + public StorageEntry(final AccountStorageEntry entry, final boolean shortValues) { + if (shortValues) { + this.value = entry.getValue().toStrictShortHexString(); + this.key = entry.getKey().map(UInt256::toStrictShortHexString).orElse(null); + } else { + this.value = entry.getValue().toHexString(); + this.key = entry.getKey().map(UInt256::toHexString).orElse(null); + } } @JsonGetter(value = "key") @@ -67,7 +105,7 @@ public String getValue() { @Override public String toString() { - return MoreObjects.toStringHelper(this).add("value", value).toString(); + return MoreObjects.toStringHelper(this).add("key", key).add("value", value).toString(); } @Override diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/AbstractEthJsonRpcHttpServiceTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/AbstractEthJsonRpcHttpServiceTest.java index 302c80e212..89b9ced9f7 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/AbstractEthJsonRpcHttpServiceTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/AbstractEthJsonRpcHttpServiceTest.java @@ -103,7 +103,7 @@ public abstract class AbstractEthJsonRpcHttpServiceTest { protected final int NETWORK_ID = 123; protected static final Collection JSON_RPC_APIS = - Arrays.asList(RpcApis.ETH, RpcApis.NET, RpcApis.WEB3); + Arrays.asList(RpcApis.ETH, RpcApis.NET, RpcApis.WEB3, RpcApis.DEBUG); protected MutableBlockchain blockchain; diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/EthJsonRpcHttpBySpecTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/EthJsonRpcHttpBySpecTest.java index f9912a04bc..c672fa3aa9 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/EthJsonRpcHttpBySpecTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/EthJsonRpcHttpBySpecTest.java @@ -14,10 +14,12 @@ import static org.assertj.core.api.Assertions.assertThat; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.DebugAccountRange; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.EthBlockNumber; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.EthCall; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.EthEstimateGas; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.EthGetBalance; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.EthGetBlockByNumber; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.EthGetBlockTransactionCountByHash; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.EthGetBlockTransactionCountByNumber; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.EthGetCode; @@ -84,6 +86,9 @@ public static Collection specs() { specs.put(EthGetBalance.class, "eth_getBalance_illegalRangeLessThan"); specs.put(EthGetBalance.class, "eth_getBalance_invalidParams"); + specs.put(EthGetBlockByNumber.class, "eth_getBlockByNumber_complete"); + specs.put(EthGetBlockByNumber.class, "eth_getBlockByNumber_hashes"); + specs.put(EthGetStorageAt.class, "eth_getStorageAt_latest"); specs.put(EthGetStorageAt.class, "eth_getStorageAt_invalidParams"); specs.put(EthGetStorageAt.class, "eth_getStorageAt_illegalRangeGreaterThan"); @@ -239,6 +244,13 @@ public static Collection specs() { specs.put(EthProtocolVersion.class, "eth_protocolVersion"); + specs.put(DebugAccountRange.class, "debug_accountRange_blockHash"); + specs.put(DebugAccountRange.class, "debug_accountRange_complete"); + specs.put(DebugAccountRange.class, "debug_accountRange_partial"); + specs.put(DebugAccountRange.class, "debug_storageRangeAt_blockHash"); + specs.put(DebugAccountRange.class, "debug_storageRangeAt_blockNumber"); + specs.put(DebugAccountRange.class, "debug_storageRangeAt_midBlock"); + return specs.values(); } diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/DebugStorageRangeAtTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/DebugStorageRangeAtTest.java index 0de3a242e9..1a777417c2 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/DebugStorageRangeAtTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/DebugStorageRangeAtTest.java @@ -32,6 +32,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.processor.BlockReplay; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.processor.BlockReplay.TransactionAction; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockWithMetadata; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.TransactionWithMetadata; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse; @@ -42,6 +43,7 @@ import tech.pegasys.pantheon.util.uint.UInt256; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.NavigableMap; @@ -85,9 +87,16 @@ public void nameShouldBeDebugStorageRangeAt() { } @Test - public void shouldRetrieveStorageRange() { + public void shouldRetrieveStorageRange_fullValues() { final TransactionWithMetadata transactionWithMetadata = new TransactionWithMetadata(transaction, 12L, blockHash, TRANSACTION_INDEX); + final BlockWithMetadata blockWithMetadata = + new BlockWithMetadata<>( + blockHeader, + Collections.singletonList(transactionWithMetadata), + Collections.emptyList(), + UInt256.ONE, + 1); final JsonRpcRequest request = new JsonRpcRequest( "2.0", @@ -96,6 +105,7 @@ public void shouldRetrieveStorageRange() { blockHash.toString(), TRANSACTION_INDEX, accountAddress, START_KEY_HASH.toString(), 10 }); + when(blockchainQueries.blockByHash(blockHash)).thenReturn(Optional.of(blockWithMetadata)); when(blockchainQueries.transactionByBlockHashAndIndex(blockHash, TRANSACTION_INDEX)) .thenReturn(Optional.of(transactionWithMetadata)); when(worldState.get(accountAddress)).thenReturn(account); @@ -122,9 +132,9 @@ public void shouldRetrieveStorageRange() { entries.sort(Comparator.comparing(AccountStorageEntry::getKeyHash)); assertThat(result.getStorage()) .containsExactly( - entry(entries.get(0).getKeyHash().toString(), new StorageEntry(entries.get(0))), - entry(entries.get(1).getKeyHash().toString(), new StorageEntry(entries.get(1))), - entry(entries.get(2).getKeyHash().toString(), new StorageEntry(entries.get(2)))); + entry(entries.get(0).getKeyHash().toString(), new StorageEntry(entries.get(0), false)), + entry(entries.get(1).getKeyHash().toString(), new StorageEntry(entries.get(1), false)), + entry(entries.get(2).getKeyHash().toString(), new StorageEntry(entries.get(2), false))); } private Object callAction(final InvocationOnMock invocation) { diff --git a/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/debug_accountRange_blockHash.json b/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/debug_accountRange_blockHash.json new file mode 100644 index 0000000000..527a6f26a9 --- /dev/null +++ b/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/debug_accountRange_blockHash.json @@ -0,0 +1,25 @@ +{ + "request": { + "id": 415, + "jsonrpc": "2.0", + "method": "debug_accountRangeAt", + "params": [ + "0xc8df1f061abb4d0c107b2b1a794ade8780b3120e681f723fe55a7be586d95ba6", + "0x1", + "0xbcde5374fce5edbc8e2a8697c15331677e6ebf0b", + 2 + ] + }, + "response": { + "jsonrpc": "2.0", + "id": 415, + "result": { + "addressMap": { + "0x15b040a190663dd210787de31621bae9fb17d6b59d1cd1d319c0ed452d7a0f15": "0xbcde5374fce5edbc8e2a8697c15331677e6ebf0b", + "0x03601462093b5945d1676df093446790fd31b20e7b12a2e8e5e09d068109616b": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" + }, + "nextKey": "0xa32fbb2bcac38ed596cbb34ae265df4d60b832ce8077a3abc6f57b4611005cfd" + } + }, + "statusCode": 200 +} \ No newline at end of file diff --git a/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/debug_accountRange_complete.json b/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/debug_accountRange_complete.json new file mode 100644 index 0000000000..001075bc87 --- /dev/null +++ b/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/debug_accountRange_complete.json @@ -0,0 +1,27 @@ +{ + "request": { + "id": 414, + "jsonrpc": "2.0", + "method": "debug_accountRangeAt", + "params": [ + "0x10", + "0x1", + "0x0", + 100 + ] + }, + "response": { + "jsonrpc": "2.0", + "id": 414, + "result": { + "addressMap": { + "0x15b040a190663dd210787de31621bae9fb17d6b59d1cd1d319c0ed452d7a0f15" : "0xbcde5374fce5edbc8e2a8697c15331677e6ebf0b", + "0x03601462093b5945d1676df093446790fd31b20e7b12a2e8e5e09d068109616b" : "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "0xa32fbb2bcac38ed596cbb34ae265df4d60b832ce8077a3abc6f57b4611005cfd" : "0x8888f1f195afa192cfee860698584c030f4c9db1", + "0xeb8ec137a2f5a74ec3a73144b552caad890b18b5f725872fa212fff6d4d565ba" : "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f" + }, + "nextKey": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "statusCode": 200 +} \ No newline at end of file diff --git a/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/debug_accountRange_partial.json b/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/debug_accountRange_partial.json new file mode 100644 index 0000000000..bfbf0b4ee3 --- /dev/null +++ b/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/debug_accountRange_partial.json @@ -0,0 +1,25 @@ +{ + "request": { + "id": 415, + "jsonrpc": "2.0", + "method": "debug_accountRangeAt", + "params": [ + "0x10", + "0x1", + "0xbcde5374fce5edbc8e2a8697c15331677e6ebf0b", + 2 + ] + }, + "response": { + "jsonrpc": "2.0", + "id": 415, + "result": { + "addressMap": { + "0x15b040a190663dd210787de31621bae9fb17d6b59d1cd1d319c0ed452d7a0f15": "0xbcde5374fce5edbc8e2a8697c15331677e6ebf0b", + "0x03601462093b5945d1676df093446790fd31b20e7b12a2e8e5e09d068109616b": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" + }, + "nextKey": "0xa32fbb2bcac38ed596cbb34ae265df4d60b832ce8077a3abc6f57b4611005cfd" + } + }, + "statusCode": 200 +} \ No newline at end of file diff --git a/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/debug_storageRangeAt_blockHash.json b/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/debug_storageRangeAt_blockHash.json new file mode 100644 index 0000000000..eb978418c6 --- /dev/null +++ b/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/debug_storageRangeAt_blockHash.json @@ -0,0 +1,44 @@ +{ + "request": { + "id": 414, + "jsonrpc": "2.0", + "method": "debug_storageRangeAt", + "params": [ + "0xc8df1f061abb4d0c107b2b1a794ade8780b3120e681f723fe55a7be586d95ba6", + 1, + "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f", + "0", + 100 + ] + }, + "response": { + "jsonrpc": "2.0", + "id": 414, + "result": { + "storage": { + "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563": { + "key": "0x0000000000000000000000000000000000000000000000000000000000000000", + "value": "0x000000000000000000000000000000000000000000000000000000000008fa01" + }, + "0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace": { + "key": "0x0000000000000000000000000000000000000000000000000000000000000002", + "value": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee" + }, + "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b": { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "value": "0xaabbccffffffffffffffffffffffffffffffffffffffffffffffffffffffffee" + }, + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6": { + "key": "0x0000000000000000000000000000000000000000000000000000000000000001", + "value": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee" + }, + "0xc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b": { + "key": "0x0000000000000000000000000000000000000000000000000000000000000003", + "value": "0xaabbccffffffffffffffffffffffffffffffffffffffffffffffffffffffffee" + } + }, + "complete": true + } + }, + "statusCode": 200 +} \ No newline at end of file diff --git a/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/debug_storageRangeAt_blockNumber.json b/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/debug_storageRangeAt_blockNumber.json new file mode 100644 index 0000000000..9382b60fa3 --- /dev/null +++ b/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/debug_storageRangeAt_blockNumber.json @@ -0,0 +1,44 @@ +{ + "request": { + "id": 414, + "jsonrpc": "2.0", + "method": "debug_storageRangeAt", + "params": [ + "0x1e", + 1, + "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f", + "0", + 100 + ] + }, + "response": { + "jsonrpc": "2.0", + "id": 414, + "result": { + "storage": { + "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563": { + "key": "0x0000000000000000000000000000000000000000000000000000000000000000", + "value": "0x000000000000000000000000000000000000000000000000000000000008fa01" + }, + "0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace": { + "key": "0x0000000000000000000000000000000000000000000000000000000000000002", + "value": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee" + }, + "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b": { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "value": "0xaabbccffffffffffffffffffffffffffffffffffffffffffffffffffffffffee" + }, + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6": { + "key": "0x0000000000000000000000000000000000000000000000000000000000000001", + "value": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee" + }, + "0xc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b": { + "key": "0x0000000000000000000000000000000000000000000000000000000000000003", + "value": "0xaabbccffffffffffffffffffffffffffffffffffffffffffffffffffffffffee" + } + }, + "complete": true + } + }, + "statusCode": 200 +} \ No newline at end of file diff --git a/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/debug_storageRangeAt_midBlock.json b/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/debug_storageRangeAt_midBlock.json new file mode 100644 index 0000000000..24c5d5ba32 --- /dev/null +++ b/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/debug_storageRangeAt_midBlock.json @@ -0,0 +1,44 @@ +{ + "request": { + "id": 414, + "jsonrpc": "2.0", + "method": "debug_storageRangeAt", + "params": [ + "0x1e", + 0, + "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f", + "0", + 100 + ] + }, + "response": { + "jsonrpc": "2.0", + "id": 414, + "result": { + "storage": { + "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563": { + "key": "0x0000000000000000000000000000000000000000000000000000000000000000", + "value": "0x000000000000000000000000000000000000000000000000000000000008fa01" + }, + "0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace": { + "key": "0x0000000000000000000000000000000000000000000000000000000000000002", + "value": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee" + }, + "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b": { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "value": "0xaabbccffffffffffffffffffffffffffffffffffffffffffffffffffffffffee" + }, + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6": { + "key": "0x0000000000000000000000000000000000000000000000000000000000000001", + "value": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee" + }, + "0xc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b": { + "key": "0x0000000000000000000000000000000000000000000000000000000000000003", + "value": "0xaabbccffffffffffffffffffffffffffffffffffffffffffffffffffffffffee" + } + }, + "complete": true + } + }, + "statusCode": 200 +} \ No newline at end of file diff --git a/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/eth_getBlockByNumber_complete.json b/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/eth_getBlockByNumber_complete.json new file mode 100644 index 0000000000..081d750ac5 --- /dev/null +++ b/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/eth_getBlockByNumber_complete.json @@ -0,0 +1,54 @@ +{ + "request": { + "id": 174, + "jsonrpc": "2.0", + "method": "eth_getBlockByNumber", + "params": [ + "0x10", + true + ] + }, + "response": { + "jsonrpc": "2.0", + "id": 174, + "result": { + "number": "0x10", + "hash": "0x1878c6f27178250f3d55186a2887b076936599f307d96dabcf331b2ff0a38f0c", + "parentHash": "0x9dcd5e20cc30e48e3635ac3a1c398c2f45ee818fcbc232110878ddf0e936e7ea", + "nonce": "0xdfc50ac1134d764f", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "transactionsRoot": "0x399a0923c02fd28a351a11cbf2fd647225c0a69a67d7e04ad1c35c9435fc7d82", + "stateRoot": "0xf4814dce3c4230ab409f92001f88e55d63ed5a23d84c73ad3fad1222ba06cb7a", + "receiptsRoot": "0x3e5fff5f6eaeee82841547e049b6bdb980c4e6bd8539fb072a346a30ee72a25d", + "miner": "0x8888f1f195afa192cfee860698584c030f4c9db1", + "difficulty": "0x203c0", + "totalDifficulty": "0x221e00", + "extraData": "0x", + "size": "0x288", + "gasLimit": "0x2fefd8", + "gasUsed": "0xab90", + "timestamp": "0x561bc307", + "uncles": [], + "transactions": [ + { + "blockHash": "0x1878c6f27178250f3d55186a2887b076936599f307d96dabcf331b2ff0a38f0c", + "blockNumber": "0x10", + "from": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "gas": "0x4cb2f", + "gasPrice": "0x1", + "hash": "0x9f6a5eb65991ce483a9e1bfc57b44cd2c8ff4c26ca804240d593b4b63c3eafcd", + "input": "0xc2b12a73aabbccffffffffffffffffffffffffffffffffffffffffffffffffffffffffee", + "nonce": "0xf", + "to": "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f", + "transactionIndex": "0x0", + "value": "0xa", + "v": "0x1b", + "r": "0x394fa9c43a2469d48b98f4f7e8dfdf7bd3c7abd9eafe8c124b362594aafc5d6", + "s": "0xecf369283eda15f4c4db09784b5226dfe48a0a80ff91e87d858f6adbb131cf9" + } + ] + } + }, + "statusCode": 200 +} \ No newline at end of file diff --git a/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/eth_getBlockByNumber_hashes.json b/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/eth_getBlockByNumber_hashes.json new file mode 100644 index 0000000000..63ad083507 --- /dev/null +++ b/ethereum/jsonrpc/src/test/resources/tech/pegasys/pantheon/ethereum/jsonrpc/eth_getBlockByNumber_hashes.json @@ -0,0 +1,39 @@ +{ + "request": { + "id": 174, + "jsonrpc": "2.0", + "method": "eth_getBlockByNumber", + "params": [ + "0x10", + false + ] + }, + "response": { + "jsonrpc": "2.0", + "id": 174, + "result": { + "number": "0x10", + "hash": "0x1878c6f27178250f3d55186a2887b076936599f307d96dabcf331b2ff0a38f0c", + "parentHash": "0x9dcd5e20cc30e48e3635ac3a1c398c2f45ee818fcbc232110878ddf0e936e7ea", + "nonce": "0xdfc50ac1134d764f", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "transactionsRoot": "0x399a0923c02fd28a351a11cbf2fd647225c0a69a67d7e04ad1c35c9435fc7d82", + "stateRoot": "0xf4814dce3c4230ab409f92001f88e55d63ed5a23d84c73ad3fad1222ba06cb7a", + "receiptsRoot": "0x3e5fff5f6eaeee82841547e049b6bdb980c4e6bd8539fb072a346a30ee72a25d", + "miner": "0x8888f1f195afa192cfee860698584c030f4c9db1", + "difficulty": "0x203c0", + "totalDifficulty": "0x221e00", + "extraData": "0x", + "size": "0x288", + "gasLimit": "0x2fefd8", + "gasUsed": "0xab90", + "timestamp": "0x561bc307", + "uncles": [], + "transactions": [ + "0x9f6a5eb65991ce483a9e1bfc57b44cd2c8ff4c26ca804240d593b4b63c3eafcd" + ] + } + }, + "statusCode": 200 +} \ No newline at end of file