Implement batch transactions RPC endpoint. (#1350)

* Implement batch transactions RPC endoint

Signed-off-by: Abdelhamid Bakhta <abdelhamid.bakhta@consensys.net>
pull/1358/head
Abdelhamid Bakhta 4 years ago committed by GitHub
parent 8241a47fde
commit 1b082b3bbe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java
  2. 131
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugBatchSendRawTransaction.java
  3. 10
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java
  4. 3
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java
  5. 126
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugBatchSendRawTransactionTest.java
  6. 28
      ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/debug/debug_batchSendRawTransaction_multipleTxError.json
  7. 38
      ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/debug/debug_batchSendRawTransaction_multipleTxMixedStatuses.json
  8. 26
      ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/debug/debug_batchSendRawTransaction_multipleTxSuccess.json
  9. 21
      ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/debug/debug_batchSendRawTransaction_singleTxError.json
  10. 22
      ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/debug/debug_batchSendRawTransaction_singleTxErrorInvalidSignatureV.json
  11. 20
      ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/debug/debug_batchSendRawTransaction_singleTxSuccess.json

@ -38,6 +38,7 @@ public enum RpcMethod {
DEBUG_TRACE_BLOCK_BY_HASH("debug_traceBlockByHash"),
DEBUG_TRACE_BLOCK_BY_NUMBER("debug_traceBlockByNumber"),
DEBUG_TRACE_TRANSACTION("debug_traceTransaction"),
DEBUG_BATCH_RAW_TRANSACTION("debug_batchSendRawTransaction"),
PRIV_CALL("priv_call"),
PRIV_GET_PRIVATE_TRANSACTION("priv_getPrivateTransaction"),
PRIV_GET_TRANSACTION_COUNT("priv_getTransactionCount"),

@ -0,0 +1,131 @@
/*
* 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.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.util.DomainObjectDecodeUtils;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidator;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.stream.IntStream;
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.google.common.base.Suppliers;
public class DebugBatchSendRawTransaction implements JsonRpcMethod {
private final Supplier<TransactionPool> transactionPool;
public DebugBatchSendRawTransaction(final TransactionPool transactionPool) {
this(Suppliers.ofInstance(transactionPool));
}
public DebugBatchSendRawTransaction(final Supplier<TransactionPool> transactionPool) {
this.transactionPool = transactionPool;
}
@Override
public String getName() {
return RpcMethod.DEBUG_BATCH_RAW_TRANSACTION.getMethodName();
}
@Override
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
final List<ExecutionStatus> executionStatuses = new ArrayList<>();
IntStream.range(0, requestContext.getRequest().getParamLength())
.forEach(
i ->
executionStatuses.add(
process(i, requestContext.getRequiredParameter(i, String.class))));
return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), executionStatuses);
}
private ExecutionStatus process(final int index, final String rawTransaction) {
try {
final ValidationResult<TransactionValidator.TransactionInvalidReason> validationResult =
transactionPool
.get()
.addLocalTransaction(DomainObjectDecodeUtils.decodeRawTransaction(rawTransaction));
return validationResult.either(
() -> new ExecutionStatus(index),
errorReason -> new ExecutionStatus(index, false, errorReason.name()));
} catch (final Throwable e) {
return new ExecutionStatus(index, false, e.getMessage());
}
}
@JsonPropertyOrder({"index", "success", "errorMessage"})
static class ExecutionStatus {
private final int index;
private final boolean success;
private final String errorMessage;
ExecutionStatus(final int index) {
this(index, true, null);
}
ExecutionStatus(final int index, final boolean success, final String errorMessage) {
this.index = index;
this.success = success;
this.errorMessage = errorMessage;
}
@JsonGetter(value = "index")
public int getIndex() {
return index;
}
@JsonGetter(value = "success")
public boolean isSuccess() {
return success;
}
@JsonGetter(value = "errorMessage")
@JsonInclude(Include.NON_NULL)
public String getErrorMessage() {
return errorMessage;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final ExecutionStatus other = (ExecutionStatus) o;
return index == other.index
&& success == other.success
&& Objects.equals(errorMessage, other.errorMessage);
}
@Override
public int hashCode() {
return Objects.hash(index, success, errorMessage);
}
}
}

@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.methods;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApi;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugAccountRange;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugBatchSendRawTransaction;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugMetrics;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugStorageRangeAt;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugTraceBlock;
@ -28,6 +29,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockReplay;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTracer;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTracer;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions;
import org.hyperledger.besu.metrics.ObservableMetricsSystem;
@ -39,14 +41,17 @@ public class DebugJsonRpcMethods extends ApiGroupJsonRpcMethods {
private final BlockchainQueries blockchainQueries;
private final ProtocolSchedule protocolSchedule;
private final ObservableMetricsSystem metricsSystem;
private final TransactionPool transactionPool;
DebugJsonRpcMethods(
final BlockchainQueries blockchainQueries,
final ProtocolSchedule protocolSchedule,
final ObservableMetricsSystem metricsSystem) {
final ObservableMetricsSystem metricsSystem,
final TransactionPool transactionPool) {
this.blockchainQueries = blockchainQueries;
this.protocolSchedule = protocolSchedule;
this.metricsSystem = metricsSystem;
this.transactionPool = transactionPool;
}
@Override
@ -72,6 +77,7 @@ public class DebugJsonRpcMethods extends ApiGroupJsonRpcMethods {
ScheduleBasedBlockHeaderFunctions.create(protocolSchedule),
blockchainQueries),
new DebugTraceBlockByNumber(() -> new BlockTracer(blockReplay), blockchainQueries),
new DebugTraceBlockByHash(() -> new BlockTracer(blockReplay)));
new DebugTraceBlockByHash(() -> new BlockTracer(blockReplay)),
new DebugBatchSendRawTransaction(transactionPool));
}
}

@ -84,7 +84,8 @@ public class JsonRpcMethodsFactory {
blockchainQueries,
namedPlugins,
natService),
new DebugJsonRpcMethods(blockchainQueries, protocolSchedule, metricsSystem),
new DebugJsonRpcMethods(
blockchainQueries, protocolSchedule, metricsSystem, transactionPool),
new EeaJsonRpcMethods(
blockchainQueries, protocolSchedule, transactionPool, privacyParameters),
new EthJsonRpcMethods(

@ -0,0 +1,126 @@
/*
* 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.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugBatchSendRawTransaction.ExecutionStatus;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import java.util.List;
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)
@SuppressWarnings("unchecked")
public class DebugBatchSendRawTransactionTest {
private static final String NAME = RpcMethod.DEBUG_BATCH_RAW_TRANSACTION.getMethodName();
private static final String VERSION = "2.0";
@Mock TransactionPool transactionPool;
DebugBatchSendRawTransaction method;
@Before
public void setUp() {
method = new DebugBatchSendRawTransaction(transactionPool);
}
@Test
public void nominalCaseSingleTransaction() {
final JsonRpcRequestContext request =
new JsonRpcRequestContext(
new JsonRpcRequest(
VERSION,
NAME,
new Object[] {
"0xf868808203e882520894627306090abab3a6e1400e9345bc60c78a8bef57872386f26fc10000801ba0ac74ecfa0e9b85785f042c143ead4780931234cc9a032fce99fab1f45e0d90faa02fd17e8eb433d4ca47727653232045d4f81322619c0852d3fe8ddcfcedb66a43"
}));
when(transactionPool.addLocalTransaction(any(Transaction.class)))
.thenReturn(ValidationResult.valid());
final JsonRpcSuccessResponse response = (JsonRpcSuccessResponse) method.response(request);
assertThat(response).isNotNull();
final List<ExecutionStatus> result = (List<ExecutionStatus>) response.getResult();
assertThat(result).isNotNull().hasSize(1).containsExactly(new ExecutionStatus(0));
}
@Test
public void nominalCaseMultipleTransactions() {
final JsonRpcRequestContext request =
new JsonRpcRequestContext(
new JsonRpcRequest(
VERSION,
NAME,
new Object[] {
"0xf868808203e882520894627306090abab3a6e1400e9345bc60c78a8bef57872386f26fc10000801ba0ac74ecfa0e9b85785f042c143ead4780931234cc9a032fce99fab1f45e0d90faa02fd17e8eb433d4ca47727653232045d4f81322619c0852d3fe8ddcfcedb66a43",
"0xf868018203e882520894627306090abab3a6e1400e9345bc60c78a8bef57876a94d74f430000801ba092faeec7bcb7418a79cd9f74c739237d72d52b5ab25aa08e332053304456e129a0386e3e9205a3553ecc5e85fc753c93196484c0fdeaaacdd61425caeb11bc6e5a"
}));
when(transactionPool.addLocalTransaction(any(Transaction.class)))
.thenReturn(ValidationResult.valid());
final JsonRpcSuccessResponse response = (JsonRpcSuccessResponse) method.response(request);
assertThat(response).isNotNull();
final List<ExecutionStatus> result = (List<ExecutionStatus>) response.getResult();
assertThat(result)
.isNotNull()
.hasSize(2)
.containsExactly(new ExecutionStatus(0), new ExecutionStatus(1));
}
@Test
public void errorSingleTransactionHexParsing() {
final JsonRpcRequestContext request =
new JsonRpcRequestContext(new JsonRpcRequest(VERSION, NAME, new Object[] {"www"}));
final JsonRpcSuccessResponse response = (JsonRpcSuccessResponse) method.response(request);
assertThat(response).isNotNull();
final List<ExecutionStatus> result = (List<ExecutionStatus>) response.getResult();
assertThat(result)
.isNotNull()
.hasSize(1)
.containsExactly(new ExecutionStatus(0, false, "Invalid raw transaction hex"));
}
@Test
public void errorSingleTransactionSignatureInvalidV() {
final JsonRpcRequestContext request =
new JsonRpcRequestContext(
new JsonRpcRequest(
VERSION,
NAME,
new Object[] {
"0xf868808203e882520894627306090abab3a6e1400e9345bc60c78a8bef57872386f26fc10000801fa0ac74ecfa0e9b85785f042c143ead4780931234cc9a032fce99fab1f45e0d90faa02fd17e8eb433d4ca47727653232045d4f81322619c0852d3fe8ddcfcedb66a43"
}));
final JsonRpcSuccessResponse response = (JsonRpcSuccessResponse) method.response(request);
assertThat(response).isNotNull();
final List<ExecutionStatus> result = (List<ExecutionStatus>) response.getResult();
assertThat(result)
.isNotNull()
.hasSize(1)
.containsExactly(
new ExecutionStatus(0, false, "An unsupported encoded `v` value of 31 was found"));
}
}

@ -0,0 +1,28 @@
{
"request": {
"jsonrpc": "2.0",
"method": "debug_batchSendRawTransaction",
"params": [
"0x416",
"zXDf"
],
"id": 1
},
"response": {
"jsonrpc": "2.0",
"id": 1,
"result": [
{
"index": 0,
"success": false,
"errorMessage": "Invalid raw transaction hex"
},
{
"index": 1,
"success": false,
"errorMessage": "Invalid raw transaction hex"
}
]
},
"statusCode": 200
}

@ -0,0 +1,38 @@
{
"request": {
"jsonrpc": "2.0",
"method": "debug_batchSendRawTransaction",
"params": [
"0xf868808203e882520894627306090abab3a6e1400e9345bc60c78a8bef57872386f26fc10000801ba0ac74ecfa0e9b85785f042c143ead4780931234cc9a032fce99fab1f45e0d90faa02fd17e8eb433d4ca47727653232045d4f81322619c0852d3fe8ddcfcedb66a43",
"gdTr",
"0xf868018203e882520894627306090abab3a6e1400e9345bc60c78a8bef57876a94d74f430000801ba092faeec7bcb7418a79cd9f74c739237d72d52b5ab25aa08e332053304456e129a0386e3e9205a3553ecc5e85fc753c93196484c0fdeaaacdd61425caeb11bc6e5a",
"0x147"
],
"id": 1
},
"response": {
"jsonrpc": "2.0",
"id": 1,
"result": [
{
"index": 0,
"success": true
},
{
"index": 1,
"success": false,
"errorMessage": "Invalid raw transaction hex"
},
{
"index": 2,
"success": true
},
{
"index": 3,
"success": false,
"errorMessage": "Invalid raw transaction hex"
}
]
},
"statusCode": 200
}

@ -0,0 +1,26 @@
{
"request": {
"jsonrpc": "2.0",
"method": "debug_batchSendRawTransaction",
"params": [
"0xf868808203e882520894627306090abab3a6e1400e9345bc60c78a8bef57872386f26fc10000801ba0ac74ecfa0e9b85785f042c143ead4780931234cc9a032fce99fab1f45e0d90faa02fd17e8eb433d4ca47727653232045d4f81322619c0852d3fe8ddcfcedb66a43",
"0xf868018203e882520894627306090abab3a6e1400e9345bc60c78a8bef57876a94d74f430000801ba092faeec7bcb7418a79cd9f74c739237d72d52b5ab25aa08e332053304456e129a0386e3e9205a3553ecc5e85fc753c93196484c0fdeaaacdd61425caeb11bc6e5a"
],
"id": 1
},
"response": {
"jsonrpc": "2.0",
"id": 1,
"result": [
{
"index": 0,
"success": true
},
{
"index": 1,
"success": true
}
]
},
"statusCode": 200
}

@ -0,0 +1,21 @@
{
"request": {
"jsonrpc":"2.0",
"method":"debug_batchSendRawTransaction",
"params":[
"0x416" ],
"id":1
},
"response": {
"jsonrpc": "2.0",
"id": 1,
"result": [
{
"index": 0,
"success": false,
"errorMessage": "Invalid raw transaction hex"
}
]
},
"statusCode": 200
}

@ -0,0 +1,22 @@
{
"request": {
"jsonrpc": "2.0",
"method": "debug_batchSendRawTransaction",
"params": [
"0xf868808203e882520894627306090abab3a6e1400e9345bc60c78a8bef57872386f26fc10000801fa0ac74ecfa0e9b85785f042c143ead4780931234cc9a032fce99fab1f45e0d90faa02fd17e8eb433d4ca47727653232045d4f81322619c0852d3fe8ddcfcedb66a43"
],
"id": 1
},
"response": {
"jsonrpc": "2.0",
"id": 1,
"result": [
{
"index": 0,
"success": false,
"errorMessage": "An unsupported encoded `v` value of 31 was found"
}
]
},
"statusCode": 200
}

@ -0,0 +1,20 @@
{
"request": {
"jsonrpc":"2.0",
"method":"debug_batchSendRawTransaction",
"params":[
"0xf868808203e882520894627306090abab3a6e1400e9345bc60c78a8bef57872386f26fc10000801ba0ac74ecfa0e9b85785f042c143ead4780931234cc9a032fce99fab1f45e0d90faa02fd17e8eb433d4ca47727653232045d4f81322619c0852d3fe8ddcfcedb66a43" ],
"id":1
},
"response": {
"jsonrpc": "2.0",
"id": 1,
"result": [
{
"index": 0,
"success": true
}
]
},
"statusCode": 200
}
Loading…
Cancel
Save